「作って理解するOS」でmingw-w64用のプログラムを書いてみた パート2
「作って理解するOS」という書籍はNASMでプログラムが書かれているのですが、そのプログラムをmingw-w64用に書いてみたという記事のパート2です。
ようやく16章までいったので、参考として、そこまでのソースコードの一部を記事に載せます。特に難しかった箇所は、15章3節のプロテクトモードへ移行するところでした。
コマンドとdefine.sとboot.sとkernel.sのソースコードを記事に載せます。ちなみにコマンドはwindowsを想定しています。
コマンドは下のコマンドを順番に実行してください。
c:\mingw64\bin\as.exe boot.s -o boot.o -a > boot.lst
c:\mingw64\bin\ld.exe boot.o -o boot.tmp -Ttext=0x7c00 -T NUL
c:\mingw64\bin\objcopy.exe -O binary boot.tmp boot.bin
c:\mingw64\bin\as.exe kernel.s -o kernel.o -a > kernel.lst
c:\mingw64\bin\ld.exe kernel.o -o kernel.tmp -Ttext=0x00101000 -T NUL
c:\mingw64\bin\objcopy.exe -O binary kernel.tmp kernel.bin
copy /B boot.bin+kernel.bin boot.img
書籍の16章までのdefine.sを下に載せます。
// define.s
.equ boot_load,0x7c00
.equ boot_size,1024*8
.equ boot_end,boot_load+boot_size
.equ sect_size,512
.equ boot_sect,boot_size/sect_size
.equ read_boot_sect,boot_sect-1
.equ boot_load_2nd,boot_load+sect_size
.equ boot_font_adr,boot_load+sect_size
.equ mem_recode_size,20
.equ kernel_load,0x00101000
.equ kernel_size,1024*8
.equ kernel_sect,kernel_size/sect_size
.equ drive_size,2*4
書籍の16章までのboot.sを下に載せます。
// boot.s
.code16
.include "./include/define.s"
.equ start_load,0x0000
.org start_load
entry:
jmp ipl
// bpb
bpb:
.fill 90-(.-entry),1,0x90
// ipl
ipl:
cli
movw $0x0000,%ax
movw %ax,%ds
movw %ax,%es
movw %ax,%ss
movw boot_load,%sp
sti
// save boot drive
movb %dl,boot_drive_num
// diaplay boot message
pushw $boot_message
call puts
add $2,%sp
// read sector
movw $read_boot_sect,%bx
movw $boot_load_2nd,%cx
pushw %cx
pushw %bx
pushw $boot
call read_chs
add $6,%sp
cmp %bx,%ax
jz 1f
pushw $error_message
call puts
add $2,%sp
call reboot
1:
// go to 2nd stage
jmp stage_2
boot_message:
.ascii "Booting..."
.byte 0x0a
.byte 0x0d
.byte 0x00
error_message:
.ascii "Error:sector read"
.byte 0x00
.balign 2
// boot drive
boot:
boot_drive_num:
.word 0x0000
boot_drive_cyln:
.word 0x0000
boot_drive_head:
.word 0x0000
boot_drive_sect:
.word 0x0002
// module
.include "./real/puts.s"
.include "./real/reboot.s"
.include "./real/read_chs.s"
// boot flag
.fill 510-(.-entry),1,0x00
.byte 0x55
.byte 0xaa
// over 512byte
// font
font:
font_seg:
.word 0x0000
font_off:
.word 0x0000
// acpi data
acpi_data:
acpi_data_adr:
.long 0
acpi_data_len:
.long 0
// module
.include "./real/putc.s"
.include "./real/itoa.s"
.include "./real/get_drive_param.s"
.include "./real/get_font_adr.s"
.include "./real/get_mem_info.s"
.include "./real/kbc.s"
.include "./real/lba_chs.s"
.include "./real/read_lba.s"
// boot 2nd stage
stage_2:
// display string
pushw $boot_2nd_message
call puts
add $2,%sp
// get drive param
pushw $boot
call get_drive_param
add $2,%sp
jne 1f
pushw $error_get_drive_param
call puts
add $2,%sp
call reboot
1:
// display drive param
movw boot_drive_num,%ax
pushw $0b0100
pushw $16
pushw $2
pushw $drive_message_2
pushw %ax
call itoa
add $10,%sp
movw boot_drive_cyln,%ax
pushw $0b0100
pushw $16
pushw $4
pushw $drive_message_3
pushw %ax
call itoa
add $10,%sp
movw boot_drive_head,%ax
pushw $0b0100
pushw $16
pushw $2
pushw $drive_message_4
pushw %ax
call itoa
add $10,%sp
movw boot_drive_sect,%ax
pushw $0b0100
pushw $16
pushw $2
pushw $drive_message_5
pushw %ax
call itoa
add $10,%sp
pushw $drive_message_1
call puts
add $2,%sp
jmp stage_3
boot_2nd_message:
.ascii "2nd stage..."
.byte 0x0a
.byte 0x0d
.byte 0x00
drive_message_1:
.ascii " Drive:0x"
drive_message_2:
.ascii " , C:0x"
drive_message_3:
.ascii " , H:0x"
drive_message_4:
.ascii " , S:0x"
drive_message_5:
.ascii " "
.byte 0x0a
.byte 0x0d
.byte 0x00
error_get_drive_param:
.ascii "Can't get drive parameter."
.byte 0x00
.balign 2
stage_3:
// display 3rd message
pushw $boot_3rd_message
call puts
add $2,%sp
// get font adr
pushw $font
call get_font_adr
add $2,%sp
// display font adr
pushw $0b0100
pushw $16
pushw $4
pushw $font_message_2
pushw font_seg
call itoa
add $10,%sp
pushw $0b0100
pushw $16
pushw $4
pushw $font_message_3
pushw font_off
call itoa
add $10,%sp
pushw $font_message_1
call puts
add $2,%sp
// get and display mem info
pushw $acpi_data
call get_mem_info
add $2,%sp
movl acpi_data_adr,%eax
cmp $0,%eax
je 1f
pushw $0b0100
pushw $16
pushw $4
pushw $acpi_data_message_3
pushw %ax
call itoa
add $10,%sp
shr $16,%eax
pushw $0b0100
pushw $16
pushw $4
pushw $acpi_data_message_2
pushw %ax
call itoa
add $10,%sp
pushw $acpi_data_message_1
call puts
add $2,%sp
1:
jmp stage_4
boot_3rd_message:
.ascii "3rd stage..."
.byte 0x0a
.byte 0x0d
.byte 0x00
font_message_1:
.ascii " Font Address="
font_message_2:
.ascii "ZZZZ:"
font_message_3:
.ascii "ZZZZ"
.byte 0x0a
.byte 0x0d
.byte 0x00
.byte 0x0a
.byte 0x0d
.byte 0x00
acpi_data_message_1:
.ascii " ACPI data="
acpi_data_message_2:
.ascii "ZZZZ"
acpi_data_message_3:
.ascii "ZZZZ"
.byte 0x0a
.byte 0x0d
.byte 0x00
.balign 2
stage_4:
// display 4th message
pushw $boot_4th_message
call puts
add $2,%sp
// enable a20 gate
cli
/*
pushw $0xad
call kbc_cmd_write
add $2,%sp
pushw $0xd0
call kbc_cmd_write
add $2,%sp
pushw $kbc_key
call kbc_data_read
add $2,%sp
movb kbc_key,%bl
or $0x02,%bl
pushw $0xd1
call kbc_cmd_write
add $2,%sp
pushw %bx
call kbc_data_write
add $2,%sp
pushw $0xae
call kbc_cmd_write
add $2,%sp
*/
movb $0x24,%ah
movb $0x01,%al
int $0x15
// display a20 gate message
pushw $a20_gate_message
call puts
add $2,%sp
jmp stage_5
boot_4th_message:
.ascii "4th stage..."
.byte 0x0a
.byte 0x0d
.byte 0x00
a20_gate_message:
.ascii " A20 Gate Enabled."
.byte 0x0a
.byte 0x0d
.byte 0x00
kbc_key:
.word 0x0000
.balign 2
stage_5:
// display 5th message
pushw $boot_5th_message
call puts
add $2,%sp
// read kernel
pushw $boot_end
pushw $kernel_sect
pushw $boot_sect
pushw $boot
call read_lba
add $8,%sp
cmp $kernel_sect,%ax
jz 1f
pushw $error_load_kernel
call puts
add $2,%sp
call reboot
1:
jmp stage_6
boot_5th_message:
.ascii "5th stage..."
.byte 0x0a
.byte 0x0d
.byte 0x00
error_load_kernel:
.ascii " Failure load kernel..."
.byte 0x0a
.byte 0x0d
.byte 0x00
load_kernel_size:
.ascii "ZZZZ"
.byte 0x0a
.byte 0x0d
.byte 0x00
.balign 2
stage_6:
// display 6th message
pushw $boot_6th_message
call puts
add $2,%sp
// wait input space
1:
movb $0x00,%ah
int $0x16
cmp $' ',%al
jne 1b
// set up video mode
movw $0x0012,%ax
int $0x10
jmp stage_7
boot_6th_message:
.ascii "6th stage..."
.byte 0x0a
.byte 0x0d
.byte 0x0a
.byte 0x0d
.ascii " [Push SPACE key to protect mode...]"
.byte 0x0a
.byte 0x0d
.byte 0x00
// global descriptor table
.balign 4
gdt:
.quad 0x0000000000000000
gdt_cs:
.quad 0x00cf9a000000ffff
gdt_ds:
.quad 0x00cf92000000ffff
.equ sel_code,8
.equ sel_data,16
gdtr:
gdtr_limit:
.word 23
gdtr_adr:
.long gdt
idtr:
idtr_limit:
.word 0
idtr_adr:
.long 0
stage_7:
// disable interrupt
cli
movb $0x20,%al
outb %al,$0x20
outb %al,$0xa0
movb $0xff,%al
outb %al,$0x21
outb %al,$0xa1
// set gdtr and idtr
lgdt gdtr
//lidt idtr
// go to protect mode
movl %cr0,%eax
or $1,%ax
movl %eax,%cr0
jmp 1f
1:
// jmp between segment
.code32
.byte 0x66
ljmp $sel_code,$code_32
// start 32bit code
code_32:
// initialize select
movw $sel_data,%ax
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
movw %ax,%ss
// copy kernel
movl $kernel_size/4,%ecx
movl $boot_end,%esi
movl $kernel_load,%edi
cld
rep movsd
//debug
xchg %bx,%bx
jmp $sel_code,$kernel_load
// this file is 8k byte
.fill boot_size-(.-entry),1,0x00
書籍の16章のAを出力するまでのkernel.sを下に載せます。
// kernel.s
.include "./include/define.s"
.code32
.equ start_kernel,0x0000
.org start_kernel
kernel:
// get font adr
movl $boot_font_adr,%esi
movzxw 0(%esi),%eax
movzxw 2(%esi),%ebx
shl $4,%eax
add %ebx,%eax
movl %eax,font_adr
// put char
movl $'A',%esi
shl $4,%esi
add font_adr,%esi
movl $2,%edi
shl $8,%edi
lea 0xa0000(%edi,%edi,4),%edi
movl $16,%ecx
1:
movsb
addl $79,%edi
loop 1b
/*
// put char
pushl $'A'
pushl $0x010f
pushl $0
pushl $0
call draw_char
add $16,%sp
*/
jmp .
.balign 4
font_adr:
.long 0
// module
.include "./protect/vga.s"
.include "./protect/draw_char.s"
.fill kernel_size-(.-kernel),1,0x00