diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/boot.bochsrc | 8 | ||||
-rw-r--r-- | apps/boot.hex | 183 |
2 files changed, 191 insertions, 0 deletions
diff --git a/apps/boot.bochsrc b/apps/boot.bochsrc new file mode 100644 index 00000000..6643905c --- /dev/null +++ b/apps/boot.bochsrc @@ -0,0 +1,8 @@ +# Configuration for the Bochs x86 CPU emulator to run the output of apps/boot.hex +# See apps/boot.hex for more details. + +# You'll probably want to adjust the disk geometry below when you update +# boot.hex and alter the size of boot.bin. +ata0-master: type=disk, path="boot.bin", mode=flat, cylinders=1, heads=1, spt=1 +boot: disk +log: - diff --git a/apps/boot.hex b/apps/boot.hex new file mode 100644 index 00000000..fc80b442 --- /dev/null +++ b/apps/boot.hex @@ -0,0 +1,183 @@ +# Bootable image that: +# - loads more sectors past the first boot sector (using BIOS primitives) +# - switches to 32-bit mode (giving up access to BIOS primitives) +# - as an example program, prints 'H' to the top-left of the screen (by writing to memory-mapped VGA memory) +# +# If the initial load fails, it prints 'D' to the top-left of the screen and +# halts. +# +# To convert to a disk image: +# ./bootstrap run apps/hex < apps/boot.hex > boot.bin +# To run: +# qemu-system-i386 boot.bin +# Or: +# bochs -f apps/boot.bochsrc # boot.bochsrc loads boot.bin +# +# Since we start out in 16-bit mode, we need instructions SubX doesn't +# support. +# This file contains just hex bytes and comments. Zero error-checking. Make +# liberal use of: +# - comments documenting expected offsets +# - size checks on the emitted file (currently: 512 bytes) +# - xxd to eyeball that offsets contain expected bytes + +## 16-bit entry point + +# Upon reset, the IBM PC +# loads the first sector (512 bytes) +# from some bootable image (see the boot sector marker at the end of this file) +# to the address range [0x7c00, 0x7e00) +# +# We don't read or write the stack before we get to 32-bit mode. No function +# calls. + +# 00: +# load_disk: + b4 02 # ah <- 2 # read sectors from disk + b2 00 # dl <- 0 # drive 0 + b5 00 # ch <- 0 # cylinder 0 + b6 00 # dh <- 0 # track 0 + b1 02 # cl <- 2 # sector 1 (second sector, 1-based) + b0 01 # al <- 1 # number of sectors to read + # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment + bb 00 00 # bx <- 0 + 8e c3 # es <- bx + bb 00 7e # bx <- 0x7e00 + cd 13 # int 13h, BIOS disk service + 0f 82 76 00 # jump-if-carry disk-error + +# 1a: + fa # cli # TODO: don't forget to reenable interrupts in a real program + 0f 01 16 # lgdt 00/mod/indirect 010/subop 110/rm32/TODO + 80 7c # *gdt_descriptor +# 20: + 0f 20 c0 # eax <- cr0 + 66 83 c8 01 # eax <- or 0x1 + 0f 22 c0 # cr0 <- eax + ea c0 7c 08 00 # far jump to initialize_32bit_mode after setting cs to the record at offset 8 in the gdt (gdt_code) + +# padding +# 2f: + 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## GDT: 3 records of 8 bytes each + +# 60: +# gdt_start: +# gdt_null: mandatory null descriptor + 00 00 00 00 00 00 00 00 +# gdt_code: (offset 8 from gdt_start) + ff ff # limit[0:16] + 00 00 00 # base[0:24] + 9a # 1/present 00/privilege 1/descriptor type = 1001b + # 1/code 0/conforming 1/readable 0/accessed = 1010b + cf # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b + # limit[16:20] = 1111b + 00 # base[24:32] +# gdt_data: (offset 16 from gdt_start) + ff ff # limit[0:16] + 00 00 00 # base[0:24] + 92 # 1/present 00/privilege 1/descriptor type = 1001b + # 0/data 0/conforming 1/readable 0/accessed = 0010b + cf # same as gdt_code + 00 # base[24:32] +# gdt_end: + +# padding +# 78: + 00 00 00 00 00 00 00 00 + +# 80: +# gdt_descriptor: + 17 00 # final index of gdt = gdt_end - gdt_start - 1 + 60 7c 00 00 # start = gdt_start + +# padding +# 85: + 00 00 00 00 00 00 00 00 00 00 + +# 90: +# disk_error: + # print 'D' to top-left of screen to indicate disk error + # *0xb8000 <- 0x0f44 + # bx <- 0xb800 + bb 00 b8 + # ds <- bx + 8e db # 11b/mod 011b/reg/ds 011b/rm/bx + # al <- 'D' + b0 44 + # ah <- 0x0f # white on black + b4 0f + # bx <- 0 + bb 00 00 + # *ds:bx <- ax + 89 07 # 00b/mod/indirect 000b/reg/ax 111b/rm/bx + +e9 fb ff # loop forever + +# padding +# a1: + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## 32-bit code from this point (still some instructions not in SubX) + +# c0: +# initialize_32bit_mode: + 66 b8 10 00 # ax <- offset 16 from gdt_start + 8e d8 # ds <- ax + 8e d0 # ss <- ax + 8e c0 # es <- ax + 8e e0 # fs <- ax + 8e e8 # gs <- ax + e9 1d 00 00 00 # jump to 0x7cf0 + +# padding +# d3: + 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## 'application' SubX code: print one character to top-left of screen + +# f0: +# Entry: + # *0xb8000 <- 0x0f48 + c7 # opcode + # modrm + 05 # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32 + # disp32 + 00 80 0b 00 + # imm32 + 48 # 'H' + 0f # white on black + 00 00 + +e9 fb ff ff ff # loop forever + +# padding to 512 bytes +# ff: + 00 +# 100: +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +55 aa # final 2 bytes: boot sector marker + +# vim:ft=subx |