3日目 その4
前回までで32ビットモードには到達したので
あとはHariMainの起動して3日目は終了です。
キリがいいので3日目終了ぶんまでのソースを掲載しときます。
以下細かいはなし。
- HariMain中のhlt命令はインラインアセンブラasm("hlt")で済ませました。(OS自作本のなかではインラインアセンブラは使わない方針?)
- crt.cはHariMainを呼ぶためのものです。asmhead.Sからのljmpで直接_startが実行されます。
CC = gcc CFLAGS = -fno-common -Wall -O2 -c \ -finhibit-size-directive -fno-ident \ -fomit-frame-pointer -fcall-used-ebx TARGET = hello IPL = ipl ASMHEAD = asmhead BOOTPACK = bootpack OBJS = crt.o bootpack.o ${TARGET}.bin: ${IPL} ${ASMHEAD} ${BOOTPACK} dd if=/dev/zero of=$@ bs=512 count=2880 &> /dev/null dd if=${IPL} of=$@ conv=notrunc &> /dev/null dd if=${ASMHEAD} of=$@ conv=notrunc seek=1 &> /dev/null dd if=${BOOTPACK} of=$@ conv=notrunc seek=2 &> /dev/null ${IPL}: ipl.o ipl.ls ld -T ipl.ls -o $@ ipl.o \ -Map ${IPL}.map --cref ${ASMHEAD}: asmhead.o asmhead.ls ld -T asmhead.ls -o $@ asmhead.o \ -Map ${ASMHEAD}.map --cref ${BOOTPACK}: ${OBJS} bootpack.ls ld -T bootpack.ls -o $@ ${OBJS} \ -Map ${BOOTPACK}.map --cref %.o: %.S Makefile ${CC} ${CFLAGS} $< %.o: %.c Makefile ${CC} ${CFLAGS} $< clean: rm -f *.o *.map ${IPL} ${ASMHEAD} ${BOOTPACK} ${TARGET}.bin
ipl.S
#define INIT_SEG 0x0 #define CYLS 10 .file "ipl.S" .code16gcc .section .rodata msg1: .string "load error!!" .text main: cli movw $INIT_SEG, %ax movw %ax, %ds movw %ax, %es movw $0x7c00, %sp // %sp から %ssまでスタックとして使える movw $0, %ax movw %ax, %ss sti movw $0x0820, %ax movw %ax, %es // 読み込んだディスクの内容はes:bxに書き込まれる movb $0x0, %ch movb $0x0, %dh movb $0x2, %cl readloop: movw $0x0, %si // 失敗回数を数えるレジスタ retry: movb $0x02, %ah // AH=0x02:ディスクの読み込み movb $0x01, %al // 1セクタ読み込み movw $0x0, %bx movb $0x0, %dl // Aドライブ int $0x13 jnc next // エラーでなければ終了 incw %si cmpw $5, %si jae error // 5 <= %siならerrorへ movb $0x0, %ah movb $0x0, %dl int $0x13 // ドライブリセット jmp retry next: movw %es, %ax addw $0x0020, %ax movw %ax, %es // アドレスを0x0200( != 0x20)すすめる addb $1, %cl cmpb $18, %cl jbe readloop // %cl <= 18 ならreadloop movb $1, %cl addb $1, %dh cmpb $2, %dh jb readloop // %dh < 2 ならreadloopへ // デバッグのためにシリンダごとに"."を出力する movb $'.', %eax movb $0x0e, %ah movl $7, %ebx int $0x10 movb $0, %dh addb $1, %ch cmpb $CYLS, %ch jb readloop // %ch < CYLSだったらreadloop jmp 0x8200 fin: hlt jmp fin error: movl $msg1, %esi putloop: movzbl (%esi), %eax cmpb $0, %al je fin incl %esi movb $0x0e, %ah movl $7, %ebx int $0x10 jmp putloop .section .sign, "a" // "a"の指定がないと sign: .word 0xaa55 // FD用ブートシグネチャ
ipl.ls
OUTPUT_FORMAT("binary") /* We want raw binary image */ /* Define memory layout */ MEMORY { body : org = 0x7c00, len = 510 sign : org = 0x7c00 + 510, len = 2 } /* Specify input and output sections */ SECTIONS { .text : { *(.text) } > body /* Executable codes */ .data : { *(.data) } > body /* Initialized data */ .bss : { *(.bss) } > body /* Uninitialized data */ .rodata : { *(.rodata*) } > body /* Constant data (R/O) */ .sign : { *(.sign) } > sign /* Boot signature */ }
asmhead.S
20060521追記:VIDEO周りの設定を記憶する処理が抜けていたので修正しました。
// // asmhead.S // #define CYLS 10 #define LEDS 0xff1 #define VMODE 0xff2 #define SCRNX 0xff4 #define SCRNY 0xff6 #define VRAM 0x0ff8 #define BOTPAK 0x00280000 // bootpackのロード先. #define DSKCAC 0x00100000 // ディスクキャッシュの場所. #define DSKCAC0 0x00008000 // ディスクキャッシュの場所(リアルモード). .file "asmhead.S" .code16gcc .text main: movw %cs, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss // 画面のモードを切り替える. movb $0x13, %al movb $0x00, %ah int $0x10 movb $8, VMODE movw $320, SCRNX movw $200, SCRNY movl $0x000a0000, VRAM movb $0x02, %ah int $0x16 movb %al, LEDS // PICが一切の割り込みを受け付けないようにする // AT互換機の仕様では、PICの初期化をするなら、 // こいつをCLI前にやっておかないと、たまにハングアップする // PICの初期化はあとでやる movb $0xff, %al outb %al, $0x21 nop // OUT命令を連続させるとうまくいかない機種があるらしいので out %al, $0xa1 cli // さらにCPUレベルでも割り込み禁止 // CPUから1MB以上のメモリにアクセスできるように、A20GATEを設定 call waitkbdout movb $0xd1, %al outb %al, $0x64 call waitkbdout movb $0xdf, %al // enable A20 outb %al, $0x60 call waitkbdout // プロテクトモード移行 xorl %eax, %eax // EAX = 0 movw %ds, %ax // AX = DS shll $4, %eax // EAX := (DS << 4) + gdtr addl $gdt, %eax movl %eax, (gdtr + 2) // gdtrのbaseにgdtのアドレスを設定. lgdt gdtr // Load GDTR movl %cr0, %eax andl $0x7fffffff, %eax // bit31を0にする(ページング禁止のため) orl $0x00000001, %eax // bit0を1にする(プロテクトモード移行のため) movl %eax, %cr0 jmp pipelineflash pipelineflash: movw $0x8, %ax // 読み書き可能セグメント32bit movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss // bootpackの転送 movl $0x8400, %esi // 転送元 // asmhead.oは1セクタ分あるので // bootpack.oは0x8200 + 0x200の位置にいるはず movl $BOTPAK, %edi // 転送先 movl $(512*1024/4), %ecx call memcpy // ついでにディスクデータも本来の位置へ転送 // まずはブートセクタから movl $0x7c00, %esi // 転送元 movl $DSKCAC, %edi // 転送先 movl $(512/4), %ecx call memcpy // 残り全部 movl $(DSKCAC0+512), %esi // 転送元 movl $(DSKCAC+512), %edi // 転送先 movl $0, %ecx movb $CYLS, %cl movl $(512*18*2/4), %eax imull %ecx movl %eax, %ecx subl $(512/4), %ecx // IPLの分だけ差し引く call memcpy // asmheadでしなければいけないことは全部し終わったので、 // あとはbootpackに任せる // bootpackの起動 // 必要ないのでコメントアウト // movl $BOTPAK, %ebx // movl 16(%ebx), %ecx // addl $3, %ecx // shrl $2, %ecx // jz skip // 転送するものがない. // movl 20(%ebx), %esi // 転送元 // addl %ebx, %esi // movl 12(%ebx), %edi // call memcpy skip: // movl 12(%ebx), %esp // スタック初期化 movl $0x00310000, %esp ljmpl $0x10, $0 // Interface誌ではljmplではなく以下の書きかたをしている。なんで? // .byte 0x66 // Operand size prefix // .byte 0xEA // Far jump // .long 0 // 32bit offset address // .word 0x10 // Boot segment CS waitkbdout: inb $0x64, %al andb $0x02, %al inb $0x60, %al // から読み(受信バッファが悪さをしないように) jnz waitkbdout // ANDの結果が0でなければwaitkbdoutへ ret memcpy: movl (%esi), %eax addl $4, %esi movl %eax, (%edi) addl $4, %edi subl $1, %ecx jnz memcpy // 引き算した結果が0でなければmemcpyへ ret // memcpyはアドレスサイズプリフィクスを入れ忘れなければ、ストリング命令でも書ける .section .data .align 16 gdt: .long 0 .long 0 // ヌルセレクタ .long 0x0000ffff .long 0x00cf9200 // 読み書き可能セグメント // 0x08 0x00cf92000000ffff // G = 4K, D = 32bit, P = S = 1, // Type = 7(exec/read), limit = 0xFFFFF // Base = 0x00000000 .long 0x0000ffff .long 0x00479a28 // 実行可能セグメント // 0x16 0x00479a280000ffff // G = 1B, D = 32bit, P = S = 1 // Type = 5(exec/read), limit = 0x7FFFF // Base = 0x00280000 gdtr: .word 23 .long 0 // ここにあとでgdtへのポインタが格納される
asmhead.ls
OUTPUT_FORMAT("binary") /* We want raw binary image */ /* Define memory layout */ /* Specify input and output sections */ SECTIONS { . = 0x8200; .text : { *(.text) } /* Executable codes */ .data : { *(.data) } /* Initialized data */ .bss : { *(.bss) } /* Uninitialized data */ .rodata : { *(.rodata*) } /* Constant data (R/O) */ }
crt.c
extern void HariMain(void); void _start(void) { HariMain(); return; }
bootpack.c
void HariMain(void) { fin: asm("hlt"); goto fin; }
bootpack.ls
OUTPUT_FORMAT("binary") /* We want raw binary image */ /* Define memory layout */ /* Specify input and output sections */ SECTIONS { .text 0x00: { *(.text) } /* Executable codes */ .data 0x280000 + SIZEOF(.text): AT ( ADDR (.text) + SIZEOF (.text) ) { *(.data) } /* Initialized data */ .rodata : AT ( ADDR (.text) + SIZEOF (.text) + SIZEOF (.data) ) { *(.rodata*) } /* Constant data (R/O) */ .bss : AT ( ADDR (.text) + SIZEOF (.text) + SIZEOF (.data) + SIZEOF(.rodata)) { *(.bss) } /* Uninitialized data */ }