yokobuttonの不定期で競技プログラミングをするブログ

不定期で解けた競技プログラミングコンテストの問題を載せています。

「作って理解する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

  sti

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