; haribote-os boot asm
; TAB=4

[INSTRSET "i486p"]

VBEMODE1 		EQU		0x114	; 800 x  600 x 16bitカラー
VBEMODE1_COL	EQU		16		; 16bitカラー
VBEMODE1_MODEL	EQU		6		; メモリモデル(ダイレクト)
VBEMODE2 		EQU		0x101	; 800 x  600 x 8bitカラー
VBEMODE2_COL	EQU		8		; 8bitカラー
VBEMODE2_MODEL	EQU		4		; メモリモデル(パレット)
; （画面モード一覧）
;   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カラー
;   0x111 :  640 x  480 x 16bitカラー
;   0x114 :  800 x  600 x 16bitカラー
;   0x117 : 1024 x  768 x 16bitカラー
;   0x11A : 1280 x 1024 x 16bitカラー

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

; BOOT_INFO関係
CYLS    EQU     0x0900          ; ブートセクタが設定する
VMODE   EQU     0x0904          ; 色数に関する情報。何ビットカラーか？
SCRNX   EQU     0x0908          ; 解像度のX
SCRNY   EQU     0x090c          ; 解像度のY
LEDS    EQU     0x0910
VRAM    EQU     0x0914          ; グラフィックバッファの開始番地

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

; 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

; 画面モード情報を得る
VBECHECK1:
        MOV     CX,VBEMODE1
        MOV     AX,0x4f01
        INT     0x10
        CMP     AX,0x004f
        JNE     VBECHECK2

; 画面モード情報の確認

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

; 画面モードの切り替え

        MOV     BX,VBEMODE1+0x4000
        MOV     AX,0x4f02
        INT     0x10

        JMP		VBECHECK_END	; 切り替え終了

VBECHECK2:
		JMP		VBECHECK2
        MOV     CX,VBEMODE2
        MOV     AX,0x4f01
        INT     0x10
        CMP     AX,0x004f
        JNE     scrn320

; 画面モード情報の確認

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

; 画面モードの切り替え

        MOV     BX,VBEMODE2+0x4000
        MOV     AX,0x4f02
        INT     0x10
        
        						; 切り替え終了

VBECHECK_END:

		MOV		AL,BYTE [ES:DI+0x19]
        MOV     BYTE [VMODE],AL ; 画面モードをメモする（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     DWORD [VMODE],8  ; 画面モードをメモする（C言語が参照する）
        MOV     DWORD [SCRNX],320
        MOV     DWORD [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

; プロテクトモード移行

        LGDT    [GDTR0]         ; 暫定GDTを設定
        MOV     EAX,CR0
        AND     EAX,0x7fffffff  ; bit31を0にする（ページング禁止のため）
        OR      EAX,0x00000001  ; bit0を1にする（プロテクトモード移行のため）
        MOV     CR0,EAX
        JMP     pipelineflash
pipelineflash:
        MOV     AX,1*8          ;  読み書き可能セグメント32bit
        MOV     DS,AX
        MOV     ES,AX
        MOV     FS,AX
        MOV     GS,AX
        MOV     SS,AX

; bootpackの転送

        MOV     ESI,bootpack    ; 転送元
        MOV     EDI,BOTPAK      ; 転送先
        MOV     ECX,512*1024/4
        CALL    memcpy

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

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

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

; 残り全部

        MOV     ESI,DSKCAC0+512 ; 転送元
        MOV     EDI,DSKCAC+512  ; 転送先
        MOV     ECX,0
        MOV     CL,BYTE [CYLS]
        IMUL    ECX,512*18*2/4  ; シリンダ数からバイト数/4に変換
        SUB     ECX,512/4       ; IPLの分だけ差し引く
        CALL    memcpy

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

; bootpackの起動

        MOV     EBX,BOTPAK
        MOV     ECX,[EBX+16]
        ADD     ECX,3           ; ECX += 3;
        SHR     ECX,2           ; ECX /= 4;
        JZ      skip            ; 転送するべきものがない
        MOV     ESI,[EBX+20]    ; 転送元
        ADD     ESI,EBX
        MOV     EDI,[EBX+12]    ; 転送先
        CALL    memcpy
skip:
        MOV     ESP,[EBX+12]    ; スタック初期値
        JMP     DWORD 2*8:0x0000001b

waitkbdout:
        IN       AL,0x64
        AND      AL,0x02
        JNZ     waitkbdout      ; ANDの結果が0でなければwaitkbdoutへ
        RET

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

        ALIGNB  16
GDT0:
        RESB    8               ; ヌルセレクタ
        DW      0xffff,0x0000,0x9200,0x00cf ; 読み書き可能セグメント32bit
        DW      0xffff,0x0000,0x9a28,0x0047 ; 実行可能セグメント32bit（bootpack用）

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

        ALIGNB  16
bootpack:
