From 3120f938c660ab5fcecb579122440d487ccd798e Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 23 Mar 2021 22:27:46 -0700 Subject: . --- html/boot.subx.html | 1281 ++++++++++++++++++++++----------------------------- 1 file changed, 552 insertions(+), 729 deletions(-) (limited to 'html/boot.subx.html') diff --git a/html/boot.subx.html b/html/boot.subx.html index 4518b0d0..5fa48c95 100644 --- a/html/boot.subx.html +++ b/html/boot.subx.html @@ -69,178 +69,178 @@ if ('onhashchange' in window) { 10 # uses: 11 # - sigils only support 32-bit general-purpose registers, so don't work with segment registers or 16-bit or 8-bit registers 12 # - metadata like rm32 and r32 can sometimes misleadingly refer to only the bottom 16 bits of the register; pay attention to the register name - 13 - 14 # Memory map of a Mu computer: - 15 # code: 4 tracks of disk to [0x00007c00, 0x00027400) - 16 # stack grows down from 0x00070000 - 17 # see below - 18 # heap: [0x01000000, 0x02000000) - 19 # see 120allocate.subx - 20 # Consult https://wiki.osdev.org/Memory_Map_(x86) before modifying any of this. - 21 - 22 == code + 13 # + 14 # While most of Mu is thoroughly tested, this file is not. I don't yet + 15 # understand hardware interfaces well enough to explain to others. + 16 + 17 # Memory map of a Mu computer: + 18 # code: currently 4 tracks loaded from the primary disk to [0x00007c00, 0x00027400) + 19 # stack: grows down from 0x00070000 + 20 # heap: [0x01000000, 0x02000000) + 21 # see 120allocate.subx + 22 # Consult https://wiki.osdev.org/Memory_Map_(x86) before modifying any of this. 23 - 24 ## 16-bit entry point: 0x7c00 + 24 == code 25 - 26 # Upon reset, the IBM PC: - 27 # - loads the first sector (512 bytes) - 28 # from some bootable image (look for the boot-sector-marker further down this file) - 29 # to the address range [0x7c00, 0x7e00) - 30 # - starts executing code at address 0x7c00 - 31 - 32 fa/disable-interrupts + 26 ## 16-bit entry point: 0x7c00 + 27 + 28 # Upon reset, the IBM PC: + 29 # - loads the first sector (512 bytes) + 30 # from some bootable image (look for the boot-sector-marker further down this file) + 31 # to the address range [0x7c00, 0x7e00) + 32 # - starts executing code at address 0x7c00 33 - 34 # initialize segment registers - 35 b8/copy-to-ax 0/imm16 - 36 8e/->seg 3/mod/direct 0/rm32/ax 3/r32/ds - 37 8e/->seg 3/mod/direct 0/rm32/ax 0/r32/es - 38 8e/->seg 3/mod/direct 0/rm32/ax 4/r32/fs - 39 8e/->seg 3/mod/direct 0/rm32/ax 5/r32/gs - 40 - 41 # initialize stack to 0x00070000 - 42 # We don't read or write the stack before we get to 32-bit mode, but BIOS - 43 # calls do. We need to move the stack in case BIOS initializes it to some - 44 # low address that we want to write code into. - 45 b8/copy-to-ax 0x7000/imm16 - 46 8e/->seg 3/mod/direct 0/rm32/ax 2/r32/ss - 47 bc/copy-to-esp 0/imm16 - 48 - 49 # undo the A20 hack: https://en.wikipedia.org/wiki/A20_line - 50 # this is from https://github.com/mit-pdos/xv6-public/blob/master/bootasm.S - 51 { - 52 e4/read-port-into-al 0x64/imm8 - 53 a8/test-bits-in-al 0x02/imm8 # set zf if bit 1 (second-least significant) is not set - 54 75/jump-if-!zero loop/imm8 - 55 b0/copy-to-al 0xd1/imm8 - 56 e6/write-al-into-port 0x64/imm8 - 57 } - 58 { - 59 e4/read-port-into-al 0x64/imm8 - 60 a8/test-bits-in-al 0x02/imm8 # set zf if bit 1 (second-least significant) is not set - 61 75/jump-if-!zero loop/imm8 - 62 b0/copy-to-al 0xdf/imm8 - 63 e6/write-al-into-port 0x64/imm8 - 64 } - 65 - 66 # load remaining sectors from first two tracks of disk into addresses [0x7e00, 0x17800) - 67 b4/copy-to-ah 2/imm8/read-drive - 68 # dl comes conveniently initialized at boot time with the index of the device being booted - 69 b5/copy-to-ch 0/imm8/cylinder - 70 b6/copy-to-dh 0/imm8/head - 71 b1/copy-to-cl 2/imm8/sector # 1-based - 72 b0/copy-to-al 0x7d/imm8/num-sectors # 2*63 - 1 = 125 - 73 # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment - 74 bb/copy-to-bx 0/imm16 - 75 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - 76 bb/copy-to-bx 0x7e00/imm16 - 77 cd/syscall 0x13/imm8/bios-disk-services - 78 0f 82/jump-if-carry disk_error/disp16 - 79 - 80 # load two more tracks of disk into addresses [0x17800, 0x27400) - 81 b4/copy-to-ah 2/imm8/read-drive - 82 # dl comes conveniently initialized at boot time with the index of the device being booted - 83 b5/copy-to-ch 0/imm8/cylinder - 84 b6/copy-to-dh 2/imm8/head - 85 b1/copy-to-cl 1/imm8/sector # 1-based - 86 b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 - 87 # address to write sectors to = es:bx = 0x17800, contiguous with boot segment - 88 bb/copy-to-bx 0x1780/imm16 - 89 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - 90 bb/copy-to-bx 0/imm16 - 91 cd/syscall 0x13/imm8/bios-disk-services - 92 0f 82/jump-if-carry disk_error/disp16 - 93 - 94 # load two more tracks of disk into addresses [0x27400, 0x37000) - 95 b4/copy-to-ah 2/imm8/read-drive - 96 # dl comes conveniently initialized at boot time with the index of the device being booted - 97 b5/copy-to-ch 0/imm8/cylinder - 98 b6/copy-to-dh 4/imm8/head - 99 b1/copy-to-cl 1/imm8/sector # 1-based - 100 b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 - 101 # address to write sectors to = es:bx = 0x27400, contiguous with boot segment - 102 bb/copy-to-bx 0x2740/imm16 - 103 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - 104 bb/copy-to-bx 0/imm16 - 105 cd/syscall 0x13/imm8/bios-disk-services - 106 0f 82/jump-if-carry disk_error/disp16 - 107 - 108 # reset es - 109 bb/copy-to-bx 0/imm16 - 110 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es - 111 - 112 # adjust video mode - 113 b4/copy-to-ah 0x4f/imm8 # VBE commands - 114 b0/copy-to-al 2/imm8 # set video mode - 115 bb/copy-to-bx 0x4105/imm16 # 0x0105 | 0x4000 - 116 # 0x0105 = graphics mode 1024x768x256 - 117 # (alternative candidate: 0x0101 for 640x480x256) - 118 # 0x4000 bit = configure linear frame buffer in Bochs emulator; hopefully this doesn't hurt anything when running natively - 119 cd/syscall 0x10/imm8/bios-video-services - 120 - 121 # load information for the (hopefully) current video mode - 122 # mostly just for the address to the linear frame buffer - 123 b4/copy-to-ah 0x4f/imm8 # VBE commands - 124 b0/copy-to-al 1/imm8 # get video mode info - 125 b9/copy-to-cx 0x0105/imm16 # mode we requested - 126 bf/copy-to-di Video-mode-info/imm16 - 127 cd/syscall 0x10/imm8/bios-video-services - 128 - 129 ## switch to 32-bit mode - 130 # load global descriptor table - 131 # We can't refer to the label directly because SubX doesn't do the right - 132 # thing for lgdt, so rather than make errors worse in most places we instead - 133 # pin gdt_descriptor below. - 134 0f 01 2/subop/lgdt 0/mod/indirect 6/rm32/use-disp16 0x7cf8/disp16/gdt_descriptor - 135 # enable paging - 136 0f 20/<-cr 3/mod/direct 0/rm32/eax 0/r32/cr0 - 137 66 83 1/subop/or 3/mod/direct 0/rm32/eax 1/imm8 # eax <- or 0x1 - 138 0f 22/->cr 3/mod/direct 0/rm32/eax 0/r32/cr0 - 139 # far jump to initialize_32bit_mode that sets cs to offset 8 in the gdt in the process - 140 # We can't refer to the label directly because SubX doesn't have syntax for - 141 # segment selectors. So we instead pin initialize_32bit_mode below. - 142 ea/jump-far-absolute 0x00087d00/disp32 # address 0x7d00 in offset 8 of the gdt - 143 - 144 disk_error: - 145 # print 'D' to top-left of screen to indicate disk error - 146 # *0xb8000 <- 0x0f44 - 147 bb/copy-to-bx 0xb800/imm16 - 148 8e/->seg 3/mod/direct 3/rm32/bx 3/r32/ds - 149 b0/copy-to-al 0x44/imm8/D - 150 b4/copy-to-ah 0x0f/imm8/white-on-black - 151 bb/copy-to-bx 0/imm16 - 152 89/<- 0/mod/indirect 7/rm32/bx 0/r32/ax # *ds:bx <- ax - 153 # loop forever - 154 { - 155 eb/jump loop/disp8 - 156 } - 157 - 158 ## GDT: 3 records of 8 bytes each - 159 == data - 160 - 161 gdt_start: - 162 # offset 0: gdt_null: mandatory null descriptor - 163 00 00 00 00 00 00 00 00 - 164 # offset 8: gdt_code - 165 ff ff # limit[0:16] - 166 00 00 00 # base[0:24] - 167 9a # 1/present 00/privilege 1/descriptor type = 1001b - 168 # 1/code 0/conforming 1/readable 0/accessed = 1010b - 169 cf # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b - 170 # limit[16:20] = 1111b - 171 00 # base[24:32] - 172 # offset 16: gdt_data - 173 ff ff # limit[0:16] - 174 00 00 00 # base[0:24] - 175 92 # 1/present 00/privilege 1/descriptor type = 1001b - 176 # 0/data 0/conforming 1/readable 0/accessed = 0010b - 177 cf # same as gdt_code - 178 00 # base[24:32] - 179 # gdt_end: - 180 - 181 == data 0x7cf8 - 182 gdt_descriptor: - 183 0x17/imm16 # final index of gdt = size of gdt - 1 - 184 gdt_start/imm32/start + 34 fa/disable-interrupts + 35 + 36 # initialize segment registers + 37 b8/copy-to-ax 0/imm16 + 38 8e/->seg 3/mod/direct 0/rm32/ax 3/r32/ds + 39 8e/->seg 3/mod/direct 0/rm32/ax 0/r32/es + 40 8e/->seg 3/mod/direct 0/rm32/ax 4/r32/fs + 41 8e/->seg 3/mod/direct 0/rm32/ax 5/r32/gs + 42 + 43 # initialize stack to 0x00070000 + 44 # We don't read or write the stack before we get to 32-bit mode, but BIOS + 45 # calls do. We need to move the stack in case BIOS initializes it to some + 46 # low address that we want to write code into. + 47 b8/copy-to-ax 0x7000/imm16 + 48 8e/->seg 3/mod/direct 0/rm32/ax 2/r32/ss + 49 bc/copy-to-esp 0/imm16 + 50 + 51 # undo the A20 hack: https://en.wikipedia.org/wiki/A20_line + 52 # this is from https://github.com/mit-pdos/xv6-public/blob/master/bootasm.S + 53 { + 54 e4/read-port-into-al 0x64/imm8 + 55 a8/test-bits-in-al 0x02/imm8 # set zf if bit 1 (second-least significant) is not set + 56 75/jump-if-!zero loop/imm8 + 57 b0/copy-to-al 0xd1/imm8 + 58 e6/write-al-into-port 0x64/imm8 + 59 } + 60 { + 61 e4/read-port-into-al 0x64/imm8 + 62 a8/test-bits-in-al 0x02/imm8 # set zf if bit 1 (second-least significant) is not set + 63 75/jump-if-!zero loop/imm8 + 64 b0/copy-to-al 0xdf/imm8 + 65 e6/write-al-into-port 0x64/imm8 + 66 } + 67 + 68 # load remaining sectors from first two tracks of disk into addresses [0x7e00, 0x17800) + 69 b4/copy-to-ah 2/imm8/read-drive + 70 # dl comes conveniently initialized at boot time with the index of the device being booted + 71 b5/copy-to-ch 0/imm8/cylinder + 72 b6/copy-to-dh 0/imm8/head + 73 b1/copy-to-cl 2/imm8/sector # 1-based + 74 b0/copy-to-al 0x7d/imm8/num-sectors # 2*63 - 1 = 125 + 75 # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment + 76 bb/copy-to-bx 0/imm16 + 77 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es + 78 bb/copy-to-bx 0x7e00/imm16 + 79 cd/syscall 0x13/imm8/bios-disk-services + 80 0f 82/jump-if-carry disk_error/disp16 + 81 + 82 # load two more tracks of disk into addresses [0x17800, 0x27400) + 83 b4/copy-to-ah 2/imm8/read-drive + 84 # dl comes conveniently initialized at boot time with the index of the device being booted + 85 b5/copy-to-ch 0/imm8/cylinder + 86 b6/copy-to-dh 2/imm8/head + 87 b1/copy-to-cl 1/imm8/sector # 1-based + 88 b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 + 89 # address to write sectors to = es:bx = 0x17800, contiguous with boot segment + 90 bb/copy-to-bx 0x1780/imm16 + 91 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es + 92 bb/copy-to-bx 0/imm16 + 93 cd/syscall 0x13/imm8/bios-disk-services + 94 0f 82/jump-if-carry disk_error/disp16 + 95 + 96 # load two more tracks of disk into addresses [0x27400, 0x37000) + 97 b4/copy-to-ah 2/imm8/read-drive + 98 # dl comes conveniently initialized at boot time with the index of the device being booted + 99 b5/copy-to-ch 0/imm8/cylinder + 100 b6/copy-to-dh 4/imm8/head + 101 b1/copy-to-cl 1/imm8/sector # 1-based + 102 b0/copy-to-al 0x7e/imm8/num-sectors # 2*63 = 126 + 103 # address to write sectors to = es:bx = 0x27400, contiguous with boot segment + 104 bb/copy-to-bx 0x2740/imm16 + 105 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es + 106 bb/copy-to-bx 0/imm16 + 107 cd/syscall 0x13/imm8/bios-disk-services + 108 0f 82/jump-if-carry disk_error/disp16 + 109 + 110 # reset es + 111 bb/copy-to-bx 0/imm16 + 112 8e/->seg 3/mod/direct 3/rm32/bx 0/r32/es + 113 + 114 # adjust video mode + 115 b4/copy-to-ah 0x4f/imm8 # VBE commands + 116 b0/copy-to-al 2/imm8 # set video mode + 117 bb/copy-to-bx 0x4105/imm16 # 0x0105 | 0x4000 + 118 # 0x0105 = graphics mode 1024x768x256 + 119 # (alternative candidate: 0x0101 for 640x480x256) + 120 # 0x4000 bit = configure linear frame buffer in Bochs emulator; hopefully this doesn't hurt anything when running natively + 121 cd/syscall 0x10/imm8/bios-video-services + 122 + 123 # load information for the (hopefully) current video mode + 124 # mostly just for the address to the linear frame buffer + 125 b4/copy-to-ah 0x4f/imm8 # VBE commands + 126 b0/copy-to-al 1/imm8 # get video mode info + 127 b9/copy-to-cx 0x0105/imm16 # mode we requested + 128 bf/copy-to-di Video-mode-info/imm16 + 129 cd/syscall 0x10/imm8/bios-video-services + 130 + 131 ## switch to 32-bit mode + 132 # load global descriptor table + 133 # We can't refer to the label directly because SubX doesn't do the right + 134 # thing for lgdt, so rather than make errors worse in most places we instead + 135 # pin gdt_descriptor below. + 136 0f 01 2/subop/lgdt 0/mod/indirect 6/rm32/use-disp16 0x7ce0/disp16/gdt_descriptor + 137 # enable paging + 138 0f 20/<-cr 3/mod/direct 0/rm32/eax 0/r32/cr0 + 139 66 83 1/subop/or 3/mod/direct 0/rm32/eax 1/imm8 # eax <- or 0x1 + 140 0f 22/->cr 3/mod/direct 0/rm32/eax 0/r32/cr0 + 141 # far jump to initialize_32bit_mode that sets cs to offset 8 in the gdt in the process + 142 # We can't refer to the label directly because SubX doesn't have syntax for + 143 # segment selectors. So we instead pin initialize_32bit_mode below. + 144 ea/jump-far-absolute 0x00087d00/disp32 # address 0x7d00 in offset 8 of the gdt + 145 + 146 disk_error: + 147 # print 'D' to top-left of screen to indicate disk error + 148 # *0xb8000 <- 0x0f44 + 149 bb/copy-to-bx 0xb800/imm16 + 150 8e/->seg 3/mod/direct 3/rm32/bx 3/r32/ds + 151 b0/copy-to-al 0x44/imm8/D + 152 b4/copy-to-ah 0x0f/imm8/white-on-black + 153 bb/copy-to-bx 0/imm16 + 154 89/<- 0/mod/indirect 7/rm32/bx 0/r32/ax # *ds:bx <- ax + 155 # loop forever + 156 { + 157 eb/jump loop/disp8 + 158 } + 159 + 160 ## GDT: 3 records of 8 bytes each + 161 == data 0x7ce0 + 162 gdt_descriptor: + 163 0x17/imm16 # final index of gdt = size of gdt - 1 + 164 gdt_start/imm32/start + 165 + 166 gdt_start: + 167 # offset 0: gdt_null: mandatory null descriptor + 168 00 00 00 00 00 00 00 00 + 169 # offset 8: gdt_code + 170 ff ff # limit[0:16] + 171 00 00 00 # base[0:24] + 172 9a # 1/present 00/privilege 1/descriptor type = 1001b + 173 # 1/code 0/conforming 1/readable 0/accessed = 1010b + 174 cf # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b + 175 # limit[16:20] = 1111b + 176 00 # base[24:32] + 177 # offset 16: gdt_data + 178 ff ff # limit[0:16] + 179 00 00 00 # base[0:24] + 180 92 # 1/present 00/privilege 1/descriptor type = 1001b + 181 # 0/data 0/conforming 1/readable 0/accessed = 0010b + 182 cf # same as gdt_code + 183 00 # base[24:32] + 184 # gdt_end: 185 186 ## 32-bit code from this point 187 @@ -259,7 +259,7 @@ if ('onhashchange' in window) { 200 # We can't refer to the label directly because SubX doesn't do the right 201 # thing for lidt, so rather than make errors worse in most places we instead 202 # pin idt_descriptor below. - 203 0f 01 3/subop/lidt 0/mod/indirect 5/rm32/use-disp32 0x8000/disp32/idt_descriptor + 203 0f 01 3/subop/lidt 0/mod/indirect 5/rm32/use-disp32 0x7e00/disp32/idt_descriptor 204 205 # For now, not bothering reprogramming the IRQ to not conflict with software 206 # exceptions. @@ -276,566 +276,389 @@ if ('onhashchange' in window) { 217 218 fb/enable-interrupts 219 - 220 ## enable floating point - 221 db/floating-point-coprocessor e3/initialize - 222 # eax <- cr4 - 223 0f 20/<-cr 3/mod/direct 0/rm32/eax 4/r32/cr4 - 224 # eax <- or bit 9 - 225 0f ba/bit-test 5/subop/bit-test-and-set 3/mod/direct 0/rm32/eax 9/imm8 - 226 # cr4 <- eax - 227 0f 22/->cr 3/mod/direct 0/rm32/eax 4/r32/cr4 - 228 - 229 e9/jump Entry/disp32 + 220 (initialize-mouse) + 221 + 222 ## enable floating point + 223 db/floating-point-coprocessor e3/initialize + 224 # eax <- cr4 + 225 0f 20/<-cr 3/mod/direct 0/rm32/eax 4/r32/cr4 + 226 # eax <- or bit 9 + 227 0f ba/bit-test 5/subop/bit-test-and-set 3/mod/direct 0/rm32/eax 9/imm8 + 228 # cr4 <- eax + 229 0f 22/->cr 3/mod/direct 0/rm32/eax 4/r32/cr4 230 - 231 == boot-sector-marker 0x7dfe - 232 # final 2 bytes of boot sector - 233 55 aa - 234 - 235 ## sector 2 onwards loaded by load_disk, not automatically on boot - 236 == code - 237 - 238 null-interrupt-handler: - 239 cf/return-from-interrupt - 240 - 241 keyboard-interrupt-handler: - 242 # prologue - 243 fa/disable-interrupts - 244 60/push-all-registers - 245 # acknowledge interrupt - 246 b0/copy-to-al 0x20/imm8 - 247 e6/write-al-into-port 0x20/imm8 - 248 31/xor %eax 0/r32/eax - 249 # check output buffer of 8042 keyboard controller (https://web.archive.org/web/20040604041507/http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/keyboard/atkeyboard.html) - 250 e4/read-port-into-al 0x64/imm8 - 251 a8/test-bits-in-al 0x01/imm8 # set zf if bit 0 (least significant) is not set - 252 74/jump-if-not-set $keyboard-interrupt-handler:epilogue/disp8 - 253 # - if keyboard buffer is full, return - 254 # var dest-addr/ecx: (addr byte) = (keyboard-buffer + *keyboard-buffer:write) - 255 31/xor %ecx 1/r32/ecx - 256 8a/byte-> *Keyboard-buffer:write 1/r32/cl - 257 81 0/subop/add %ecx Keyboard-buffer:data/imm32 - 258 # al = *dest-addr - 259 8a/byte-> *ecx 0/r32/al - 260 # if (al != 0) return - 261 3c/compare-al-and 0/imm8 - 262 75/jump-if-!= $keyboard-interrupt-handler:epilogue/disp8 - 263 # - read keycode - 264 e4/read-port-into-al 0x60/imm8 - 265 # - key released - 266 # if (al == 0xaa) shift = false # left shift is being lifted - 267 { - 268 3c/compare-al-and 0xaa/imm8 - 269 75/jump-if-!= break/disp8 - 270 # *shift = 0 - 271 c7 0/subop/copy *Keyboard-shift-pressed? 0/imm32 - 272 } - 273 # if (al == 0xb6) shift = false # right shift is being lifted - 274 { - 275 3c/compare-al-and 0xb6/imm8 - 276 75/jump-if-!= break/disp8 - 277 # *shift = 0 - 278 c7 0/subop/copy *Keyboard-shift-pressed? 0/imm32 - 279 } - 280 # if (al == 0x9d) ctrl = false # ctrl is being lifted - 281 { - 282 3c/compare-al-and 0x9d/imm8 - 283 75/jump-if-!= break/disp8 - 284 # *ctrl = 0 - 285 c7 0/subop/copy *Keyboard-ctrl-pressed? 0/imm32 - 286 } - 287 # if (al & 0x80) a key is being lifted; return - 288 50/push-eax - 289 24/and-al-with 0x80/imm8 - 290 3c/compare-al-and 0/imm8 - 291 58/pop-to-eax - 292 75/jump-if-!= $keyboard-interrupt-handler:epilogue/disp8 - 293 # - key pressed - 294 # if (al == 0x2a) shift = true, return # left shift pressed - 295 { - 296 3c/compare-al-and 0x2a/imm8 - 297 75/jump-if-!= break/disp8 - 298 # *shift = 1 - 299 c7 0/subop/copy *Keyboard-shift-pressed? 1/imm32 - 300 # return - 301 eb/jump $keyboard-interrupt-handler:epilogue/disp8 - 302 } - 303 # if (al == 0x36) shift = true, return # right shift pressed - 304 { - 305 3c/compare-al-and 0x36/imm8 - 306 75/jump-if-!= break/disp8 - 307 # *shift = 1 - 308 c7 0/subop/copy *Keyboard-shift-pressed? 1/imm32 - 309 # return - 310 eb/jump $keyboard-interrupt-handler:epilogue/disp8 - 311 } - 312 # if (al == 0x1d) ctrl = true, return - 313 { - 314 3c/compare-al-and 0x1d/imm8 - 315 75/jump-if-!= break/disp8 - 316 # *ctrl = 1 - 317 c7 0/subop/copy *Keyboard-ctrl-pressed? 1/imm32 - 318 # return - 319 eb/jump $keyboard-interrupt-handler:epilogue/disp8 - 320 } - 321 # - convert key to character - 322 # if (shift) use keyboard shift map - 323 { - 324 81 7/subop/compare *Keyboard-shift-pressed? 0/imm32 - 325 74/jump-if-= break/disp8 - 326 # sigils don't currently support labels inside *(eax+label) - 327 05/add-to-eax Keyboard-shift-map/imm32 - 328 8a/byte-> *eax 0/r32/al - 329 eb/jump $keyboard-interrupt-handler:select-map-done/disp8 - 330 } - 331 # if (ctrl) al = *(ctrl map + al) - 332 { - 333 81 7/subop/compare *Keyboard-ctrl-pressed? 0/imm32 - 334 74/jump-if-= break/disp8 - 335 05/add-to-eax Keyboard-ctrl-map/imm32 - 336 8a/byte-> *eax 0/r32/al - 337 eb/jump $keyboard-interrupt-handler:select-map-done/disp8 - 338 } - 339 # otherwise al = *(normal map + al) - 340 05/add-to-eax Keyboard-normal-map/imm32 - 341 8a/byte-> *eax 0/r32/al - 342 $keyboard-interrupt-handler:select-map-done: - 343 # - if there's no character mapping, return - 344 { - 345 3c/compare-al-and 0/imm8 - 346 74/jump-if-= break/disp8 - 347 # - store al in keyboard buffer - 348 88/<- *ecx 0/r32/al - 349 # increment index - 350 fe/increment-byte *Keyboard-buffer:write - 351 # clear top nibble of index (keyboard buffer is circular) - 352 80 4/subop/and-byte *Keyboard-buffer:write 0x0f/imm8 - 353 } - 354 $keyboard-interrupt-handler:epilogue: - 355 # epilogue - 356 61/pop-all-registers - 357 fb/enable-interrupts - 358 cf/return-from-interrupt - 359 - 360 ## the rest of this file is all data - 361 - 362 == data 0x8000 - 363 idt_descriptor: - 364 ff 03 # final index of idt = size of idt - 1 - 365 idt_start/imm32/start - 366 - 367 Keyboard-shift-pressed?: # boolean - 368 0/imm32 - 369 - 370 Keyboard-ctrl-pressed?: # boolean - 371 0/imm32 - 372 - 373 # var keyboard circular buffer - 374 Keyboard-buffer:write: # nibble - 375 0/imm32 - 376 Keyboard-buffer:read: # nibble - 377 0/imm32 - 378 Keyboard-buffer:data: # byte[16] - 379 00 00 00 00 - 380 00 00 00 00 - 381 00 00 00 00 - 382 00 00 00 00 - 383 - 384 == data 0x8100 - 385 Video-mode-info: - 386 +-- 53 lines: # video mode info --------------------------------------------------------------------------------------------------------------------------------------------------------- - 439 - 440 == data 0x8200 - 441 +--161 lines: # interrupt descriptor table ---------------------------------------------------------------------------------------------------------------------------------------------- - 602 - 603 == data 0x8600 - 604 +-- 72 lines: # translating keys to ASCII ----------------------------------------------------------------------------------------------------------------------------------------------- - 676 - 677 == data 0x8c00 - 678 Font: - 679 +--236 lines: # Bitmaps for some ASCII characters (soon Unicode) ------------------------------------------------------------------------------------------------------------------------ - 915 - 916 # offset 1800 (address 0x9400) - 917 == code 0x9400 - 918 - 919 # Use 28-bit PIO mode to read the first sector (512 bytes) from an IDE (ATA) - 920 # disk drive. - 921 # Inspired by https://colorforth.github.io/ide.html - 922 # - 923 # Resources: - 924 # https://wiki.osdev.org/ATA_PIO_Mode - 925 # https://forum.osdev.org/viewtopic.php?f=1&p=167798 - 926 # read-sector, according to https://www.scs.stanford.edu/11wi-cs140/pintos/specs/ata-3-std.pdf - 927 load-first-sector-from-primary-bus-secondary-drive: # out: (addr stream byte) - 928 # . prologue - 929 55/push-ebp - 930 89/<- %ebp 4/r32/esp - 931 # . save registers - 932 50/push-eax - 933 51/push-ecx - 934 52/push-edx - 935 # check for drive - 936 (secondary-drive-exists?) # => eax - 937 3d/compare-eax-and 0/imm32/false - 938 0f 84/jump-if-= $load-first-sector-from-primary-bus-secondary-drive:end/disp32 - 939 # kick off read - 940 (ata-drive-select 0xf0) # primary bus, secondary drive; 4 LSBs contain 4 upper bits of LBA (here 0) - 941 (clear-ata-error) - 942 (ata-sector-count 1) - 943 (ata-lba 0 0 0) # lower 24 bits of LBA, all 0 - 944 (ata-command 0x20) # read sectors with retries - 945 # poll for results - 946 (while-ata-busy) - 947 (until-ata-data-available) - 948 # emit results - 949 31/xor %eax 0/r32/eax - 950 ba/copy-to-edx 0x1f0/imm32 - 951 b9/copy-to-ecx 0x200/imm32 # 512 bytes per sector - 952 { - 953 81 7/subop/compare %ecx 0/imm32 - 954 74/jump-if-= break/disp8 - 955 66 ed/read-port-dx-into-ax - 956 # write 2 bytes to stream one at a time - 957 (append-byte *(ebp+8) %eax) - 958 49/decrement-ecx - 959 c1/shift 5/subop/right-padding-zeroes %eax 8/imm8 - 960 (append-byte *(ebp+8) %eax) - 961 49/decrement-ecx - 962 eb/jump loop/disp8 - 963 } - 964 $load-first-sector-from-primary-bus-secondary-drive:end: - 965 # . restore registers - 966 5a/pop-to-edx - 967 59/pop-to-ecx - 968 58/pop-to-eax - 969 # . epilogue - 970 89/<- %esp 5/r32/ebp - 971 5d/pop-to-ebp - 972 c3/return - 973 - 974 store-first-sector-to-primary-bus-secondary-drive: # in: (addr stream byte) - 975 # . prologue - 976 55/push-ebp - 977 89/<- %ebp 4/r32/esp - 978 # . save registers - 979 50/push-eax - 980 51/push-ecx - 981 52/push-edx - 982 53/push-ebx - 983 # check for drive - 984 (secondary-drive-exists?) # => eax - 985 3d/compare-eax-and 0/imm32/false - 986 0f 84/jump-if-= $store-first-sector-to-primary-bus-secondary-drive:end/disp32 - 987 # kick off write - 988 (ata-drive-select 0xf0) # primary bus, secondary drive; 4 LSBs contain 4 upper bits of LBA (here 0) - 989 (clear-ata-error) - 990 (ata-sector-count 1) - 991 (ata-lba 0 0 0) # lower 24 bits of LBA, all 0 - 992 (ata-command 0x30) # write sectors with retries - 993 # wait - 994 (while-ata-busy) - 995 (until-ata-ready-for-data) - 996 # send data - 997 ba/copy-to-edx 0x1f0/imm32 - 998 b9/copy-to-ecx 0x200/imm32 # 512 bytes per sector - 999 # var first-byte/ebx: byte -1000 # when it's more than 0xff, we're at an even-numbered byte -1001 bb/copy-to-ebx 0xffff/imm32 -1002 $store-first-sector-to-primary-bus-secondary-drive:loop: -1003 { -1004 81 7/subop/compare %ecx 0/imm32 -1005 74/jump-if-= break/disp8 -1006 # this loop is slow, but the ATA spec also requires a small delay -1007 (stream-empty? *(ebp+8)) # => eax -1008 3d/compare-eax-and 0/imm32/false -1009 75/jump-if-!= break/disp8 -1010 # read byte from stream -1011 (read-byte *(ebp+8)) # => eax -1012 # if we're at an odd-numbered byte, save it to first-byte -1013 81 7/subop/compare %ebx 0xff/imm32 -1014 { -1015 7e/jump-if-<= break/disp8 -1016 89/<- %ebx 0/r32/eax -1017 eb/jump $store-first-sector-to-primary-bus-secondary-drive:loop/disp8 -1018 } -1019 # otherwise OR it with first-byte and write it out -1020 c1/shift 4/subop/left %eax 8/imm8 -1021 09/or %eax 3/r32/ebx -1022 66 ef/write-ax-into-port-dx -1023 49/decrement-ecx -1024 49/decrement-ecx -1025 # reset first-byte -1026 bb/copy-to-ebx 0xffff/imm32 -1027 eb/jump loop/disp8 -1028 } -1029 # write out first-byte if necessary -1030 81 7/subop/compare %ebx 0xff/imm32 -1031 { -1032 7f/jump-if-> break/disp8 -1033 89/<- %eax 3/r32/ebx -1034 66 ef/write-ax-into-port-dx -1035 49/decrement-ecx -1036 49/decrement-ecx -1037 } -1038 # pad zeroes -1039 31/xor %eax 0/r32/eax -1040 { -1041 81 7/subop/compare %ecx 0/imm32 -1042 74/jump-if-= break/disp8 -1043 66 ef/write-ax-into-port-dx -1044 49/decrement-ecx -1045 49/decrement-ecx -1046 eb/jump loop/disp8 -1047 } -1048 $store-first-sector-to-primary-bus-secondary-drive:end: -1049 # . restore registers -1050 5b/pop-to-ebx -1051 5a/pop-to-edx -1052 59/pop-to-ecx -1053 58/pop-to-eax -1054 # . epilogue -1055 89/<- %esp 5/r32/ebp -1056 5d/pop-to-ebp -1057 c3/return -1058 -1059 secondary-drive-exists?: # -> _/eax: boolean -1060 # . prologue -1061 55/push-ebp -1062 89/<- %ebp 4/r32/esp -1063 # . save registers -1064 52/push-edx -1065 # check for floating bus -1066 { -1067 31/xor %eax 0/r32/eax -1068 ba/copy-to-edx 0x1f7/imm32 -1069 ec/read-port-dx-into-al -1070 3d/compare-eax-and 0xff/imm32 -1071 # if eax is 0xff, primary bus has no drives -1072 b8/copy-to-eax 0/imm32/false -1073 74/jump-if-= $secondary-drive-exists?:end/disp8 -1074 } -1075 # identify -1076 (ata-drive-select 0xb0) # primary bus, secondary drive -1077 (ata-sector-count 0) -1078 (ata-lba 0 0 0) -1079 (ata-command 0xec) # identify -1080 # read status register -1081 # TODO: might need to spin here for 400ns: https://wiki.osdev.org/index.php?title=ATA_PIO_Mode&oldid=25664#400ns_delays -1082 31/xor %eax 0/r32/eax -1083 ba/copy-to-edx 0x1f7/imm32 -1084 ec/read-port-dx-into-al -1085 # if eax is 0, secondary drive does not exist -1086 3d/compare-eax-and 0/imm32 -1087 { -1088 74/jump-if-= break/disp8 -1089 b8/copy-to-eax 1/imm32/true -1090 eb/jump $secondary-drive-exists?:complete-identify/disp8 -1091 } -1092 # TODO: might need to perform remaining steps at https://wiki.osdev.org/index.php?title=ATA_PIO_Mode&oldid=25664#IDENTIFY_command -1093 b8/copy-to-eax 0/imm32/false -1094 $secondary-drive-exists?:complete-identify: -1095 50/push-eax -1096 # clear FIFO from the drive -1097 ba/copy-to-edx 0x1f0/imm32 -1098 b9/copy-to-ecx 0x200/imm32 -1099 { -1100 81 7/subop/compare %ecx 0/imm32 -1101 74/jump-if-= break/disp8 -1102 # read 4 bytes -1103 ed/read-port-dx-into-eax -1104 49/decrement-ecx -1105 49/decrement-ecx -1106 49/decrement-ecx -1107 49/decrement-ecx -1108 eb/jump loop/disp8 -1109 } -1110 58/pop-to-eax -1111 $secondary-drive-exists?:end: -1112 # . restore registers -1113 5a/pop-to-edx -1114 # . epilogue -1115 89/<- %esp 5/r32/ebp -1116 5d/pop-to-ebp -1117 c3/return -1118 -1119 ata-drive-select: # n: byte -1120 # . prologue -1121 55/push-ebp -1122 89/<- %ebp 4/r32/esp -1123 # . save registers -1124 50/push-eax -1125 52/push-edx -1126 # -1127 8b/-> *(ebp+8) 0/r32/eax -1128 ba/copy-to-edx 0x1f6/imm32 -1129 ee/write-al-into-port-dx -1130 $ata-drive-select:end: -1131 # . restore registers -1132 5a/pop-to-edx -1133 58/pop-to-eax -1134 # . epilogue -1135 89/<- %esp 5/r32/ebp -1136 5d/pop-to-ebp -1137 c3/return -1138 -1139 clear-ata-error: -1140 # . prologue -1141 55/push-ebp -1142 89/<- %ebp 4/r32/esp -1143 # . save registers -1144 50/push-eax -1145 52/push-edx -1146 # -1147 b8/copy-to-eax 0/imm32 -1148 ba/copy-to-edx 0x1f1/imm32 -1149 ee/write-al-into-port-dx -1150 $ata-error:end: -1151 # . restore registers -1152 5a/pop-to-edx -1153 58/pop-to-eax -1154 # . epilogue -1155 89/<- %esp 5/r32/ebp -1156 5d/pop-to-ebp -1157 c3/return -1158 -1159 ata-sector-count: # n: byte -1160 # . prologue -1161 55/push-ebp -1162 89/<- %ebp 4/r32/esp -1163 # . save registers -1164 50/push-eax -1165 52/push-edx -1166 # -1167 8b/-> *(ebp+8) 0/r32/eax -1168 ba/copy-to-edx 0x1f2/imm32 -1169 ee/write-al-into-port-dx -1170 $ata-sector-count:end: -1171 # . restore registers -1172 5a/pop-to-edx -1173 58/pop-to-eax -1174 # . epilogue -1175 89/<- %esp 5/r32/ebp -1176 5d/pop-to-ebp -1177 c3/return -1178 -1179 ata-lba: # lo: byte, mid: byte, hi: byte -1180 # . prologue -1181 55/push-ebp -1182 89/<- %ebp 4/r32/esp -1183 # . save registers -1184 50/push-eax -1185 52/push-edx -1186 # lo -1187 8b/-> *(ebp+8) 0/r32/eax -1188 ba/copy-to-edx 0x1f3/imm32 -1189 ee/write-al-into-port-dx -1190 # mid -1191 8b/-> *(ebp+0xc) 0/r32/eax -1192 ba/copy-to-edx 0x1f4/imm32 -1193 ee/write-al-into-port-dx -1194 # hi -1195 8b/-> *(ebp+0x10) 0/r32/eax -1196 ba/copy-to-edx 0x1f5/imm32 -1197 ee/write-al-into-port-dx -1198 $ata-lba:end: -1199 # . restore registers -1200 5a/pop-to-edx -1201 58/pop-to-eax -1202 # . epilogue -1203 89/<- %esp 5/r32/ebp -1204 5d/pop-to-ebp -1205 c3/return -1206 -1207 # sector: [1, 63] -1208 # cylinder: [0, 1023] -1209 ata-cyl-sector: # sector: byte, cyl-lo: byte, cyl-hi: byte -1210 # . prologue -1211 55/push-ebp -1212 89/<- %ebp 4/r32/esp -1213 # . save registers -1214 50/push-eax -1215 52/push-edx -1216 # sector -1217 8b/-> *(ebp+8) 0/r32/eax -1218 ba/copy-to-edx 0x1f3/imm32 -1219 ee/write-al-into-port-dx -1220 # cyl-lo -1221 8b/-> *(ebp+0xc) 0/r32/eax -1222 ba/copy-to-edx 0x1f4/imm32 -1223 ee/write-al-into-port-dx -1224 # cyl-hi -1225 8b/-> *(ebp+0x10) 0/r32/eax -1226 ba/copy-to-edx 0x1f5/imm32 -1227 ee/write-al-into-port-dx -1228 $ata-lba:end: -1229 # . restore registers -1230 5a/pop-to-edx -1231 58/pop-to-eax -1232 # . epilogue -1233 89/<- %esp 5/r32/ebp -1234 5d/pop-to-ebp -1235 c3/return -1236 -1237 ata-command: # cmd: byte -1238 # . prologue -1239 55/push-ebp -1240 89/<- %ebp 4/r32/esp -1241 # . save registers -1242 50/push-eax -1243 52/push-edx -1244 # -1245 8b/-> *(ebp+8) 0/r32/eax -1246 ba/copy-to-edx 0x1f7/imm32 -1247 ee/write-al-into-port-dx -1248 $ata-command:end: -1249 # . restore registers -1250 5a/pop-to-edx -1251 58/pop-to-eax -1252 # . epilogue -1253 89/<- %esp 5/r32/ebp -1254 5d/pop-to-ebp -1255 c3/return -1256 -1257 while-ata-busy: -1258 # . save registers -1259 50/push-eax -1260 52/push-edx -1261 # -1262 ba/copy-to-edx 0x1f7/imm32 -1263 { -1264 ec/read-port-dx-into-al -1265 a8/test-bits-in-al 0x80/imm8/bsy # set zf if bit 7 (most significant) is not set -1266 75/jump-if-zf-not-set-and-bit-7-set loop/disp8 -1267 } -1268 $while-ata-busy:end: -1269 # . restore registers -1270 5a/pop-to-edx -1271 58/pop-to-eax -1272 # . epilogue -1273 c3/return -1274 -1275 until-ata-data-available: -1276 # . save registers -1277 50/push-eax -1278 52/push-edx -1279 # -1280 ba/copy-to-edx 0x1f7/imm32 -1281 { -1282 ec/read-port-dx-into-al -1283 a8/test-bits-in-al 8/imm8/drq # set zf if bit 3 is not set -1284 74/jump-if-zf-set-and-bit-3-not-set loop/disp8 -1285 } -1286 $while-ata-busy:end: -1287 # . restore registers -1288 5a/pop-to-edx -1289 58/pop-to-eax -1290 # . epilogue -1291 c3/return -1292 -1293 until-ata-ready-for-data: -1294 (until-ata-data-available) -1295 c3/return -1296 -1297 # vim:ft=subx + 231 e9/jump Entry/disp32 + 232 + 233 == boot-sector-marker 0x7dfe + 234 # final 2 bytes of boot sector + 235 55 aa + 236 + 237 ## sector 2 onwards loaded by load_disk, not automatically on boot + 238 + 239 == data 0x7e00 + 240 idt_descriptor: + 241 ff 03 # final index of idt = size of idt - 1 + 242 idt_start/imm32/start + 243 + 244 +-- 55 lines: # interrupt descriptor table ---------------------------------------------------------------------------------------------------------------------------------------------- + 299 + 300 == code + 301 + 302 null-interrupt-handler: + 303 cf/return-from-interrupt + 304 + 305 keyboard-interrupt-handler: + 306 # prologue + 307 fa/disable-interrupts + 308 60/push-all-registers + 309 # acknowledge interrupt + 310 b0/copy-to-al 0x20/imm8 + 311 e6/write-al-into-port 0x20/imm8 + 312 31/xor %eax 0/r32/eax + 313 # check output buffer of 8042 keyboard controller (https://web.archive.org/web/20040604041507/http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/keyboard/atkeyboard.html) + 314 e4/read-port-into-al 0x64/imm8 + 315 a8/test-bits-in-al 0x01/imm8 # set zf if bit 0 (least significant) is not set + 316 74/jump-if-not-set $keyboard-interrupt-handler:epilogue/disp8 + 317 # - if keyboard buffer is full, return + 318 # var dest-addr/ecx: (addr byte) = (keyboard-buffer + *keyboard-buffer:write) + 319 31/xor %ecx 1/r32/ecx + 320 8a/byte-> *Keyboard-buffer:write 1/r32/cl + 321 81 0/subop/add %ecx Keyboard-buffer:data/imm32 + 322 # al = *dest-addr + 323 8a/byte-> *ecx 0/r32/al + 324 # if (al != 0) return + 325 3c/compare-al-and 0/imm8 + 326 75/jump-if-!= $keyboard-interrupt-handler:epilogue/disp8 + 327 # - read keycode + 328 e4/read-port-into-al 0x60/imm8 + 329 # - key released + 330 # if (al == 0xaa) shift = false # left shift is being lifted + 331 { + 332 3c/compare-al-and 0xaa/imm8 + 333 75/jump-if-!= break/disp8 + 334 # *shift = 0 + 335 c7 0/subop/copy *Keyboard-shift-pressed? 0/imm32 + 336 } + 337 # if (al == 0xb6) shift = false # right shift is being lifted + 338 { + 339 3c/compare-al-and 0xb6/imm8 + 340 75/jump-if-!= break/disp8 + 341 # *shift = 0 + 342 c7 0/subop/copy *Keyboard-shift-pressed? 0/imm32 + 343 } + 344 # if (al == 0x9d) ctrl = false # ctrl is being lifted + 345 { + 346 3c/compare-al-and 0x9d/imm8 + 347 75/jump-if-!= break/disp8 + 348 # *ctrl = 0 + 349 c7 0/subop/copy *Keyboard-ctrl-pressed? 0/imm32 + 350 } + 351 # if (al & 0x80) a key is being lifted; return + 352 50/push-eax + 353 24/and-al-with 0x80/imm8 + 354 3c/compare-al-and 0/imm8 + 355 58/pop-to-eax + 356 75/jump-if-!= $keyboard-interrupt-handler:epilogue/disp8 + 357 # - key pressed + 358 # if (al == 0x2a) shift = true, return # left shift pressed + 359 { + 360 3c/compare-al-and 0x2a/imm8 + 361 75/jump-if-!= break/disp8 + 362 # *shift = 1 + 363 c7 0/subop/copy *Keyboard-shift-pressed? 1/imm32 + 364 # return + 365 eb/jump $keyboard-interrupt-handler:epilogue/disp8 + 366 } + 367 # if (al == 0x36) shift = true, return # right shift pressed + 368 { + 369 3c/compare-al-and 0x36/imm8 + 370 75/jump-if-!= break/disp8 + 371 # *shift = 1 + 372 c7 0/subop/copy *Keyboard-shift-pressed? 1/imm32 + 373 # return + 374 eb/jump $keyboard-interrupt-handler:epilogue/disp8 + 375 } + 376 # if (al == 0x1d) ctrl = true, return + 377 { + 378 3c/compare-al-and 0x1d/imm8 + 379 75/jump-if-!= break/disp8 + 380 # *ctrl = 1 + 381 c7 0/subop/copy *Keyboard-ctrl-pressed? 1/imm32 + 382 # return + 383 eb/jump $keyboard-interrupt-handler:epilogue/disp8 + 384 } + 385 # - convert key to character + 386 # if (shift) use keyboard shift map + 387 { + 388 81 7/subop/compare *Keyboard-shift-pressed? 0/imm32 + 389 74/jump-if-= break/disp8 + 390 # sigils don't currently support labels inside *(eax+label) + 391 05/add-to-eax Keyboard-shift-map/imm32 + 392 8a/byte-> *eax 0/r32/al + 393 eb/jump $keyboard-interrupt-handler:select-map-done/disp8 + 394 } + 395 # if (ctrl) al = *(ctrl map + al) + 396 { + 397 81 7/subop/compare *Keyboard-ctrl-pressed? 0/imm32 + 398 74/jump-if-= break/disp8 + 399 05/add-to-eax Keyboard-ctrl-map/imm32 + 400 8a/byte-> *eax 0/r32/al + 401 eb/jump $keyboard-interrupt-handler:select-map-done/disp8 + 402 } + 403 # otherwise al = *(normal map + al) + 404 05/add-to-eax Keyboard-normal-map/imm32 + 405 8a/byte-> *eax 0/r32/al + 406 $keyboard-interrupt-handler:select-map-done: + 407 # - if there's no character mapping, return + 408 { + 409 3c/compare-al-and 0/imm8 + 410 74/jump-if-= break/disp8 + 411 # - store al in keyboard buffer + 412 88/<- *ecx 0/r32/al + 413 # increment index + 414 fe/increment-byte *Keyboard-buffer:write + 415 # clear top nibble of index (keyboard buffer is circular) + 416 80 4/subop/and-byte *Keyboard-buffer:write 0x0f/imm8 + 417 } + 418 $keyboard-interrupt-handler:epilogue: + 419 # epilogue + 420 61/pop-all-registers + 421 fb/enable-interrupts + 422 cf/return-from-interrupt + 423 + 424 == data + 425 Keyboard-shift-pressed?: # boolean + 426 0/imm32 + 427 + 428 Keyboard-ctrl-pressed?: # boolean + 429 0/imm32 + 430 + 431 # var keyboard circular buffer + 432 Keyboard-buffer:write: # nibble + 433 0/imm32 + 434 Keyboard-buffer:read: # nibble + 435 0/imm32 + 436 Keyboard-buffer:data: # byte[16] + 437 00 00 00 00 + 438 00 00 00 00 + 439 00 00 00 00 + 440 00 00 00 00 + 441 + 442 +-- 70 lines: # Keyboard maps for translating keys to ASCII ----------------------------------------------------------------------------------------------------------------------------- + 512 + 513 Video-mode-info: + 514 +-- 53 lines: # video mode info --------------------------------------------------------------------------------------------------------------------------------------------------------- + 567 + 568 Font: + 569 +--236 lines: # Bitmaps for some ASCII characters (soon Unicode) ------------------------------------------------------------------------------------------------------------------------ + 805 + 806 == code + 807 + 808 ## Controlling IDE (ATA) hard disks + 809 # Uses 28-bit PIO mode. + 810 # Inspired by https://colorforth.github.io/ide.html + 811 # + 812 # Resources: + 813 # https://wiki.osdev.org/ATA_PIO_Mode + 814 # https://forum.osdev.org/viewtopic.php?f=1&p=167798 + 815 # read-sector, according to https://www.scs.stanford.edu/11wi-cs140/pintos/specs/ata-3-std.pdf + 816 + 817 load-first-sector-from-primary-bus-secondary-drive: # out: (addr stream byte) + 818 # . prologue + 819 55/push-ebp + 820 89/<- %ebp 4/r32/esp + 821 # . save registers + 822 50/push-eax + 823 51/push-ecx + 824 52/push-edx + 825 # check for drive + 826 (secondary-drive-exists?) # => eax + 827 3d/compare-eax-and 0/imm32/false + 828 0f 84/jump-if-= $load-first-sector-from-primary-bus-secondary-drive:end/disp32 + 829 # kick off read + 830 (ata-drive-select 0xf0) # primary bus, secondary drive; 4 LSBs contain 4 upper bits of LBA (here 0) + 831 (clear-ata-error) + 832 (ata-sector-count 1) + 833 (ata-lba 0 0 0) # lower 24 bits of LBA, all 0 + 834 (ata-command 0x20) # read sectors with retries + 835 # poll for results + 836 (while-ata-busy) + 837 (until-ata-data-available) + 838 # emit results + 839 31/xor %eax 0/r32/eax + 840 ba/copy-to-edx 0x1f0/imm32 + 841 b9/copy-to-ecx 0x200/imm32 # 512 bytes per sector + 842 { + 843 81 7/subop/compare %ecx 0/imm32 + 844 74/jump-if-= break/disp8 + 845 66 ed/read-port-dx-into-ax + 846 # write 2 bytes to stream one at a time + 847 (append-byte *(ebp+8) %eax) + 848 49/decrement-ecx + 849 c1/shift 5/subop/right-padding-zeroes %eax 8/imm8 + 850 (append-byte *(ebp+8) %eax) + 851 49/decrement-ecx + 852 eb/jump loop/disp8 + 853 } + 854 $load-first-sector-from-primary-bus-secondary-drive:end: + 855 # . restore registers + 856 5a/pop-to-edx + 857 59/pop-to-ecx + 858 58/pop-to-eax + 859 # . epilogue + 860 89/<- %esp 5/r32/ebp + 861 5d/pop-to-ebp + 862 c3/return + 863 + 864 store-first-sector-to-primary-bus-secondary-drive: # in: (addr stream byte) + 865 # . prologue + 866 55/push-ebp + 867 89/<- %ebp 4/r32/esp + 868 # . save registers + 869 50/push-eax + 870 51/push-ecx + 871 52/push-edx + 872 53/push-ebx + 873 # check for drive + 874 (secondary-drive-exists?) # => eax + 875 3d/compare-eax-and 0/imm32/false + 876 0f 84/jump-if-= $store-first-sector-to-primary-bus-secondary-drive:end/disp32 + 877 # kick off write + 878 (ata-drive-select 0xf0) # primary bus, secondary drive; 4 LSBs contain 4 upper bits of LBA (here 0) + 879 (clear-ata-error) + 880 (ata-sector-count 1) + 881 (ata-lba 0 0 0) # lower 24 bits of LBA, all 0 + 882 (ata-command 0x30) # write sectors with retries + 883 # wait + 884 (while-ata-busy) + 885 (until-ata-ready-for-data) + 886 # send data + 887 ba/copy-to-edx 0x1f0/imm32 + 888 b9/copy-to-ecx 0x200/imm32 # 512 bytes per sector + 889 # var first-byte/ebx: byte + 890 # when it's more than 0xff, we're at an even-numbered byte + 891 bb/copy-to-ebx 0xffff/imm32 + 892 $store-first-sector-to-primary-bus-secondary-drive:loop: + 893 { + 894 81 7/subop/compare %ecx 0/imm32 + 895 74/jump-if-= break/disp8 + 896 # this loop is slow, but the ATA spec also requires a small delay + 897 (stream-empty? *(ebp+8)) # => eax + 898 3d/compare-eax-and 0/imm32/false + 899 75/jump-if-!= break/disp8 + 900 # read byte from stream + 901 (read-byte *(ebp+8)) # => eax + 902 # if we're at an odd-numbered byte, save it to first-byte + 903 81 7/subop/compare %ebx 0xff/imm32 + 904 { + 905 7e/jump-if-<= break/disp8 + 906 89/<- %ebx 0/r32/eax + 907 eb/jump $store-first-sector-to-primary-bus-secondary-drive:loop/disp8 + 908 } + 909 # otherwise OR it with first-byte and write it out + 910 c1/shift 4/subop/left %eax 8/imm8 + 911 09/or %eax 3/r32/ebx + 912 66 ef/write-ax-into-port-dx + 913 49/decrement-ecx + 914 49/decrement-ecx + 915 # reset first-byte + 916 bb/copy-to-ebx 0xffff/imm32 + 917 eb/jump loop/disp8 + 918 } + 919 # write out first-byte if necessary + 920 81 7/subop/compare %ebx 0xff/imm32 + 921 { + 922 7f/jump-if-> break/disp8 + 923 89/<- %eax 3/r32/ebx + 924 66 ef/write-ax-into-port-dx + 925 49/decrement-ecx + 926 49/decrement-ecx + 927 } + 928 # pad zeroes + 929 31/xor %eax 0/r32/eax + 930 { + 931 81 7/subop/compare %ecx 0/imm32 + 932 74/jump-if-= break/disp8 + 933 66 ef/write-ax-into-port-dx + 934 49/decrement-ecx + 935 49/decrement-ecx + 936 eb/jump loop/disp8 + 937 } + 938 $store-first-sector-to-primary-bus-secondary-drive:end: + 939 # . restore registers + 940 5b/pop-to-ebx + 941 5a/pop-to-edx + 942 59/pop-to-ecx + 943 58/pop-to-eax + 944 # . epilogue + 945 89/<- %esp 5/r32/ebp + 946 5d/pop-to-ebp + 947 c3/return + 948 + 949 +--241 lines: # disk helpers ------------------------------------------------------------------------------------------------------------------------------------------------------------ +1190 +1191 ## Controlling a PS/2 mouse +1192 # Uses no IRQs, just polling. +1193 # Thanks Dave Long: https://github.com/jtauber/cleese/blob/master/necco/kernel/bochs/py8042.py +1194 # +1195 # Resources: +1196 # https://wiki.osdev.org/Mouse_Input +1197 +1198 # results x/eax, y/ecx range from -256 to +255 +1199 # See https://wiki.osdev.org/index.php?title=Mouse_Input&oldid=25663#Format_of_First_3_Packet_Bytes +1200 read-mouse-event: # -> _/eax: int, _/ecx: int +1201 # . prologue +1202 55/push-ebp +1203 89/<- %ebp 4/r32/esp +1204 # . save registers +1205 52/push-edx +1206 53/push-ebx +1207 # if no event, return 0, 0 +1208 b8/copy-to-eax 0/imm32 +1209 b9/copy-to-ecx 0/imm32 +1210 (any-mouse-event?) # => eax +1211 3d/compare-eax-and 0/imm32/false +1212 74/jump-if-= $read-mouse-event:end/disp8 +1213 # var f1/edx: byte = inb(0x60) +1214 31/xor %eax 0/r32/eax +1215 e4/read-port-into-al 0x60/imm8 +1216 89/<- %edx 0/r32/eax +1217 (wait-for-mouse-event) +1218 # var dx/ebx: byte = inb(0x60) +1219 31/xor %eax 0/r32/eax +1220 e4/read-port-into-al 0x60/imm8 +1221 89/<- %ebx 0/r32/eax +1222 (wait-for-mouse-event) +1223 # var dy/ecx: byte = inb(0x60) +1224 31/xor %eax 0/r32/eax +1225 e4/read-port-into-al 0x60/imm8 +1226 89/<- %ecx 0/r32/eax +1227 # eax = dx +1228 89/<- %eax 3/r32/ebx +1229 # if (f1 & 0x10) dx = -dx +1230 { +1231 f6 0/subop/test-bits %dl 0x10/imm8 +1232 74/jump-if-zero break/disp8 +1233 0d/or-eax-with 0xffffff00/imm32 +1234 } +1235 # if (f1 & 0x20) dy = -dy +1236 { +1237 f6 0/subop/test-bits %dl 0x20/imm8 +1238 74/jump-if-zero break/disp8 +1239 81 1/subop/or %ecx 0xffffff00/imm32 +1240 } +1241 $read-mouse-event:end: +1242 # . restore registers +1243 5b/pop-to-ebx +1244 5a/pop-to-edx +1245 # . epilogue +1246 89/<- %esp 5/r32/ebp +1247 5d/pop-to-ebp +1248 c3/return +1249 +1250 +--152 lines: # mouse helpers ----------------------------------------------------------------------------------------------------------------------------------------------------------- +1402 +1403 # vim:ft=subx -- cgit 1.4.1-2-gfad0