3日目 その4

前回までで32ビットモードには到達したので
あとはHariMainの起動して3日目は終了です。
キリがいいので3日目終了ぶんまでのソースを掲載しときます。


以下細かいはなし。

  1. HariMain中のhlt命令はインラインアセンブラasm("hlt")で済ませました。(OS自作本のなかではインラインアセンブラは使わない方針?)
  2. crt.cはHariMainを呼ぶためのものです。asmhead.Sからのljmpで直接_startが実行されます。

Makefile

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 */
}