; haribote-os boot asm
; TAB=4


VBEMODE EQU 0x118			; 1024 x 768 x 24bitカラー
BITPERPIXEL EQU 24
MEMORYMODEL EQU 6 ; 4ならインデックスカラー　6ならダイレクトカラー
; （画面モード一覧）
;	0x100 :  640 x  400 x 8bitカラー
;	0x101 :  640 x  480 x 8bitカラー
;	0x103 :  800 x  600 x 8bitカラー
;	0x105 : 1024 x  768 x 8bitカラー
;	0x107 : 1280 x 1024 x 8bitカラー
;	0x118 : 1024 x 768 x 24bitカラー


BOOTPACK_CODE EQU 0x00280000		; bootpackのロード先

;DSKCAC	EQU		0x00100000		; ディスクキャッシュの場所
;DSKCAC0	EQU		0x00008000		; ディスクキャッシュの場所（リアルモード）

SYSPAGEDIR EQU 0x00100000

; BOOT_INFO関係
BOOTDRIVE	EQU		0x0ff0			; ブートセクタが設定する
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			; 色数に関する情報。何ビットカラーか？
SCRNX	EQU		0x0ff4			; 解像度のX
SCRNY	EQU		0x0ff6			; 解像度のY
VRAM	EQU		0x0ff8			; グラフィックバッファの開始番地

		ORG		0xc200			; このプログラムがどこに読み込まれるのか

;ブートデバイスを保存
		mov [BOOTDRIVE], dl
; VBE存在確認

		MOV		AX,0x9000
		MOV		ES,AX
		MOV		DI,0
		MOV		AX,0x4f00
		INT		0x10
		CMP		AX,0x004f
		JNE		scrn320

; VBEのバージョンチェック

		MOV		AX,[ES:DI+4]
		CMP		AX,0x0200
		JB		scrn320			; if (AX < 0x0200) goto scrn320

; 画面モード情報を得る

		MOV		CX,VBEMODE
		MOV		AX,0x4f01
		INT		0x10
		CMP		AX,0x004f
		JNE		scrn320

; 画面モード情報の確認

		CMP		BYTE [ES:DI+0x19],BITPERPIXEL
		JNE		scrn320
		CMP		BYTE [ES:DI+0x1b],MEMORYMODEL
		JNE		scrn320
		MOV		AX,[ES:DI+0x00]
		AND		AX,0x0080
		JZ		scrn320			; モード属性のbit7が0だったのであきらめる

; 画面モードの切り替え

		MOV		BX,VBEMODE | 0x4000
		MOV		AX,0x4f02
		INT		0x10
		MOV		BYTE [VMODE],BITPERPIXEL	; 画面モードをメモする（C言語が参照する）
		MOV		AX,[ES:DI+0x12]
		MOV		[SCRNX],AX
		MOV		AX,[ES:DI+0x14]
		MOV		[SCRNY],AX
		MOV		EAX,[ES:DI+0x28]
		MOV		[VRAM],EAX
		JMP		keystatus

scrn320:
		MOV		AL,0x13			; VGAグラフィックス、320x200x8bitカラー
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE],8	; 画面モードをメモする（C言語が参照する）
		MOV		WORD [SCRNX],320
		MOV		WORD [SCRNY],200
		MOV		DWORD [VRAM],0x000a0000

; キーボードのLED状態をBIOSに教えてもらう

keystatus:
		MOV		AH,0x02
		INT		0x16 			; keyboard BIOS
		MOV		[LEDS],AL

; PICが一切の割り込みを受け付けないようにする
;	AT互換機の仕様では、PICの初期化をするなら、
;	こいつをCLI前にやっておかないと、たまにハングアップする
;	PICの初期化はあとでやる

		MOV		AL,0xff
		OUT		0x21,AL
		NOP						; OUT命令を連続させるとうまくいかない機種があるらしいので
		OUT		0xa1,AL

		CLI						; さらにCPUレベルでも割り込み禁止

; CPUから1MB以上のメモリにアクセスできるように、A20GATEを設定

		CALL	waitkbdout
		MOV		AL,0xd1
		OUT		0x64,AL
		CALL	waitkbdout
		MOV		AL,0xdf			; enable A20
		OUT		0x60,AL
		CALL	waitkbdout
		
;[INSTRSET "i486p"]				; 486の命令まで使いたいという記述

