You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1628 lines
48 KiB
1628 lines
48 KiB
# 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 |
|
|
|
# For now, not bothering reprogramming the IRQ to not conflict with software |
|
# exceptions. |
|
# https://wiki.osdev.org/index.php?title=8259_PIC&oldid=24650#Protected_Mode |
|
# |
|
# Interrupt 1 (keyboard) conflicts with debugger faults. We don't use a |
|
# debugger. |
|
# Reference: |
|
# https://wiki.osdev.org/Exceptions |
|
|
|
# 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 |
|
|
|
(initialize-mouse) |
|
|
|
## enable floating point |
|
db/floating-point-coprocessor e3/initialize |
|
# eax <- cr4 |
|
0f 20/<-cr 3/mod/direct 0/rm32/eax 4/r32/cr4 |
|
# eax <- or bit 9 |
|
0f ba/bit-test 5/subop/bit-test-and-set 3/mod/direct 0/rm32/eax 9/imm8 |
|
# cr4 <- eax |
|
0f 22/->cr 3/mod/direct 0/rm32/eax 4/r32/cr4 |
|
|
|
e9/jump Entry/disp32 |
|
|
|
== 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 |
|
# update *Timer-current-color |
|
ff 0/subop/increment *Timer-counter |
|
$timer-interrupt-handler:epilogue: |
|
# epilogue |
|
9d/pop-flags |
|
61/pop-all-registers |
|
fb/enable-interrupts |
|
cf/return-from-interrupt |
|
|
|
== data |
|
Timer-counter: |
|
0/imm32 |
|
|
|
== code |
|
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:end/disp32 |
|
# - if keyboard buffer is full, return |
|
# var dest-addr/ecx: (addr byte) = (keyboard-buffer + *keyboard-buffer:write) |
|
31/xor %ecx 1/r32/ecx |
|
8a/byte-> *Keyboard-buffer:write 1/r32/cl |
|
81 0/subop/add %ecx Keyboard-buffer:data/imm32 |
|
# al = *dest-addr |
|
8a/byte-> *ecx 0/r32/al |
|
# if (al != 0) return |
|
3c/compare-al-and 0/imm8 |
|
0f 85/jump-if-!= $keyboard-interrupt-handler:end/disp32 |
|
# - read keycode |
|
e4/read-port-into-al 0x60/imm8 |
|
# - key released |
|
# if (al == 0xaa) shift = false # left shift is being lifted |
|
{ |
|
3c/compare-al-and 0xaa/imm8 |
|
75/jump-if-!= break/disp8 |
|
# *shift = 0 |
|
c7 0/subop/copy *Keyboard-shift-pressed? 0/imm32 |
|
} |
|
# if (al == 0xb6) shift = false # right shift is being lifted |
|
{ |
|
3c/compare-al-and 0xb6/imm8 |
|
75/jump-if-!= break/disp8 |
|
# *shift = 0 |
|
c7 0/subop/copy *Keyboard-shift-pressed? 0/imm32 |
|
} |
|
# if (al == 0x9d) ctrl = false # ctrl is being lifted |
|
{ |
|
3c/compare-al-and 0x9d/imm8 |
|
75/jump-if-!= break/disp8 |
|
# *ctrl = 0 |
|
c7 0/subop/copy *Keyboard-ctrl-pressed? 0/imm32 |
|
} |
|
# 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:end/disp8 |
|
# - key pressed |
|
# if (al == 0x2a) shift = true, return # left shift pressed |
|
{ |
|
3c/compare-al-and 0x2a/imm8 |
|
75/jump-if-!= break/disp8 |
|
# *shift = 1 |
|
c7 0/subop/copy *Keyboard-shift-pressed? 1/imm32 |
|
# return |
|
eb/jump $keyboard-interrupt-handler:end/disp8 |
|
} |
|
# if (al == 0x36) shift = true, return # right shift pressed |
|
{ |
|
3c/compare-al-and 0x36/imm8 |
|
75/jump-if-!= break/disp8 |
|
# *shift = 1 |
|
c7 0/subop/copy *Keyboard-shift-pressed? 1/imm32 |
|
# return |
|
eb/jump $keyboard-interrupt-handler:end/disp8 |
|
} |
|
# if (al == 0x1d) ctrl = true, return |
|
{ |
|
3c/compare-al-and 0x1d/imm8 |
|
75/jump-if-!= break/disp8 |
|
# *ctrl = 1 |
|
c7 0/subop/copy *Keyboard-ctrl-pressed? 1/imm32 |
|
# return |
|
eb/jump $keyboard-interrupt-handler:end/disp8 |
|
} |
|
# - convert key to character |
|
# if (shift) use keyboard shift map |
|
{ |
|
81 7/subop/compare *Keyboard-shift-pressed? 0/imm32 |
|
74/jump-if-= break/disp8 |
|
# sigils don't currently support labels inside *(eax+label) |
|
05/add-to-eax Keyboard-shift-map/imm32 |
|
8a/byte-> *eax 0/r32/al |
|
eb/jump $keyboard-interrupt-handler:select-map-done/disp8 |
|
} |
|
# if (ctrl) al = *(ctrl map + al) |
|
{ |
|
81 7/subop/compare *Keyboard-ctrl-pressed? 0/imm32 |
|
74/jump-if-= break/disp8 |
|
05/add-to-eax Keyboard-ctrl-map/imm32 |
|
8a/byte-> *eax 0/r32/al |
|
eb/jump $keyboard-interrupt-handler:select-map-done/disp8 |
|
} |
|
# otherwise al = *(normal map + al) |
|
05/add-to-eax Keyboard-normal-map/imm32 |
|
8a/byte-> *eax 0/r32/al |
|
$keyboard-interrupt-handler:select-map-done: |
|
# - if there's no character mapping, return |
|
{ |
|
3c/compare-al-and 0/imm8 |
|
74/jump-if-= break/disp8 |
|
# - store al in keyboard buffer |
|
88/<- *ecx 0/r32/al |
|
# increment index |
|
fe/increment-byte *Keyboard-buffer:write |
|
# clear top nibble of index (keyboard buffer is circular) |
|
80 4/subop/and-byte *Keyboard-buffer:write 0x0f/imm8 |
|
} |
|
$keyboard-interrupt-handler:end: |
|
# epilogue |
|
9d/pop-flags |
|
61/pop-all-registers |
|
fb/enable-interrupts |
|
cf/return-from-interrupt |
|
|
|
== data |
|
Keyboard-shift-pressed?: # boolean |
|
0/imm32 |
|
|
|
Keyboard-ctrl-pressed?: # boolean |
|
0/imm32 |
|
|
|
# var keyboard circular buffer |
|
Keyboard-buffer:write: # nibble |
|
0/imm32 |
|
Keyboard-buffer:read: # nibble |
|
0/imm32 |
|
Keyboard-buffer:data: # byte[16] |
|
00 00 00 00 |
|
00 00 00 00 |
|
00 00 00 00 |
|
00 00 00 00 |
|
|
|
# Keyboard maps for translating keys to ASCII {{{ |
|
Keyboard-normal-map: |
|
00 |
|
# es |
|
1b |
|
# |<--- digits -------------->| - = backspace |
|
31 32 33 34 35 36 37 38 39 30 2d 3d 08 |
|
# 0f |
|
# tab q w e r t y u i o p [ ] |
|
09 71 77 65 72 74 79 75 69 6f 70 5b 5d |
|
# 1c |
|
# enter (newline) |
|
0a 00 |
|
# 1e |
|
# a s d f g h j k l ; ' ` \ |
|
61 73 64 66 67 68 6a 6b 6c 3b 27 60 00 5c |
|
# ^ left shift |
|
# 2c |
|
# z x c v b n m , . / * |
|
7a 78 63 76 62 6e 6d 2c 2e 2f 00 2a |
|
# ^ right shift |
|
# 38 |
|
# space |
|
00 20 |
|
# 3a |
|
00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 |
|
# 48 |
|
# ↑* ←* →* ↓* |
|
82 00 00 80 00 83 00 00 81 |
|
# 51 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
|
|
# * - Not a valid ASCII/Unicode value. |
|
|
|
Keyboard-shift-map: |
|
00 |
|
# es |
|
1b |
|
# ! @ # $ % ^ & * ( ) _ + backspace |
|
21 40 23 24 25 53 26 2a 28 29 5f 2b 08 |
|
# 0f |
|
# tab Q W E R T Y U I O P { } |
|
09 51 57 45 52 54 59 55 49 5f 50 7b 7d |
|
# 1c |
|
# enter (newline) |
|
0a 00 |
|
# 1e |
|
# A S D F G H J K L : " ~ | |
|
41 53 44 46 47 48 4a 4b 4c 3a 22 7e 00 7c |
|
# 2c |
|
# Z X C V B N M < > ? * |
|
5a 58 43 56 42 4e 4d 3c 3e 3f 00 2a |
|
# 38 |
|
# space |
|
00 20 |
|
# 3a |
|
00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 |
|
# 48 |
|
# ↑* ←* →* ↓* |
|
82 00 00 80 00 83 00 00 81 |
|
# 51 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
|
|
# * - Not a valid ASCII/Unicode value. |
|
|
|
Keyboard-ctrl-map: |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
# 10 |
|
# ^q ^w ^e ^r ^t ^y ^u tb ^o ^p |
|
11 17 05 12 14 19 15 09 1f 10 00 00 |
|
# 1c |
|
# carriage-return |
|
0d 00 |
|
# 1e |
|
# ^a ^s ^d ^f ^g ^h ^j ^k ^l ^\ |
|
01 13 04 06 07 08 0a 0b 0c 00 00 00 00 1c |
|
# 2c |
|
# ^z ^x ^c ^v ^b ^n ^m ^/ |
|
1a 18 03 16 02 0e 0d 00 00 1f 00 00 |
|
# 38 |
|
# space |
|
00 20 |
|
# 3a |
|
00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 |
|
# 48 |
|
# ↑* ←* →* ↓* |
|
82 00 00 80 00 83 00 00 81 |
|
# 51 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
|
|
# * - Not a valid ASCII/Unicode value. |
|
# }}} |
|
|
|
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 |
|
# }}} |
|
|
|
Font: |
|
# Bitmaps for some ASCII characters (soon Unicode) {{{ |
|
# Part of GNU Unifont |
|
# 8px wide, 16px tall |
|
# Based on http://unifoundry.com/pub/unifont/unifont-13.0.05/font-builds/unifont-13.0.05.hex.gz |
|
# See https://en.wikipedia.org/wiki/GNU_Unifont#The_.hex_font_format |
|
# Website: http://unifoundry.com/unifont/index.html |
|
# License: http://unifoundry.com/LICENSE.txt (GPL v2) |
|
# Each line below is a bitmap for a single character. |
|
# Each byte is a bitmap for a single row of 8 pixels. |
|
|
|
# some unprintable ASCII chars |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
# 0x20 = space |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
# ! |
|
00 00 00 00 08 08 08 08 08 08 08 00 08 08 00 00 |
|
# " |
|
00 00 22 22 22 22 00 00 00 00 00 00 00 00 00 00 |
|
# 0x23 = '#' |
|
00 00 00 00 12 12 12 7e 24 24 7e 48 48 48 00 00 |
|
# $ |
|
00 00 00 00 08 3e 49 48 38 0e 09 49 3e 08 00 00 |
|
# % |
|
00 00 00 00 31 4a 4a 34 08 08 16 29 29 46 00 00 |
|
# & |
|
00 00 00 00 1c 22 22 14 18 29 45 42 46 39 00 00 |
|
# ' |
|
00 00 08 08 08 08 00 00 00 00 00 00 00 00 00 00 |
|
# ( |
|
00 00 00 04 08 08 10 10 10 10 10 10 08 08 04 00 |
|
# ) |
|
00 00 00 20 10 10 08 08 08 08 08 08 10 10 20 00 |
|
# * |
|
00 00 00 00 00 00 08 49 2a 1c 2a 49 08 00 00 00 |
|
# + |
|
00 00 00 00 00 00 08 08 08 7f 08 08 08 00 00 00 |
|
# , |
|
00 00 00 00 00 00 00 00 00 00 00 00 18 08 08 10 |
|
# - |
|
00 00 00 00 00 00 00 00 00 3c 00 00 00 00 00 00 |
|
# . |
|
00 00 00 00 00 00 00 00 00 00 00 00 18 18 00 00 |
|
# / |
|
00 00 00 00 02 02 04 08 08 10 10 20 40 40 00 00 |
|
# 0x30 = '0' |
|
00 00 00 00 18 24 42 46 4a 52 62 42 24 18 00 00 |
|
# 1 |
|
00 00 00 00 08 18 28 08 08 08 08 08 08 3e 00 00 |
|
# 2 |
|
00 00 00 00 3c 42 42 02 0c 10 20 40 40 7e 00 00 |
|
# 3 |
|
00 00 00 00 3c 42 42 02 1c 02 02 42 42 3c 00 00 |
|
# 4 |
|
00 00 00 00 04 0c 14 24 44 44 7e 04 04 04 00 00 |
|
# 5 |
|
00 00 00 00 7e 40 40 40 7c 02 02 02 42 3c 00 00 |
|
# 6 |
|
00 00 00 00 1c 20 40 40 7c 42 42 42 42 3c 00 00 |
|
# 7 |
|
00 00 00 00 7e 02 02 04 04 04 08 08 08 08 00 00 |
|
# 8 |
|
00 00 00 00 3c 42 42 42 3c 42 42 42 42 3c 00 00 |
|
# 9 |
|
00 00 00 00 3c 42 42 42 3e 02 02 02 04 38 00 00 |
|
# : |
|
00 00 00 00 00 00 18 18 00 00 00 18 18 00 00 00 |
|
# ; |
|
00 00 00 00 00 00 18 18 00 00 00 18 08 08 10 00 |
|
# < |
|
00 00 00 00 00 02 04 08 10 20 10 08 04 02 00 00 |
|
# = |
|
00 00 00 00 00 00 00 7e 00 00 00 7e 00 00 00 00 |
|
# > |
|
00 00 00 00 00 40 20 10 08 04 08 10 20 40 00 00 |
|
# ? |
|
00 00 00 00 3c 42 42 02 04 08 08 00 08 08 00 00 |
|
# 0x40 = @ |
|
00 00 00 00 1c 22 4a 56 52 52 52 4e 20 1e 00 00 |
|
# A |
|
00 00 00 00 18 24 24 42 42 7e 42 42 42 42 00 00 |
|
# B |
|
00 00 00 00 7c 42 42 42 7c 42 42 42 42 7c 00 00 |
|
# C |
|
00 00 00 00 3c 42 42 40 40 40 40 42 42 3c 00 00 |
|
# D |
|
00 00 00 00 78 44 42 42 42 42 42 42 44 78 00 00 |
|
# E |
|
00 00 00 00 7e 40 40 40 7c 40 40 40 40 7e 00 00 |
|
# F |
|
00 00 00 00 7e 40 40 40 7c 40 40 40 40 40 00 00 |
|
# G |
|
00 00 00 00 3c 42 42 40 40 4e 42 42 46 3a 00 00 |
|
# H |
|
00 00 00 00 42 42 42 42 7e 42 42 42 42 42 00 00 |
|
# I |
|
00 00 00 00 3e 08 08 08 08 08 08 08 08 3e 00 00 |
|
# J |
|
00 00 00 00 1f 04 04 04 04 04 04 44 44 38 00 00 |
|
# K |
|
00 00 00 00 42 44 48 50 60 60 50 48 44 42 00 00 |
|
# L |
|
00 00 00 00 40 40 40 40 40 40 40 40 40 7e 00 00 |
|
# M |
|
00 00 00 00 42 42 66 66 5a 5a 42 42 42 42 00 00 |
|
# N |
|
00 00 00 00 42 62 62 52 52 4a 4a 46 46 42 00 00 |
|
# O |
|
00 00 00 00 3c 42 42 42 42 42 42 42 42 3c 00 00 |
|
# 0x50 = P |
|
00 00 00 00 7c 42 42 42 7c 40 40 40 40 40 00 00 |
|
# Q |
|
00 00 00 00 3c 42 42 42 42 42 42 5a 66 3c 03 00 |
|
# R |
|
00 00 00 00 7c 42 42 42 7c 48 44 44 42 42 00 00 |
|
# S |
|
00 00 00 00 3c 42 42 40 30 0c 02 42 42 3c 00 00 |
|
# T |
|
00 00 00 00 7f 08 08 08 08 08 08 08 08 08 00 00 |
|
# U |
|
00 00 00 00 42 42 42 42 42 42 42 42 42 3c 00 00 |
|
# V |
|
00 00 00 00 41 41 41 22 22 22 14 14 08 08 00 00 |
|
# W |
|
00 00 00 00 42 42 42 42 5a 5a 66 66 42 42 00 00 |
|
# X |
|
00 00 00 00 42 42 24 24 18 18 24 24 42 42 00 00 |
|
# Y |
|
00 00 00 00 41 41 22 22 14 08 08 08 08 08 00 00 |
|
# Z |
|
00 00 00 00 7e 02 02 04 08 10 20 40 40 7e 00 00 |
|
# [ |
|
00 00 00 0e 08 08 08 08 08 08 08 08 08 08 0e 00 |
|
# \ |
|
00 00 00 00 40 40 20 10 10 08 08 04 02 02 00 00 |
|
# ] |
|
00 00 00 70 10 10 10 10 10 10 10 10 10 10 70 00 |
|
# ^ |
|
00 00 18 24 42 00 00 00 00 00 00 00 00 00 00 00 |
|
# _ |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 7f 00 |
|
# 0x60 = backtick |
|
00 20 10 08 00 00 00 00 00 00 00 00 00 00 00 00 |
|
# a |
|
00 00 00 00 00 00 3c 42 02 3e 42 42 46 3a 00 00 |
|
# b |
|
00 00 00 40 40 40 5c 62 42 42 42 42 62 5c 00 00 |
|
# c |
|
00 00 00 00 00 00 3c 42 40 40 40 40 42 3c 00 00 |
|
# d |
|
00 00 00 02 02 02 3a 46 42 42 42 42 46 3a 00 00 |
|
# e |
|
00 00 00 00 00 00 3c 42 42 7e 40 40 42 3c 00 00 |
|
# f |
|
00 00 00 0c 10 10 10 7c 10 10 10 10 10 10 00 00 |
|
# g |
|
00 00 00 00 00 02 3a 44 44 44 38 20 3c 42 42 3c |
|
# h |
|
00 00 00 40 40 40 5c 62 42 42 42 42 42 42 00 00 |
|
# i |
|
00 00 00 08 08 00 18 08 08 08 08 08 08 3e 00 00 |
|
# j |
|
00 00 00 04 04 00 0c 04 04 04 04 04 04 04 48 30 |
|
# k |
|
00 00 00 40 40 40 44 48 50 60 50 48 44 42 00 00 |
|
# l |
|
00 00 00 18 08 08 08 08 08 08 08 08 08 3e 00 00 |
|
# m |
|
00 00 00 00 00 00 76 49 49 49 49 49 49 49 00 00 |
|
# n |
|
00 00 00 00 00 00 5c 62 42 42 42 42 42 42 00 00 |
|
# o |
|
00 00 00 00 00 00 3c 42 42 42 42 42 42 3c 00 00 |
|
# 0x70 = p |
|
00 00 00 00 00 00 5c 62 42 42 42 42 62 5c 40 40 |
|
# q |
|
00 00 00 00 00 00 3a 46 42 42 42 42 46 3a 02 02 |
|
# r |
|
00 00 00 00 00 00 5c 62 42 40 40 40 40 40 00 00 |
|
# s |
|
00 00 00 00 00 00 3c 42 40 30 0c 02 42 3c 00 00 |
|
# t |
|
00 00 00 00 10 10 10 7c 10 10 10 10 10 0c 00 00 |
|
# u |
|
00 00 00 00 00 00 42 42 42 42 42 42 46 3a 00 00 |
|
# v |
|
00 00 00 00 00 00 42 42 42 24 24 24 18 18 00 00 |
|
# w |
|
00 00 00 00 00 00 41 49 49 49 49 49 49 36 00 00 |
|
# x |
|
00 00 00 00 00 00 42 42 24 18 18 24 42 42 00 00 |
|
# y |
|
00 00 00 00 00 00 42 42 42 42 42 26 1a 02 02 3c |
|
# z |
|
00 00 00 00 00 00 7e 02 04 08 10 20 40 7e 00 00 |
|
# { |
|
00 00 00 0c 10 10 08 08 10 20 10 08 08 10 10 0c |
|
# | |
|
00 00 08 08 08 08 08 08 08 08 08 08 08 08 08 08 |
|
# } |
|
00 00 00 30 08 08 10 10 08 04 08 10 10 08 08 30 |
|
# ~ |
|
00 00 00 31 49 46 00 00 00 00 00 00 00 00 00 00 |
|
# 0x7f = del (unused) |
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
|
# }}} |
|
|
|
## Controlling IDE (ATA) hard disks |
|
# Uses 28-bit PIO mode. |
|
# Inspired by https://colorforth.github.io/ide.html |
|
# |
|
# Resources: |
|
# https://wiki.osdev.org/ATA_PIO_Mode |
|
# https://forum.osdev.org/viewtopic.php?f=1&p=167798 |
|
# read-sector, according to https://www.scs.stanford.edu/11wi-cs140/pintos/specs/ata-3-std.pdf |
|
|
|
== data |
|
|
|
# code disk |
|
# All ports are 8-bit except data-port, which is 16-bit. |
|
Primary-bus-primary-drive: |
|
# command-port: int (write) |
|
0x1f7/imm32 |
|
# status-port: int (read) |
|
0x1f7/imm32 |
|
# alternative-status-port: int (read) |
|
0x3f6/imm32 |
|
# error-port: int (read) |
|
0x1f1/imm32 |
|
# drive-and-head-port: int |
|
0x1f6/imm32 |
|
# sector-count-port: int |
|
0x1f2/imm32 |
|
# lba-low-port: int |
|
0x1f3/imm32 |
|
# lba-mid-port: int |
|
0x1f4/imm32 |
|
# lba-high-port: int |
|
0x1f5/imm32 |
|
# data-port: int |
|
0x1f0/imm32 |
|
# drive-code: byte # only drive-specific field |
|
0xe0/imm32 # LBA mode also enabled |
|
|
|
# data disk |
|
# All ports are 8-bit except data-port, which is 16-bit. |
|
Primary-bus-secondary-drive: |
|
# command-port: int (write) |
|
0x1f7/imm32 |
|
# status-port: int (read) |
|
0x1f7/imm32 |
|
# alternative-status-port: int (read) |
|
0x3f6/imm32 |
|
# error-port: int (read) |
|
0x1f1/imm32 |
|
# drive-and-head-port: int |
|
0x1f6/imm32 |
|
# sector-count-port: int |
|
0x1f2/imm32 |
|
# lba-low-port: int |
|
0x1f3/imm32 |
|
# lba-mid-port: int |
|
0x1f4/imm32 |
|
# lba-high-port: int |
|
0x1f5/imm32 |
|
# data-port: int |
|
0x1f0/imm32 |
|
# drive-code: byte # only drive-specific field |
|
0xf0/imm32 # LBA mode also enabled |
|
|
|
== code |
|
|
|
load-sectors: # disk: (addr disk), lba: int, n: int, out: (addr stream byte) |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
51/push-ecx |
|
52/push-edx |
|
# check for drive |
|
(drive-exists? *(ebp+8)) # => eax |
|
3d/compare-eax-and 0/imm32/false |
|
0f 84/jump-if-= $load-sectors:end/disp32 |
|
# kick off read |
|
(ata-drive-select *(ebp+8) *(ebp+0xc)) |
|
(clear-ata-error *(ebp+8)) |
|
(ata-sector-count *(ebp+8) *(ebp+0x10)) |
|
(ata-lba *(ebp+8) *(ebp+0xc)) |
|
(ata-command *(ebp+8) 0x20) # read sectors with retries |
|
# for each sector |
|
{ |
|
# poll for results |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "waiting for sector.." 7 0) |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "." 7 0) |
|
(while-ata-busy *(ebp+8)) |
|
(until-ata-data-available *(ebp+8)) |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "reading\n" 7 0) |
|
# var data-port/edx = disk->data-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+0x24) 2/r32/edx |
|
# emit results |
|
31/xor %eax 0/r32/eax |
|
b9/copy-to-ecx 0x200/imm32 # 512 bytes per sector |
|
{ |
|
81 7/subop/compare %ecx 0/imm32 |
|
74/jump-if-= break/disp8 |
|
66 ed/read-port-dx-into-ax |
|
# write 2 bytes to stream one at a time |
|
(append-byte *(ebp+0x14) %eax) |
|
49/decrement-ecx |
|
c1/shift 5/subop/right-padding-zeroes %eax 8/imm8 |
|
(append-byte *(ebp+0x14) %eax) |
|
49/decrement-ecx |
|
eb/jump loop/disp8 |
|
} |
|
# next sector |
|
ff 1/subop/decrement *(ebp+0x10) |
|
#? (draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+0x10) 0xc 0) |
|
81 7/subop/compare *(ebp+0x10) 0/imm32 |
|
7e/jump-if-<= break/disp8 |
|
(wait-400ns *(ebp+8)) |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "next sector\n" 7 0) |
|
e9/jump loop/disp32 |
|
} |
|
$load-sectors:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
59/pop-to-ecx |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
store-sectors: # disk: (addr disk), lba: int, n: int, in: (addr stream byte) |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
51/push-ecx |
|
52/push-edx |
|
53/push-ebx |
|
# check for drive |
|
(drive-exists? *(ebp+8)) # => eax |
|
3d/compare-eax-and 0/imm32/false |
|
0f 84/jump-if-= $store-sectors:end/disp32 |
|
# kick off write |
|
(ata-drive-select *(ebp+8) *(ebp+0xc)) |
|
(clear-ata-error *(ebp+8)) |
|
(ata-sector-count *(ebp+8) *(ebp+0x10)) |
|
(ata-lba *(ebp+8) *(ebp+0xc)) |
|
(ata-command *(ebp+8) 0x30) # write sectors with retries |
|
# for each sector |
|
#? (set-cursor-position 0 0 0) |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "0" 7 0) |
|
{ |
|
# wait |
|
(while-ata-busy *(ebp+8)) |
|
(until-ata-ready-for-data *(ebp+8)) |
|
# var data-port/edx = disk->data-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+0x24) 2/r32/edx |
|
# send data |
|
b9/copy-to-ecx 0x200/imm32 # 512 bytes per sector |
|
# . var first-byte/ebx: byte |
|
# . when it's more than 0xff, we're at an even-numbered byte |
|
bb/copy-to-ebx 0xffff/imm32 |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "D" 7 0) |
|
$store-sectors:store-sector: |
|
{ |
|
81 7/subop/compare %ecx 0/imm32 |
|
74/jump-if-= break/disp8 |
|
# this loop is slow, but the ATA spec also requires a small delay |
|
(stream-empty? *(ebp+0x14)) # => eax |
|
3d/compare-eax-and 0/imm32/false |
|
75/jump-if-!= break/disp8 |
|
# read byte from stream |
|
(read-byte *(ebp+0x14)) # => eax |
|
# if we're at an odd-numbered byte, save it to first-byte |
|
81 7/subop/compare %ebx 0xff/imm32 |
|
{ |
|
7e/jump-if-<= break/disp8 |
|
89/<- %ebx 0/r32/eax |
|
eb/jump $store-sectors:store-sector/disp8 |
|
} |
|
# otherwise OR it with first-byte and write it out |
|
c1/shift 4/subop/left %eax 8/imm8 |
|
09/or %eax 3/r32/ebx |
|
66 ef/write-ax-into-port-dx |
|
49/decrement-ecx |
|
49/decrement-ecx |
|
# reset first-byte |
|
bb/copy-to-ebx 0xffff/imm32 |
|
eb/jump loop/disp8 |
|
} |
|
# write out final first-byte if necessary |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "I" 7 0) |
|
81 7/subop/compare %ebx 0xff/imm32 |
|
{ |
|
7f/jump-if-> break/disp8 |
|
89/<- %eax 3/r32/ebx |
|
66 ef/write-ax-into-port-dx |
|
49/decrement-ecx |
|
49/decrement-ecx |
|
} |
|
# pad zeroes |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "P" 7 0) |
|
31/xor %eax 0/r32/eax |
|
{ |
|
81 7/subop/compare %ecx 0/imm32 |
|
74/jump-if-= break/disp8 |
|
66 ef/write-ax-into-port-dx |
|
49/decrement-ecx |
|
49/decrement-ecx |
|
eb/jump loop/disp8 |
|
} |
|
# next sector |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "N" 7 0) |
|
ff 1/subop/decrement *(ebp+0x10) |
|
81 7/subop/compare *(ebp+0x10) 0/imm32 |
|
7e/jump-if-<= break/disp8 |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "W" 7 0) |
|
(wait-400ns *(ebp+8)) |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "L" 7 0) |
|
e9/jump loop/disp32 |
|
} |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "F" 7 0) |
|
(flush-ata-cache *(ebp+8)) |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "Y" 7 0) |
|
$store-sectors:end: |
|
# . restore registers |
|
5b/pop-to-ebx |
|
5a/pop-to-edx |
|
59/pop-to-ecx |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
# disk helpers {{{ |
|
|
|
drive-exists?: # disk: (addr disk) -> _/eax: boolean |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
52/push-edx |
|
# check for floating bus |
|
{ |
|
31/xor %eax 0/r32/eax |
|
ba/copy-to-edx 0x1f7/imm32 |
|
ec/read-port-dx-into-al |
|
3d/compare-eax-and 0xff/imm32 |
|
# if eax is 0xff, primary bus has no drives |
|
b8/copy-to-eax 0/imm32/false |
|
0f 84/jump-if-= $drive-exists?:end/disp32 |
|
} |
|
# identify |
|
(ata-drive-select *(ebp+8) 0) |
|
(ata-sector-count *(ebp+8) 0) |
|
(ata-lba *(ebp+8) 0) |
|
(ata-command *(ebp+8) 0xec) # identify |
|
# var status-port/edx = disk->status-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+4) 2/r32/edx # 4 = status-port offset |
|
# read status port |
|
# TODO: might need to spin here for 400ns: https://wiki.osdev.org/index.php?title=ATA_PIO_Mode&oldid=25664#400ns_delays |
|
31/xor %eax 0/r32/eax |
|
ec/read-port-dx-into-al |
|
# if eax is 0, drive does not exist |
|
3d/compare-eax-and 0/imm32 |
|
{ |
|
74/jump-if-= break/disp8 |
|
b8/copy-to-eax 1/imm32/true |
|
eb/jump $drive-exists?:complete-identify/disp8 |
|
} |
|
# TODO: might need to perform remaining steps at https://wiki.osdev.org/index.php?title=ATA_PIO_Mode&oldid=25664#IDENTIFY_command |
|
b8/copy-to-eax 0/imm32/false |
|
$drive-exists?:complete-identify: |
|
50/push-eax |
|
# var data-port/edx = disk->data-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+0x24) 2/r32/edx # 0x24 = data-port offset |
|
# clear FIFO from the drive |
|
b9/copy-to-ecx 0x200/imm32 |
|
{ |
|
81 7/subop/compare %ecx 0/imm32 |
|
74/jump-if-= break/disp8 |
|
# read 4 bytes |
|
ed/read-port-dx-into-eax |
|
49/decrement-ecx |
|
49/decrement-ecx |
|
49/decrement-ecx |
|
49/decrement-ecx |
|
eb/jump loop/disp8 |
|
} |
|
58/pop-to-eax |
|
$drive-exists?:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
ata-drive-select: # disk: (addr disk), lba: int |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
52/push-edx |
|
56/push-esi |
|
# esi = disk |
|
8b/-> *(ebp+8) 6/r32/esi |
|
# var drive-head/edx: byte = lba >> 24 |
|
8b/-> *(ebp+0xc) 2/r32/edx |
|
c1/shift 5/subop/right-padding-zeroes %edx 0x18/imm8 |
|
# var drive-code/eax: byte = disk->drive-code | drive-head |
|
8b/-> *(esi+0x28) 0/r32/eax # 0x28 = drive-code offset |
|
09/or= %eax 2/r32/edx |
|
# var drive-and-head-port/edx: int |
|
8b/-> *(esi+0x10) 2/r32/edx # 0x10 = drive-and-head-port offset |
|
ee/write-al-into-port-dx |
|
$ata-drive-select:end: |
|
# . restore registers |
|
5e/pop-to-esi |
|
5a/pop-to-edx |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
clear-ata-error: # disk: (addr disk) |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
52/push-edx |
|
# var error-port/edx = disk->error-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+0xc) 2/r32/edx # 0xc = error-port offset |
|
# |
|
b8/copy-to-eax 0/imm32 |
|
ee/write-al-into-port-dx |
|
$ata-error:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
ata-sector-count: # disk: (addr disk), n: byte |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
52/push-edx |
|
# var sector-count-port/edx = disk->sector-count-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+0x14) 2/r32/edx # 0x14 = sector-count-port offset |
|
# |
|
8b/-> *(ebp+0xc) 0/r32/eax |
|
ee/write-al-into-port-dx |
|
$ata-sector-count:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
ata-lba: # disk: (addr disk), lba: int |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
52/push-edx |
|
# var port/edx = disk->port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+0x18) 2/r32/edx # 0x18 = lba-low-port offset |
|
# eax = lba |
|
8b/-> *(ebp+0xc) 0/r32/eax |
|
# lo |
|
ee/write-al-into-port-dx |
|
# mid |
|
42/increment-dx # lba-mid-port |
|
c1/shift 5/subop/right-padding-zeroes %eax 8/imm8 |
|
ee/write-al-into-port-dx |
|
# hi |
|
42/increment-dx # lba-high-port |
|
c1/shift 5/subop/right-padding-zeroes %eax 8/imm8 |
|
ee/write-al-into-port-dx |
|
$ata-lba:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
ata-command: # disk: (addr disk), cmd: byte |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
52/push-edx |
|
# var command-port/edx = disk->command-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+0) 2/r32/edx # 0 = command-port offset |
|
# |
|
8b/-> *(ebp+0xc) 0/r32/eax |
|
ee/write-al-into-port-dx |
|
$ata-command:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
while-ata-busy: # disk: (addr disk) |
|
# . save registers |
|
50/push-eax |
|
52/push-edx |
|
# var status-port/edx = disk->status-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+4) 2/r32/edx # 4 = status-port offset |
|
{ |
|
ec/read-port-dx-into-al |
|
a8/test-bits-in-al 0x80/imm8/bsy # set zf if bit 7 (most significant) is not set |
|
75/jump-if-zf-not-set-and-bit-7-set loop/disp8 |
|
} |
|
$while-ata-busy:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
58/pop-to-eax |
|
# . epilogue |
|
c3/return |
|
|
|
until-ata-data-available: # disk: (addr disk) |
|
# . save registers |
|
50/push-eax |
|
52/push-edx |
|
# var status-port/edx = disk->status-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+4) 2/r32/edx # 4 = status-port offset |
|
{ |
|
ec/read-port-dx-into-al |
|
a8/test-bits-in-al 8/imm8/drq # set zf if bit 3 is not set |
|
74/jump-if-zf-set-and-bit-3-not-set loop/disp8 |
|
} |
|
$until-ata-data-available:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
58/pop-to-eax |
|
# . epilogue |
|
c3/return |
|
|
|
until-ata-ready-for-data: |
|
(until-ata-data-available) |
|
c3/return |
|
|
|
# https://wiki.osdev.org/index.php?title=ATA_PIO_Mode&oldid=25664#400ns_delays |
|
wait-400ns: # disk: (addr disk) |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
51/push-ecx |
|
52/push-edx |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "waiting 400ns\n" 7 0) |
|
# var status-port/edx = disk->status-port |
|
8b/-> *(ebp+8) 0/r32/eax |
|
8b/-> *(eax+4) 2/r32/edx # 4 = status-port offset |
|
# |
|
b9/copy-to-ecx 0x10/imm32 |
|
{ |
|
81 7/subop/compare %ecx 0/imm32 |
|
74/jump-if-= break/disp8 |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "." 7 0) |
|
ec/read-port-dx-into-al |
|
49/decrement-ecx |
|
eb/jump loop/disp8 |
|
} |
|
$wait-400ns:end: |
|
# . restore registers |
|
5a/pop-to-edx |
|
59/pop-to-ecx |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
# Flush cache isn't in ATA 3, but it shows up by the ATA 5 spec: |
|
# http://hddguru.com/download/documentation/ATA-ATAPI-standard-5/ATA-ATAPI-5.pdf |
|
flush-ata-cache: # disk: (addr disk) |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# |
|
(ata-drive-select *(ebp+8) 0) |
|
(ata-command *(ebp+8) 0xe7) # flush cache |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "W" 7 0) |
|
(while-ata-busy *(ebp+8)) |
|
#? (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "X" 7 0) |
|
# TODO: seems unneeded? works for a single sector but Qemu hangs with multiple |
|
# sectors. Data is still written. |
|
#? (until-ata-ready-for-data *(ebp+8)) |
|
$flush-ata-cache:end: |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
# }}} |
|
|
|
## Controlling a PS/2 mouse |
|
# Uses no IRQs, just polling. |
|
# Thanks Dave Long: https://github.com/jtauber/cleese/blob/master/necco/kernel/bochs/py8042.py |
|
# |
|
# Resources: |
|
# https://wiki.osdev.org/Mouse_Input |
|
|
|
# results x/eax, y/ecx range from -256 to +255 |
|
# See https://wiki.osdev.org/index.php?title=Mouse_Input&oldid=25663#Format_of_First_3_Packet_Bytes |
|
read-mouse-event: # -> _/eax: int, _/ecx: int |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
52/push-edx |
|
53/push-ebx |
|
# if no event, return 0, 0 |
|
b8/copy-to-eax 0/imm32 |
|
b9/copy-to-ecx 0/imm32 |
|
(any-mouse-event?) # => eax |
|
3d/compare-eax-and 0/imm32/false |
|
74/jump-if-= $read-mouse-event:end/disp8 |
|
# var f1/edx: byte = inb(0x60) |
|
31/xor %eax 0/r32/eax |
|
e4/read-port-into-al 0x60/imm8 |
|
89/<- %edx 0/r32/eax |
|
(wait-for-mouse-event) |
|
# var dx/ebx: byte = inb(0x60) |
|
31/xor %eax 0/r32/eax |
|
e4/read-port-into-al 0x60/imm8 |
|
89/<- %ebx 0/r32/eax |
|
(wait-for-mouse-event) |
|
# var dy/ecx: byte = inb(0x60) |
|
31/xor %eax 0/r32/eax |
|
e4/read-port-into-al 0x60/imm8 |
|
89/<- %ecx 0/r32/eax |
|
# eax = dx |
|
89/<- %eax 3/r32/ebx |
|
# if (f1 & 0x10) dx = -dx |
|
{ |
|
f6 0/subop/test-bits %dl 0x10/imm8 |
|
74/jump-if-zero break/disp8 |
|
0d/or-eax-with 0xffffff00/imm32 |
|
} |
|
# if (f1 & 0x20) dy = -dy |
|
{ |
|
f6 0/subop/test-bits %dl 0x20/imm8 |
|
74/jump-if-zero break/disp8 |
|
81 1/subop/or %ecx 0xffffff00/imm32 |
|
} |
|
$read-mouse-event:end: |
|
# . restore registers |
|
5b/pop-to-ebx |
|
5a/pop-to-edx |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
# mouse helpers {{{ |
|
|
|
wait-for-mouse-event: |
|
# . save registers |
|
50/push-eax |
|
# |
|
{ |
|
(any-mouse-event?) # => eax |
|
3d/compare-eax-and 0/imm32/false |
|
74/jump-if-= loop/disp8 |
|
} |
|
$wait-for-mouse-event:end: |
|
# . restore registers |
|
58/pop-to-eax |
|
# . |
|
c3/return |
|
|
|
any-mouse-event?: # -> _/eax: boolean |
|
31/xor %eax 0/r32/eax |
|
# 0x1 bit: there's data from the keyboard controller |
|
# 0x20 bit: it's data from the aux port (the mouse) |
|
e4/read-port-into-al 0x60/imm8 |
|
24/and-al-with 0x21/imm8 |
|
3c/compare-al-with 0x21/imm8 |
|
0f 94/set-byte-if-= %al |
|
c3/return |
|
|
|
initialize-mouse: |
|
(enable-keyboard-controller-aux-device) |
|
# tell mouse to use default settings |
|
(send-mouse-command 0xf6) |
|
# enable mouse |
|
(send-mouse-command 0xf4) |
|
c3/return |
|
|
|
enable-keyboard-controller-aux-device: |
|
(command-keyboard-controller 0xa8) |
|
c3/return |
|
|
|
send-mouse-command: # command: byte |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# |
|
(command-keyboard-controller 0xd4) |
|
(send-keyboard-controller-data *(ebp+8)) |
|
(wait-for-ack-from-mouse) |
|
$send-mouse-command:end: |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
wait-for-ack-from-mouse: |
|
# . save registers |
|
50/push-eax |
|
{ |
|
(read-keyboard-controller-data) # => eax |
|
3d/compare-eax-with 0xfa/imm32 |
|
75/jump-if-!= loop/disp8 |
|
} |
|
$wait-for-ack-from-mouse:end: |
|
# . restore registers |
|
58/pop-eax |
|
c3/return |
|
|
|
command-keyboard-controller: # command: byte |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
# |
|
(poll-keyboard-controller-to-write) |
|
8b/-> *(ebp+8) 0/r32/eax |
|
e6/write-al-into-port 0x64/imm8 |
|
$command-keyboard-controller:end: |
|
# . restore registers |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
send-keyboard-controller-data: # data: byte |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
# |
|
(poll-keyboard-controller-to-write) |
|
8b/-> *(ebp+8) 0/r32/eax |
|
e6/write-al-into-port 0x60/imm8 |
|
$send-keyboard-controller-data:end: |
|
# . restore registers |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
read-keyboard-controller-data: # -> _/eax: byte |
|
(poll-keyboard-controller-to-read-data-port) |
|
31/xor %eax 0/r32/eax |
|
e4/read-port-into-al 0x60/imm8 |
|
c3/return |
|
|
|
poll-keyboard-controller-to-write: |
|
# . save registers |
|
50/push-eax |
|
# "All output to port 0x60 or 0x64 must be preceded by waiting for bit 1 |
|
# (value=2) of port 0x64 to become clear." |
|
# https://wiki.osdev.org/index.php?title=Mouse_Input&oldid=25663#Waiting_to_Send_Bytes_to_Port_0x60_and_0x64 |
|
{ |
|
e4/read-port-into-al 0x64/imm8 |
|
a8/test-bits-in-al 2/imm8 # set zf if bit 1 (second-least significant) is not set |
|
75/jump-if-zf-not-set-and-bit-1-set loop/disp8 |
|
} |
|
$poll-keyboard-controller-to-write:end: |
|
# . restore registers |
|
58/pop-to-eax |
|
# . epilogue |
|
c3/return |
|
|
|
poll-keyboard-controller-to-read-data-port: |
|
# . prologue |
|
55/push-ebp |
|
89/<- %ebp 4/r32/esp |
|
# . save registers |
|
50/push-eax |
|
# "Bytes cannot be read from port 0x60 until bit 0 (value=1) of port 0x64 is set." |
|
# https://wiki.osdev.org/index.php?title=Mouse_Input&oldid=25663#Waiting_to_Send_Bytes_to_Port_0x60_and_0x64 |
|
{ |
|
e4/read-port-into-al 0x64/imm8 |
|
a8/test-bits-in-al 1/imm8 # set zf if bit 0 (least significant) is not set |
|
74/jump-if-zf-set-and-bit-0-not-set loop/disp8 |
|
} |
|
$poll-keyboard-controller-to-read-data-port:end: |
|
# . restore registers |
|
58/pop-to-eax |
|
# . epilogue |
|
89/<- %esp 5/r32/ebp |
|
5d/pop-to-ebp |
|
c3/return |
|
|
|
# }}} |
|
|
|
# vim:ft=subx
|
|
|