diff --git a/boot.subx b/boot.subx index 287c6877..2a896a97 100644 --- a/boot.subx +++ b/boot.subx @@ -302,11 +302,11 @@ idt_start: # https://wiki.osdev.org/index.php?title=Interrupts&oldid=25102#Default_PC_Interrupt_Vector_Assignment # entry 8: https://wiki.osdev.org/Programmable_Interval_Timer - null-interrupt-handler/imm16 # target[0:16] + timer-interrupt-handler/imm16 # target[0:16] 8/imm16 # segment selector (gdt_code) 00 # unused 8e # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate - 0/imm16 # target[16:32] -- null-interrupt-handler must be within address 0x10000 + 0/imm16 # target[16:32] -- timer-interrupt-handler must be within address 0x10000 # entry 9: keyboard keyboard-interrupt-handler/imm16 # target[0:16] @@ -342,7 +342,7 @@ idt_start: == code -null-interrupt-handler: +timer-interrupt-handler: # prologue fa/disable-interrupts 60/push-all-registers @@ -351,13 +351,35 @@ null-interrupt-handler: b0/copy-to-al 0x20/imm8 e6/write-al-into-port 0x20/imm8 31/xor %eax 0/r32/eax -$null-interrupt-handler:end: + # ecx = *Timer-current-color + 8b/-> *Timer-current-color 1/r32/ecx + # update *Timer-current-color + 81 0/subop/add *Timer-current-color 0x01010101/imm32 + # eax = *Video-memory + 0x1200 (a few rows down from top, around middle of screen) + 8b/-> *Video-memory-addr 0/r32/eax + 05/add-to-eax 0x1200/imm32 + 89/copy *eax 1/r32/ecx + # eax += 0x400 + 05/add-to-eax 0x400/imm32 + 89/copy *eax 1/r32/ecx + # eax += 0x400 + 05/add-to-eax 0x400/imm32 + 89/copy *eax 1/r32/ecx + # eax += 0x400 + 05/add-to-eax 0x400/imm32 + 89/copy *eax 1/r32/ecx +$timer-interrupt-handler:epilogue: # epilogue 9d/pop-flags 61/pop-all-registers fb/enable-interrupts cf/return-from-interrupt +== data +Timer-current-color: + 0/imm32 + +== code keyboard-interrupt-handler: # prologue fa/disable-interrupts diff --git a/timer.subx b/timer.subx deleted file mode 100644 index 89c5ef6d..00000000 --- a/timer.subx +++ /dev/null @@ -1,464 +0,0 @@ -# Example bootloader that handles just the timer interrupt. -# -# Code for the first few disk sectors that all programs in this directory need: -# - load sectors past the first (using BIOS primitives) since only the first is available by default -# - if this fails, print 'D' at top-left of screen and halt -# - initialize a minimal graphics mode -# - switch to 32-bit mode (giving up access to BIOS primitives) -# - set up a handler for keyboard events -# - jump to start of program - -# Code in this file needs to be more deliberate about the SubX facilities it -# uses: -# - sigils only support 32-bit general-purpose registers, so don't work with segment registers or 16-bit or 8-bit registers -# - metadata like rm32 and r32 can sometimes misleadingly refer to only the bottom 16 bits of the register; pay attention to the register name -# -# While most of Mu is thoroughly tested, this file is not. I don't yet -# understand hardware interfaces well enough to explain to others. - -# Memory map of a Mu computer: -# code: currently 4 tracks loaded from the primary disk to [0x00007c00, 0x00048600) -# stack: grows down from 0x02000000 to 0x01000000 -# heap: [0x02000000, 0x08000000) -# see 120allocate.subx; Qemu initializes with 128MB RAM by default -# Consult https://wiki.osdev.org/Memory_Map_(x86) before modifying any of -# this. And don't forget to keep *stack-debug.subx in sync. - -== code - -## 16-bit entry point: 0x7c00 - -# Upon reset, the IBM PC: -# - loads the first sector (512 bytes) -# from some bootable image (look for the boot-sector-marker further down this file) -# to the address range [0x7c00, 0x7e00) -# - starts executing code at address 0x7c00 - - fa/disable-interrupts - - # initialize segment registers - b8/copy-to-ax 0/imm16 - 8e/->seg 3/mod/direct 0/rm32/ax 3/r32/ds - 8e/->seg 3/mod/direct 0/rm32/ax 0/r32/es - 8e/->seg 3/mod/direct 0/rm32/ax 4/r32/fs - 8e/->seg 3/mod/direct 0/rm32/ax 5/r32/gs - - # Temporarily initialize stack to 0x00070000 in real mode. - # We don't read or write the stack before we get to 32-bit mode, but BIOS - # calls do. We need to move the stack in case BIOS initializes it to some - # low address that we want to write code into. - b8/copy-to-ax 0x7000/imm16 - 8e/->seg 3/mod/direct 0/rm32/ax 2/r32/ss - bc/copy-to-esp 0/imm16 - - # undo the A20 hack: https://en.wikipedia.org/wiki/A20_line - # this is from https://github.com/mit-pdos/xv6-public/blob/master/bootasm.S - { - e4/read-port-into-al 0x64/imm8 - a8/test-bits-in-al 0x02/imm8 # set zf if bit 1 (second-least significant) is not set - 75/jump-if-!zero loop/disp8 - b0/copy-to-al 0xd1/imm8 - e6/write-al-into-port 0x64/imm8 - } - { - e4/read-port-into-al 0x64/imm8 - a8/test-bits-in-al 0x02/imm8 # set zf if bit 1 (second-least significant) is not set - 75/jump-if-!zero loop/disp8 - b0/copy-to-al 0xdf/imm8 - e6/write-al-into-port 0x64/imm8 - } - - # load remaining sectors from first two tracks of disk into addresses [0x7e00, 0x17800) - b4/copy-to-ah 2/imm8/read-drive - # dl comes conveniently initialized at boot time with the index of the device being booted - b5/copy-to-ch 0/imm8/cylinder - b6/copy-to-dh 0/imm8/head # <==== - b1/copy-to-cl 2/imm8/sector # 1-based - b0/copy-to-al 0x7d/imm8/num-sectors # 2*63 - 1 = 125 - # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment - bb/copy-to-bx 0/imm16 - 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - bb/copy-to-bx 0x7e00/imm16 # <==== - cd/syscall 0x13/imm8/bios-disk-services - 0f 82/jump-if-carry disk_error/disp16 - - # load two more tracks of disk into addresses [0x17800, 0x27400) - b4/copy-to-ah 2/imm8/read-drive - # dl comes conveniently initialized at boot time with the index of the device being booted - b5/copy-to-ch 0/imm8/cylinder - b6/copy-to-dh 2/imm8/head # <==== - b1/copy-to-cl 1/imm8/sector # 1-based - b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 - # address to write sectors to = es:bx = 0x17800, contiguous with boot segment - bb/copy-to-bx 0x1780/imm16 # <==== - 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - bb/copy-to-bx 0/imm16 - cd/syscall 0x13/imm8/bios-disk-services - 0f 82/jump-if-carry disk_error/disp16 - - # load two more tracks of disk into addresses [0x27400, 0x37000) - b4/copy-to-ah 2/imm8/read-drive - # dl comes conveniently initialized at boot time with the index of the device being booted - b5/copy-to-ch 0/imm8/cylinder - b6/copy-to-dh 4/imm8/head # <==== - b1/copy-to-cl 1/imm8/sector # 1-based - b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 - # address to write sectors to = es:bx = 0x27400, contiguous with boot segment - bb/copy-to-bx 0x2740/imm16 # <==== - 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - bb/copy-to-bx 0/imm16 - cd/syscall 0x13/imm8/bios-disk-services - 0f 82/jump-if-carry disk_error/disp16 - - # load two more tracks of disk into addresses [0x37000, 0x46c00) - b4/copy-to-ah 2/imm8/read-drive - # dl comes conveniently initialized at boot time with the index of the device being booted - b5/copy-to-ch 0/imm8/cylinder - b6/copy-to-dh 6/imm8/head # <==== - b1/copy-to-cl 1/imm8/sector # 1-based - b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 - # address to write sectors to = es:bx = 0x37000, contiguous with boot segment - bb/copy-to-bx 0x3700/imm16 # <==== - 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - bb/copy-to-bx 0/imm16 - cd/syscall 0x13/imm8/bios-disk-services - 0f 82/jump-if-carry disk_error/disp16 - - # load two more tracks of disk into addresses [0x46c00, 0x56800) - b4/copy-to-ah 2/imm8/read-drive - # dl comes conveniently initialized at boot time with the index of the device being booted - b5/copy-to-ch 0/imm8/cylinder - b6/copy-to-dh 8/imm8/head # <==== - b1/copy-to-cl 1/imm8/sector # 1-based - b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 - # address to write sectors to = es:bx = 0x46c00, contiguous with boot segment - bb/copy-to-bx 0x46c0/imm16 # <==== - 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - bb/copy-to-bx 0/imm16 - cd/syscall 0x13/imm8/bios-disk-services - 0f 82/jump-if-carry disk_error/disp16 - - # load two more tracks of disk into addresses [0x56800, 0x66400) - b4/copy-to-ah 2/imm8/read-drive - # dl comes conveniently initialized at boot time with the index of the device being booted - b5/copy-to-ch 0/imm8/cylinder - b6/copy-to-dh 0xa/imm8/head # <==== - b1/copy-to-cl 1/imm8/sector # 1-based - b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 - # address to write sectors to = es:bx = 0x56800, contiguous with boot segment - bb/copy-to-bx 0x5680/imm16 # <==== - 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - bb/copy-to-bx 0/imm16 - cd/syscall 0x13/imm8/bios-disk-services - 0f 82/jump-if-carry disk_error/disp16 - - # reset es - bb/copy-to-bx 0/imm16 - 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - - # adjust video mode - b4/copy-to-ah 0x4f/imm8 # VBE commands - b0/copy-to-al 2/imm8 # set video mode - bb/copy-to-bx 0x4105/imm16 # 0x0105 | 0x4000 - # 0x0105 = graphics mode 1024x768x256 - # (alternative candidate: 0x0101 for 640x480x256) - # 0x4000 bit = configure linear frame buffer in Bochs emulator; hopefully this doesn't hurt anything when running natively - cd/syscall 0x10/imm8/bios-video-services - - # load information for the (hopefully) current video mode - # mostly just for the address to the linear frame buffer - b4/copy-to-ah 0x4f/imm8 # VBE commands - b0/copy-to-al 1/imm8 # get video mode info - b9/copy-to-cx 0x0105/imm16 # mode we requested - bf/copy-to-di Video-mode-info/imm16 - cd/syscall 0x10/imm8/bios-video-services - - ## switch to 32-bit mode - # load global descriptor table - # We can't refer to the label directly because SubX doesn't do the right - # thing for lgdt, so rather than make errors worse in most places we instead - # pin gdt_descriptor below. - 0f 01 2/subop/lgdt 0/mod/indirect 6/rm32/use-disp16 0x7de0/disp16/gdt_descriptor - # enable paging - 0f 20/<-cr 3/mod/direct 0/rm32/eax 0/r32/cr0 - 66 83 1/subop/or 3/mod/direct 0/rm32/eax 1/imm8 # eax <- or 0x1 - 0f 22/->cr 3/mod/direct 0/rm32/eax 0/r32/cr0 - # far jump to initialize_32bit_mode that sets cs to offset 8 in the gdt in the process - # We can't refer to the label directly because SubX doesn't have syntax for - # segment selectors. So we instead pin initialize_32bit_mode below. - ea/jump-far-absolute 0x00087e00/disp32 # address 0x7e00 in offset 8 of the gdt - -disk_error: - # print 'D' to top-left of screen to indicate disk error - # *0xb8000 <- 0x0f44 - bb/copy-to-bx 0xb800/imm16 - 8e/->seg 3/mod/direct 3/rm32/bx 3/r32/ds - b0/copy-to-al 0x44/imm8/D - b4/copy-to-ah 0x0f/imm8/white-on-black - bb/copy-to-bx 0/imm16 - 89/<- 0/mod/indirect 7/rm32/bx 0/r32/ax # *ds:bx <- ax - # loop forever - { - eb/jump loop/disp8 - } - -## GDT: 3 records of 8 bytes each -== data 0x7de0 -gdt_descriptor: - 0x17/imm16 # final index of gdt = size of gdt - 1 - gdt_start/imm32/start - -gdt_start: -# offset 0: gdt_null: mandatory null descriptor - 00 00 00 00 00 00 00 00 -# offset 8: gdt_code - 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] -# offset 16: gdt_data - 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: - -== boot-sector-marker 0x7dfe -# final 2 bytes of boot sector -55 aa - -## sector 2 onwards loaded by load_disk, not automatically on boot - -## 32-bit code from this point - -== code 0x7e00 -initialize_32bit_mode: - 66 b8/copy-to-ax 0x10/imm16 # offset 16 from gdt_start - 8e/->seg 3/mod/direct 0/rm32/ax 3/r32/ds - 8e/->seg 3/mod/direct 0/rm32/ax 2/r32/ss - 8e/->seg 3/mod/direct 0/rm32/ax 0/r32/es - 8e/->seg 3/mod/direct 0/rm32/ax 4/r32/fs - 8e/->seg 3/mod/direct 0/rm32/ax 5/r32/gs - - bc/copy-to-esp 0x02000000/imm32 - - ## load interrupt handlers - # We can't refer to the label directly because SubX doesn't do the right - # thing for lidt, so rather than make errors worse in most places we instead - # pin idt_descriptor below. - 0f 01 3/subop/lidt 0/mod/indirect 5/rm32/use-disp32 0x7f00/disp32/idt_descriptor - - # enable timer IRQ0 and keyboard IRQ1 - b0/copy-to-al 0xfc/imm8 # disable mask for IRQ0 and IRQ1 - e6/write-al-into-port 0x21/imm8 - - fb/enable-interrupts - - { - eb/jump loop/disp8 - } - -== data 0x7f00 -idt_descriptor: - ff 03 # final index of idt = size of idt - 1 - idt_start/imm32/start - -# interrupt descriptor table {{{ -# 32 entries of 8 bytes each -idt_start: - -# entry 0 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 - -# By default, BIOS maps IRQ0-7 to interrupt vectors 8-15. -# https://wiki.osdev.org/index.php?title=Interrupts&oldid=25102#Default_PC_Interrupt_Vector_Assignment - -# entry 8: https://wiki.osdev.org/Programmable_Interval_Timer - timer-interrupt-handler/imm16 # target[0:16] - 8/imm16 # segment selector (gdt_code) - 00 # unused - 8e # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate - 0/imm16 # target[16:32] -- timer-interrupt-handler must be within address 0x10000 - -# entry 9: keyboard - keyboard-interrupt-handler/imm16 # target[0:16] - 8/imm16 # segment selector (gdt_code) - 00 # unused - 8e # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate - 0/imm16 # target[16:32] -- keyboard-interrupt-handler must be within address 0x10000 - -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 -# idt_end: -# }}} - -== code - -timer-interrupt-handler: - # prologue - fa/disable-interrupts - 60/push-all-registers - 9c/push-flags - # acknowledge interrupt - b0/copy-to-al 0x20/imm8 - e6/write-al-into-port 0x20/imm8 - 31/xor %eax 0/r32/eax - # ecx = *Timer-current-color - 8b/-> *Timer-current-color 1/r32/ecx - # update *Timer-current-color - 81 0/subop/add *Timer-current-color 0x01010101/imm32 - # eax = *Video-memory + 0x1200 (a few rows down from top, around middle of screen) - 8b/-> *Video-memory-addr 0/r32/eax - 05/add-to-eax 0x1200/imm32 - 89/copy *eax 1/r32/ecx - # eax += 0x400 - 05/add-to-eax 0x400/imm32 - 89/copy *eax 1/r32/ecx - # eax += 0x400 - 05/add-to-eax 0x400/imm32 - 89/copy *eax 1/r32/ecx - # eax += 0x400 - 05/add-to-eax 0x400/imm32 - 89/copy *eax 1/r32/ecx -$timer-interrupt-handler:epilogue: - # epilogue - 9d/pop-flags - 61/pop-all-registers - fb/enable-interrupts - cf/return-from-interrupt - -keyboard-interrupt-handler: - # prologue - fa/disable-interrupts - 60/push-all-registers - 9c/push-flags - # acknowledge interrupt - b0/copy-to-al 0x20/imm8 - e6/write-al-into-port 0x20/imm8 - 31/xor %eax 0/r32/eax - # check output buffer of 8042 keyboard controller (https://web.archive.org/web/20040604041507/http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/keyboard/atkeyboard.html) - e4/read-port-into-al 0x64/imm8 - a8/test-bits-in-al 0x01/imm8 # set zf if bit 0 (least significant) is not set - 0f 84/jump-if-not-set $keyboard-interrupt-handler:epilogue/disp32 - # initialize current-pixel-location if necessary - 81 7/subop/compare *Keyboard-current-pixel 0/imm32 - { - 75/jump-if-!= break/disp8 - 8b/-> *Video-memory-addr 1/r32/ecx - 89/<- *Keyboard-current-pixel 1/r32/ecx - } - # - read keycode - e4/read-port-into-al 0x60/imm8 - # if (al & 0x80) a key is being lifted; return - 50/push-eax - 24/and-al-with 0x80/imm8 - 3c/compare-al-and 0/imm8 - 58/pop-to-eax - 75/jump-if-!= $keyboard-interrupt-handler:epilogue/disp8 - # key pressed - 8b/-> *Keyboard-current-pixel 1/r32/ecx - c6 0/subop/copy-byte *ecx 0x31/imm8/green - ff 0/subop/increment *Keyboard-current-pixel -$keyboard-interrupt-handler:epilogue: - # epilogue - 9d/pop-flags - 61/pop-all-registers - fb/enable-interrupts - cf/return-from-interrupt - -== data - -Video-mode-info: -# video mode info {{{ - 0/imm16 # attributes - 00 # winA - 00 # winB -# 04 - 0/imm16 # granularity - 0/imm16 # winsize -# 08 - 0/imm16 # segmentA - 0/imm16 # segmentB -# 0c - 0/imm32 # realFctPtr (who knows) -# 10 - 0/imm16 # pitch - 0/imm16 # Xres - 0/imm16 # Yres - 0/imm16 # Wchar Ychar -# 18 - 00 # planes - 00 # bpp - 00 # banks - 00 # memory_model -# 1c - 00 # bank_size - 00 # image_pages - 00 # reserved -# 1f - 0/imm16 # red_mask red_position - 0/imm16 # green_mask green_position - 0/imm16 # blue_mask blue_position - 0/imm16 # rsv_mask rsv_position - 00 # directcolor_attributes -# 28 -Video-memory-addr: - 0/imm32 # physbase - -# 2c -# reserved for video mode info - 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -# }}} - -Timer-current-color: - 0/imm32 - -Keyboard-current-pixel: - 0/imm32 - -# vim:ft=subx