; プロテクトモード移行
; page size = 4mb or 4kb (混在)  pys addr size = 32 bit

		LGDT	[GDTR0]			; 暫定GDTを設定
		MOV		EAX,CR0
		;or eax, 1 << 0x1f ; paging flag
		;AND		EAX,0x7fffffff	; bit31を0にする（ページング禁止のため）
		OR		EAX,0x00000001	; bit0を1にする（プロテクトモード移行のため）
		MOV		CR0,EAX
		JMP	pipelineflush
pipelineflush:
;ページディレクトリ等の設定

		xor eax, eax
		mov ecx, 0
		
		align 16
pagedirloop:
		mov eax, ecx
		shl eax, 22
		or eax, ((1 << 8)|(1 << 7)|(1 << 1)|1)
		mov [SYSPAGEDIR + 4 * ecx], eax
		
		inc ecx
		cmp ecx, 0x400
		jb pagedirloop


;ページングの設定
		mov eax, cr3
		or eax, SYSPAGEDIR
		mov cr3, eax
		
		mov eax, cr4
		and eax, ~(1 << 5) ;  physical addr ext(pae) を無効に
		or eax, 1 << 4; enable paging size ext(pse)
		mov cr4, eax
		
		;TLB無効化?
		mov eax, cr3
		mov cr3, eax
		
		MOV		EAX,CR0
		or eax, 1 << 0x1f ; paging flag
		MOV		CR0,EAX
		JMP	pipelineflush2
pipelineflush2:
		
		MOV		AX,1*8			;  読み書き可能セグメント32bit
		MOV		DS,AX
		MOV		ES,AX
		MOV		FS,AX
		MOV		GS,AX
		MOV		SS,AX


; ついでにディスクデータも本来の位置へ転送

; まずはブートセクタから

		;MOV		ESI,0x7c00		; 転送元
		;MOV		EDI,DSKCAC		; 転送先
		;MOV		ECX,512/4
		;CALL	memcpy

; asmheadでしなければいけないことは全部し終わったので、
;	あとはbootpackに任せる

; bootpackの起動

		MOV	EBX, bootpack
		MOV	ECX, [EBX+0x3C]   ; lfanew
		add ebx, ecx          ; nt header 32
		mov  ax, [ebx + 6]    ; number of section
		mov ebp, [ebx + 0x28] ; entry point
		add ebp, BOOTPACK_CODE
		add ebx, 0xf8          ; section header
readsection:
		mov edi, [ebx + 0x0C] ; virtual addr
		mov ecx, [ebx + 0x10] ; size of raw data
		mov esi, [ebx + 0x14] ; pointer to raw data
		add edi, BOOTPACK_CODE ; このセクションのコピー先
		add esi, bootpack      ; コピー元
		cld
		
		a32 rep movsb
		;db 0xf3
		;db 0x67
		;db 0xa4
		;rep dword movsb ; f3 a4
		
		
		add ebx, 0x28 ; section header size
		dec ax
		jnz readsection
		

		MOV	ESP, 0x00380000	; スタック初期値

		push dword 2*8
		push ebp
		
		;sti
		
		
		o32 retf 
		; call eipload
; eipload:
		; pop ax
		; movzx eax, ax
		; mov [eax+0x15], ebp
		; mov dword [eax+0x19], 2*8

		; jmp dword 0:0
		; db 0,0

waitkbdout:
		IN		 AL,0x64
		AND		 AL,0x02
		IN		 AL,0x60 		; から読み(受信バッファが悪さをしないように)
		JNZ		waitkbdout		; ANDの結果が0でなければwaitkbdoutへ
		RET

memcpy:
		push eax
memcpy_loop:
		MOV		EAX,[ESI]
		ADD		ESI,4
		MOV		[EDI],EAX
		ADD		EDI,4
		SUB		ECX,1
		JNZ		memcpy_loop			; 引き算した結果が0でなければmemcpyへ
		pop eax
		RET
; memcpyはアドレスサイズプリフィクスを入れ忘れなければ、ストリング命令でも書ける


		ALIGNB	16
GDT0:
		RESB	8				; ヌルセレクタ
		db 0xff,0xff,0x00,0x00,0x00,0x92,0xcf,0x00	; 読み書き可能セグメント32bit
		;db 0xff,0xff,0x00,0x00,0x28,0x9a,0x4f,0x00	; 実行可能セグメント32bit（bootpack用）
		db 0xff,0xff,0x00,0x00,0x00,0x9a,0xcf,0x00	; 実行可能セグメント32bit（bootpack用）

		DW		0
GDTR0:
		DW		8*3-1
		DD		GDT0

		ALIGNB	16
bootpack:
