C言語から呼べる、アセンブラで作成した関数のリンクもうまくできたようだ。
やっと3日目が終了。長かった...
func.sに定義したio_hltで、hltを実行関数で、tinyos.cのMain関数から呼び出される。
最初、エラーもなく、コンパイル・リンクができたので、実行してみると、どうも挙動が変。
生成されたバイナリを見てみると、hltのコードが出力されていなかった。
原因がなかなか分からず、試行錯誤していたが、
.section .text
と書かなければならないところを、
.section text
と書いていたことが原因であった。
つまらないところで、手間取ってしまった。
でも、.section text と書いた場合、何を意味することになるのだろう。
ここまでのソースを書いておこう。
コードが多くなってきたので、今後はgithubを使おう。
Makefile
image_file=tinyos.img
image_file: ipl.bin tinyos.sys
mformat -f 1440 -B ipl.bin -C -i ${image_file} ::
mcopy tinyos.sys -i ${image_file} ::
ipl.bin: ipl.s ipl_lnk.ls
gcc -nostdlib -o $@ -Tipl_lnk.ls ipl.s
gcc -Tipl_lnk.ls -c -g -Wa,-a,-ad ipl.s > ipl.lst
tinyos.sys: boot.bin func.o tinyos.c
gcc -m32 -S -c -nostdlib -Wl,--oformat=binary *.c
gcc -m32 -c -g -Wa,-a,-ad tinyos.s > tinyos.lst
gcc -m32 -c -nostdlib -Wl,--oformat=binary *.c -o tinyos.o
ld -static -m elf_i386 -o tinyos.bin -e Main --oformat=binary tinyos.o func.o
cat boot.bin tinyos.bin > $@
boot.bin: boot.s boot_lnk.ls
gcc boot.s -nostdlib -Tboot_lnk.ls -o boot.bin
gcc -Tboot_lnk.ls -c -g -Wa,-a,-ad boot.s > boot.lst
func.o: func.s
as --32 -a -ad func.s > func.lst
as --32 func.s -o func.o
img:
make image_file
run:
qemu-system-i386 -m 32 -localtime -vga std -fda ${image_file}
debug:
qemu-system-i386 -m 32 -localtime -vga std -fda ${image_file} \
-gdb tcp::10000 \
-S
clean:
rm tinyos.sys boot.bin
rm *.o
boot.s
.set BOTPAK, 0x00280000 # bootpackのロード先
.set DSKCAC, 0x00100000 # ディスクキャッシュの場所
.set DSKCAC0, 0x00008000 # ディスクキャッシュの場所(リアルモード)
// BOOT_INFO
.set CYLS, 0x0ff0 # シリンダ数
.set LEDS,0x0ff1 # LEDの状態
.set VMODE, 0x0ff2 # ビデオモード
.set SCRNX, 0x0ff4 # 解像度X
.set SCRNY, 0x0ff6 # 解像度Y
.set VRAM, 0x0ff8 # VRAMの開始アドレス
.text
.code16
// ビデオモードを変更する
movb $0x00, %ah # VGA Graphics 320x200x8bit
movb $0x13, %al
int $0x10
// 画面の状態を記録する
movb $8, (VMODE)
movw $320, (SCRNX)
movw $200, (SCRNY)
movl $0x000a0000, (VRAM)
// LEDの状態を記録する
movb $0x02, %ah
int $0x16
movb %al, (LEDS)
// PICが割り込みを受け付けないようにする
movb $0xff, %al
outb %al, $0x21
nop # outは連続して使用しない
outb %al, $0xa1
cli # CPUでも割り込み禁止
// A20互換モードを無効にして1MB以上のアドレスにアクセスできるようにする
call waitkbdout
movb $0xd1, %al
outb %al, $0x64
call waitkbdout
movb $0xdf, %al # A20を有効にする
outb %al, $0x60
call waitkbdout
// プロテクトモードに移行する
.arch i486
lgdt (GDTR0)
movl %cr0, %eax
andl $0x7fffffff, %eax # ページング禁止
orl $0x00000001, %eax # プロテクトモード移行
movl %eax, %cr0
jmp pipelineflush
pipelineflush:
movw $1*8, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
// bootpackを転送する
movl $bootpack, %esi # 転送元
movl $BOTPAK, %edi # 転送先
movl $512*1024/4, %ecx # 4で割っているのは4バイト単位で処理するため
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 # 読み込んだシリンダ数
imull $512*18*2/4, %ecx # 1シリンダあたりのバイト数/4を掛ける
sub $512/4, %ecx # IPL分を引く
call memcpy
// bootpackを起動する
movl $BOTPAK, %ebx
movl 16(%ebx), %ecx
addl $3, %ecx
shrl $2, %ecx
jz skip # 転送するものがない
movl 20(%ebx), %esi # .dataのアドレス
addl %ebx, %esi
movl 12(%ebx), %edi # .data転送先
call memcpy
skip:
movl 12(%ebx), %esp # スタック初期値
ljmpl $2*8, $0x0000001b
waitkbdout:
inb $0x64, %al
andb $0x02, %al
# inb $0x60, %al # 元のソースにはないコード
jnz waitkbdout
ret
memcpy:
movl (%esi), %eax
addl $4, %esi
movl %eax, (%edi)
addl $4, %edi
subl $1, %ecx
jnz memcpy
ret
.align 16
GDT0:
// GDTの構成
// short limit_low, base_low
// char base_mid, access_right
// char limit_high, base_high
// null selector
.skip 8, 0x00
// base=0x00000000 limit=0xcfffff access_right=0x92
.word 0xffff, 0x0000, 0x9200, 0x00cf
// base=0x00280000 limit=0x47ffff access_right=0x9a
.word 0xffff, 0x0000, 0x9a28, 0x0047
.word 0x0000
GDTR0:
.word 8 * 3 - 1 # GDTのサイズ?
.int GDT0
.align 16
bootpack:
# + 0 : stack+.data+heap の大きさ(4KBの倍数)
.int 0x00
# + 4 : シグネチャ "Hari"
.ascii "Tiny"
# + 8 : mmarea の大きさ(4KBの倍数)
.int 0x00
# +12 : スタック初期値&.data転送先
.int 0x00310000
# +16 : .dataのサイズ
.int 0x11a8
# +20 : .dataの初期値列がファイルのどこにあるか
.int 0x10c8
# +24 +28 のセットで 1bからの命令が E9 XXXXXXXX (JMP)になり、C言語のエントリポイントにJMPするようだ
# +24 : 0xe9000000
.int 0xe9000000
# +28 : エントリアドレス-0x20
.int 0x00
# +32 : heap領域(malloc領域)開始アドレス
.int 0x00
func.s
.file "func.s"
.section .text
.global io_hlt
io_hlt:
hlt
ret
tinyos.s
void io_hlt(void);
void Main(void) {
fin:
//__asm__("hlt\n\t");
io_hlt();
goto fin;
}