about summary refs log tree commit diff stats
path: root/boot.hex
diff options
context:
space:
mode:
Diffstat (limited to 'boot.hex')
-rw-r--r--boot.hex1181
1 files changed, 1181 insertions, 0 deletions
diff --git a/boot.hex b/boot.hex
new file mode 100644
index 00000000..6fa874c2
--- /dev/null
+++ b/boot.hex
@@ -0,0 +1,1181 @@
+# 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
+#
+# To convert to a disk image, first prepare a realistically sized disk image:
+#   dd if=/dev/zero of=disk.img count=20160  # 512-byte sectors, so 10MB
+# Create initial sectors from this file:
+#   ./bootstrap run apps/hex < baremetal/boot.hex > boot.bin
+# Translate other sectors into a file called a.img
+# Load all sectors into the disk image:
+#   cat boot.bin a.img > disk.bin
+#   dd if=disk.bin of=disk.img conv=notrunc
+# To run:
+#   qemu-system-i386 disk.img
+# Or:
+#   bochs -f baremetal/boot.bochsrc  # boot.bochsrc loads disk.img
+#
+# Since we start out in 16-bit mode, we need instructions SubX doesn't
+# support.
+# This file contains just lowercase hex bytes and comments. Programming it
+# requires liberal use of:
+#   - comments documenting expected offsets
+#   - size checks on the emitted file (currently: 6144 bytes)
+#   - xxd to spot-check contents of specific offsets in the generated output
+#
+# Programs using this initialization:
+#   - can't use any syscalls
+#   - can't print text to video memory (past these boot sectors)
+#   - must only print raw pixels (256 colors) to video memory (resolution 1024x768)
+#   - must start executing immediately after this file (see outline below)
+#
+# Don't panic! This file doesn't contain any loops or function calls. 80% of
+# it is data. One pass through less than 1KB of code (there's lots of
+# padding), and then we jump into a better notation. The rest of the stack
+# (really only in a couple of slightly higher-level places) needs to know just
+# a few magic constants:
+#   Video memory: start is stored at 0x8128
+#   Keyboard buffer: starts at 0x8028
+#
+# No mouse support. _That_ would require panicking.
+
+# Outline of this file with offsets and the addresses they map to at run-time:
+# -- 16-bit mode code
+#   offset    0 (address 7c00): boot code
+# -- 16-bit mode data
+#            e0 (address 7c80) global descriptor table
+#            f8 (address 7ca0) <== gdt_descriptor
+# -- 32-bit mode code
+#   offset  100 (address 7d00): boot code
+#           1fe (address 7dfe) boot sector marker (2 bytes)
+#   offset  200 (address 7e00): interrupt handler code
+# -- 32-bit mode data
+#   offset  400 (address 8000): handler data
+#           410 (address 8010): keyboard handler data
+#           428 (address 8028) <== keyboard buffer
+#   offset  500 (address 8100): video mode data (256 bytes)
+#           528 (address 8128) <== start of video RAM stored here
+#   offset  600 (address 8200): interrupt descriptor table (1KB)
+#   offset  a00 (address 8600): keyboard mappings (1.5KB)
+#   offset 1000 (address 8c00): bitmap font (2KB)
+#   offset 1800 (address 9400): entrypoint for applications (don't forget to adjust survey_baremetal if this changes)
+
+# Other details of the current memory map:
+#   code: 4 tracks of disk to [0x00007c00, 0x00027400)
+#   stack grows down from 0x00070000
+#     see below
+#   heap: [0x01000000, 0x02000000)
+#     see baremetal/120allocate.subx
+# Consult https://wiki.osdev.org/Memory_Map_(x86) before modifying any of this.
+
+## 16-bit entry point
+
+# Upon reset, the IBM PC:
+#   - loads the first sector (512 bytes)
+#     from some bootable image (see the boot sector marker at the end of this file)
+#     to the address range [0x7c00, 0x7e00)
+#   - starts executing code at address 0x7c00
+
+# offset 00 (address 0x7c00):
+  # disable interrupts for this initialization
+  fa  # cli
+
+  # initialize segment registers
+  # this isn't always needed, but the recommendation is to not make assumptions
+  b8 00 00  # ax <- 0
+  8e d8  # ds <- ax
+  8e c0  # es <- ax
+  8e e0  # fs <- ax
+  8e e8  # gs <- ax
+
+  # initialize stack to 0x00070000
+  # 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 00 70  # ax <- 0x7000
+  8e d0  # ss <- ax
+  bc 00 00  # sp <- 0x0000
+
+  # 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
+  # seta20.1:
+  e4 64  # al <- port 0x64
+  a8 02  # set zf if bit 1 (second-least significant) is not set
+  75 fa  # if zf not set, goto seta20.1 (-6)
+  b0 d1  # al <- 0xd1
+  e6 64  # port 0x64 <- al
+  # seta20.2:
+  e4 64  # al <- port 0x64
+  a8 02  # set zf if bit 1 (second-least significant) is not set
+  75 fa  # if zf not set, goto seta20.2 (-6)
+  b0 df  # al <- 0xdf
+  e6 64  # port 0x64 <- al
+
+  # load remaining sectors from first two tracks of disk into addresses [0x7e00, 0x17800)
+  b4 02  # ah <- 2  # read sectors from disk
+  # dl comes conveniently initialized at boot time with the index of the device being booted
+  b5 00  # ch <- 0  # cylinder 0
+  b6 00  # dh <- 0  # track 0
+  b1 02  # cl <- 2  # second sector, 1-based
+  b0 7d  # al <- 125  # number of sectors to read = 2*63 - 1
+  # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment
+  bb 00 00  # bx <- 0
+  8e c3  # es <- bx
+  bb 00 7e  # bx <- 0x7e00 [label]
+  cd 13  # int 13h, BIOS disk service
+  0f 82 a3 00  # jump-if-carry disk_error [label]
+
+  # load two more tracks of disk into addresses [0x17800, 0x27400)
+  b4 02  # ah <- 2  # read sectors from disk
+  # dl comes conveniently initialized at boot time with the index of the device being booted
+  b5 00  # ch <- 0  # cylinder 0
+  b6 02  # dh <- 2  # track 0
+  b1 01  # cl <- 1  # first sector, 1-based
+  b0 7e  # al <- 126  # number of sectors to read = 2*63
+  # address to write sectors to = es:bx = 0x17800
+  bb 80 17  # bx <- 0x1780 [label]
+  8e c3  # es <- bx
+  bb 00 00  # bx <- 0
+  cd 13  # int 13h, BIOS disk service
+  0f 82 9b 00  # jump-if-carry disk_error [label]
+
+  # load two more tracks of disk into addresses [0x27400, 0x37000)
+  b4 02  # ah <- 2  # read sectors from disk
+  # dl comes conveniently initialized at boot time with the index of the device being booted
+  b5 00  # ch <- 0  # cylinder 0
+  b6 02  # dh <- 2  # track 0
+  b1 01  # cl <- 1  # first sector, 1-based
+  b0 7e  # al <- 126  # number of sectors to read = 2*63
+  # address to write sectors to = es:bx = 0x17800
+  bb 80 17  # bx <- 0x1780 [label]
+  8e c3  # es <- bx
+  bb 00 00  # bx <- 0
+  cd 13  # int 13h, BIOS disk service
+  0f 82 9b 00  # jump-if-carry disk_error [label]
+
+  # reset es
+  bb 00 00  # bx <- 0
+  8e c3  # es <- bx
+
+  # adjust video mode
+  b4 4f  # ah <- 4f (VBE)
+  b0 02  # al <- 02 (set video mode)
+  bb 05 41  # bx <- 0x0105 (graphics 1024x768x256
+            #               0x4000 bit = configure linear frame buffer in Bochs emulator; hopefully this doesn't hurt anything when running natively)
+            # fallback mode: 0x0101 (640x480x256)
+  cd 10  # int 10h, Vesa BIOS extensions
+
+  # load information for the (hopefully) current video mode
+  # mostly just for the address to the linear frame buffer
+  b4 4f  # ah <- 4f (VBE)
+  b0 01  # al <- 01 (get video mode info)
+  b9 07 01  # cx <- 0x0107 (mode we requested)
+  bf 00 81  # di <- 0x8100 (video mode info) [label]
+  cd 10
+
+  # switch to 32-bit mode
+  0f 01 16  # lgdt 00/mod/indirect 010/subop 110/rm/use-disp16
+    f8 7c  # *gdt_descriptor [label]
+  0f 20 c0  # eax <- cr0
+  66 83 c8 01  # eax <- or 0x1
+  0f 22 c0  # cr0 <- eax
+  ea 00 7d 08 00  # far jump to initialize_32bit_mode after setting cs to the record at offset 8 in the gdt (gdt_code) [label]
+
+# padding
+# 8e:
+                                          00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# cf:
+# disk_error:
+  # print 'D' to top-left of screen to indicate disk error
+  # *0xb8000 <- 0x0f44
+  # bx <- 0xb800
+  bb 00 b8
+  # ds <- bx
+  8e db  # 11b/mod 011b/reg/ds 011b/rm/bx
+  # al <- 'D'
+  b0 44
+  # ah <- 0x0f  # white on black
+  b4 0f
+  # bx <- 0
+  bb 00 00
+  # *ds:bx <- ax
+  89 07  # 00b/mod/indirect 000b/reg/ax 111b/rm/bx
+
+e9 fd ff  # loop forever
+
+## GDT: 3 records of 8 bytes each
+
+# e0:
+# gdt_start:
+# gdt_null:  mandatory null descriptor
+  00 00 00 00 00 00 00 00
+# gdt_code:  (offset 8 from gdt_start)
+  ff ff  # limit[0:16]
+  00 00 00  # base[0:24]
+  9a  # 1/present 00/privilege 1/descriptor type = 1001b
+      # 1/code 0/conforming 1/readable 0/accessed = 1010b
+  cf  # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b
+      # limit[16:20] = 1111b
+  00  # base[24:32]
+# gdt_data:  (offset 16 from gdt_start)
+  ff ff  # limit[0:16]
+  00 00 00  # base[0:24]
+  92  # 1/present 00/privilege 1/descriptor type = 1001b
+      # 0/data 0/conforming 1/readable 0/accessed = 0010b
+  cf  # same as gdt_code
+  00  # base[24:32]
+# gdt_end:
+
+# f8:
+# gdt_descriptor:
+  17 00  # final index of gdt = gdt_end - gdt_start - 1
+  e0 7c 00 00  # start = gdt_start [label]
+
+# padding
+# fe:
+                                          00 00
+
+## 32-bit code from this point (still some instructions not in SubX)
+
+# offset 100 (address 0x7d00):
+# initialize_32bit_mode:
+  66 b8 10 00  # ax <- offset 16 from gdt_start
+  8e d8  # ds <- ax
+  8e d0  # ss <- ax
+  8e c0  # es <- ax
+  8e e0  # fs <- ax
+  8e e8  # gs <- ax
+
+# 10e:
+  bc 00 00 07 00  # esp <- 0x00070000
+
+# 113:
+  # load interrupt handlers
+  0f 01 1d  # lidt 00/mod/indirect 011/subop 101/rm32/use-disp32
+    00 80 00 00  # *idt_descriptor [label]
+
+  # 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
+
+# 11a:
+  # enable keyboard IRQ (1)
+  b0 fd  # al <- 0xfd  # disable mask for IRQ1
+  e6 21  # port 0x21 <- al
+
+# 11e:
+  fb  # enable interrupts
+  db e3  # initialize FPU
+  # eax <- cr4
+  0f 20  # copy cr4 to rm32
+    e0  # 11/mod/direct 100/r32/CR4 000/rm32/eax
+  # eax <- or bit 9
+  0f ba
+    e8  # 11/mod/direct 101/subop/bit-test-and-set 000/rm32/eax
+    09  # imm8
+  # cr4 <- eax
+  0f 22  # copy rm32 to cr4
+    e0  # 11/mod/direct 100/r32/CR4 000/rm32/eax
+  e9 d0 16 00 00  # jump to 0x9400 [label]
+
+# padding
+# 130:
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# 1fe:
+# final 2 bytes of boot sector
+55 aa
+
+## sector 2 onwards loaded by load_disk, not automatically on boot
+
+# offset 200 (address 0x7e00):
+# null interrupt handler:
+  cf  # iret
+
+# padding
+# 201:
+   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# 210:
+# keyboard interrupt handler:
+  # prologue
+  fa  # disable interrupts
+  60  # push all registers to stack
+  # acknowledge interrupt
+  b0 20  # al <- 0x20
+  e6 20  # port 0x20 <- al
+  31 c0  # eax <- xor eax;  11/direct 000/r32/eax 000/rm32/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 64  # al <- port 0x64
+  a8 01  # set zf if bit 0 (least significant) is not set
+  74 bb  # jump to epilogue if 0 bit is not set [label]
+# 21e:
+  # - if keyboard buffer is full, return
+  31 c9  # ecx <- xor ecx;  11/direct 001/r32/ecx 001/rm32/ecx
+  # var index/ecx: byte
+  8a  # copy m8 at r32 to r8
+    0d  # 00/mod/indirect 001/r8/cl 101/rm32/use-disp32
+    28 80 00 00  # disp32 [label]
+  # al = *(keyboard buffer + index)
+  8a  # copy m8 at r32 to r8
+    81  # 10/mod/*+disp32 000/r8/al 001/rm32/ecx
+    30 80 00 00  # disp32 [label]
+  # if (al != 0) return
+  3c 00  # compare al, 0
+  75 a9  # jump to epilogue if != [label]
+# 230:
+  # - read keycode
+  e4 60  # al <- port 0x60
+  # - key released
+  # if (al == 0xaa) shift = false  # left shift is being lifted
+  3c aa  # compare al, 0xaa
+  75 0a  # jump to $1 if != [label]
+  # *shift = 0
+  c7  # copy imm32 to rm32
+    05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+    10 80 00 00  # disp32 [label]
+    00 00 00 00  # imm32
+# 240:
+# $1:
+  # if (al == 0xb6) shift = false  # right shift is being lifted
+  3c b6  # compare al, 0xb6
+  75 0a  # jump to $2 if != [label]
+  # *shift = 0
+  c7  # copy imm32 to rm32
+    05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+    10 80 00 00  # disp32 [label]
+    00 00 00 00  # imm32
+# 24e:
+# $2:
+  # if (al == 0x9d) ctrl = false  # ctrl is being lifted
+  3c 9d  # compare al, 0x9d
+  75 0a  # jump to $3 if != [label]
+  # *ctrl = 0
+  c7  # copy imm32 to rm32
+    05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+    14 80 00 00  # disp32 [label]
+    00 00 00 00  # imm32
+# 25c:
+# $3:
+  # if (al & 0x80) a key is being lifted; return
+  50  # push eax
+  24 80  # al <- and 0x80
+  3c 00  # compare al, 0
+  58  # pop to eax (without touching flags)
+  75 75  # jump to epilogue if != [label]
+# 264:
+  # - key pressed
+  # if (al == 0x2a) shift = true, return  # left shift pressed
+  3c 2a  # compare al, 0x2a
+  75 0c  # jump to $4 if != [label]
+  # *shift = 1
+  c7  # copy imm32 to rm32
+    05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+    10 80 00 00  # disp32 [label]
+    01 00 00 00  # imm32
+  eb 65 # jump to epilogue [label]
+# 274:
+# $4:
+  # if (al == 0x36) shift = true, return  # right shift pressed
+  3c 36  # compare al, 0x36
+  75 0c  # jump to $5 if != [label]
+  # *shift = 1
+  c7  # copy imm32 to rm32
+    05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+    10 80 00 00  # disp32 [label]
+    01 00 00 00  # imm32
+  eb 55 # jump to epilogue [label]
+# 284:
+# $5:
+  # if (al == 0x1d) ctrl = true, return
+  3c 1d  # compare al, 0x36
+  75 0c  # jump to $6 if != [label]
+  # *shift = 1
+  c7  # copy imm32 to rm32
+    05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+    14 80 00 00  # disp32 [label]
+    01 00 00 00  # imm32
+  eb 45 # jump to epilogue [label]
+# 294:
+# $6:
+  # - convert key to character
+  # if (shift) use keyboard shift map
+  81  # operate on rm32 and imm32
+    3d  # 00/mod/indirect 111/subop/compare 101/rm32/use-disp32
+    10 80 00 00  # disp32 = shift [label]
+    00 00 00 00  # imm32
+  74 08  # jump to $7 if = [label]
+  # al <- *(keyboard shift map + eax)
+  8a  # copy m8 at rm32 to r8
+    80  # 10/mod/*+disp32 000/r8/al 000/rm32/eax
+    00 87 00 00  # disp32 [label]
+  eb 1a  # jump to $8 [label]
+# 2a8:
+# $7:
+  # if (ctrl) use keyboard ctrl map
+  81  # operate on rm32 and imm32
+    3d  # 00/mod/indirect 111/subop/compare 101/rm32/use-disp32
+    14 80 00 00  # disp32 = ctrl [label]
+    00 00 00 00  # imm32
+  74 08  # jump to $8 if = [label]
+  # al <- *(keyboard ctrl map + eax)
+  8a  # copy m8 at rm32 to r8
+    80  # 10/mod/*+disp32 000/r8/al 000/rm32/eax
+    00 88 00 00  # disp32 [label]
+  eb 06  # jump to $9 [label]
+# 2bc:
+# $8:
+  # otherwise use keyboard normal map
+  # al <- *(keyboard normal map + eax)
+  8a  # copy m8 at rm32 to r8
+    80  # 10/mod/*+disp32 000/r8/al 000/rm32/eax
+    00 86 00 00  # disp32 [label]
+# 2c2:
+# $9:
+  # - if there's no character mapping, return
+  3c 00  # compare al, 0
+  74 13  # jump to epilogue if = [label]
+# 2c6:
+  # - store al in keyboard buffer
+  88  # copy r8 to m8 at r32
+    81  # 10/mod/*+disp32 000/r8/al 001/rm32/ecx
+    30 80 00 00  # disp32 [label]
+  # increment index
+  fe  # increment byte
+    05  # 00/mod/indirect 000/subop/increment 101/rm32/use-disp32
+    28 80 00 00  # disp32 [label]
+  # clear top nibble of index (keyboard buffer is circular)
+  80  # and byte
+    25  # 00/mod/indirect 100/subop/and 101/rm32/use-disp32
+    28 80 00 00  # disp32 [label]
+    0f  # imm8
+# 2d9:
+  # epilogue
+  61  # pop all registers
+  fb  # enable interrupts
+  cf  # iret
+
+# padding
+# 2dc:
+                                    00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# 300:
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# offset 400 (address 0x8000): interrupt handler data
+# idt_descriptor:
+  ff 03  # idt_end - idt_start - 1
+  00 82 00 00  # start = idt_start [label]
+
+# padding
+# 406:
+                  00 00 00 00 00 00 00 00 00 00
+
+# 410:
+# var shift: boolean
+  00 00 00 00
+
+# 414:
+# var ctrl: boolean
+  00 00 00 00
+
+# padding
+# 418:
+                        00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+# 428:
+# var keyboard circular buffer
+# write index: nibble
+# still take up 4 bytes so SubX can handle it
+  00 00 00 00
+# 42c:
+# read index: nibble
+# still take up 4 bytes so SubX can handle it
+  00 00 00 00
+# 430:
+# circular buffer: byte[16]
+  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# padding
+# 440:
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# offset 500 (address 0x8100):
+# video mode info {{{
+  00 00  # attributes
+  00  # winA
+  00  # winB
+# 04
+  00 00  # granularity
+  00 00  # winsize
+# 08
+  00 00  # segmentA
+  00 00  # segmentB
+# 0c
+  00 00 00 00  # realFctPtr (who knows)
+# 10
+  00 00  # pitch
+  00 00  # Xres
+# 14
+  00 00  # Yres
+  00 00  # Wchar Ychar
+# 18
+  00  # planes
+  00  # bpp
+  00  # banks
+  00  # memory_model
+# 1c
+  00  # bank_size
+  00  # image_pages
+  00  # reserved
+# 1f
+  00 00  # red_mask red_position
+  00 00  # green_mask green_position
+  00 00  # blue_mask blue_position
+  00 00  # rsv_mask rsv_position
+  00  # directcolor_attributes
+# 28
+  00 00 00 00  # physbase <== linear frame buffer
+
+# 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
+# }}}
+
+# offset 600 (address 0x8200):
+# interrupt descriptor table {{{
+# 128 entries * 8 bytes each = 1024 bytes (0x400)
+# 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: clock
+  00 7e  # target[0:16] = null interrupt handler [label]
+  08 00  # segment selector (gdt_code)
+  00  # unused
+  8e  # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate
+  00 00  # target[16:32]
+
+# entry 9: keyboard
+  10 7e  # target[0:16] = keyboard interrupt handler [label]
+  08 00  # segment selector (gdt_code)
+  00  # unused
+  8e  # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate
+  00 00  # target[16:32]
+
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+# 500:
+# entry 0x20
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+# 600:
+# entry 0x40
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+
+# 700:
+# entry 0x60
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+# entry 0x70
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00
+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:
+# }}}
+
+## the rest of this file has data
+
+# offset a00 (address 0x8600):
+# 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 00 00 00 00 00 00 00
+# numeric keypad would start here, but isn't implemented
+                                             00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# offset b00:
+# 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 00 00 00 00 00 00 00
+# numeric keypad would start here, but isn't implemented
+                                             00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# c00:
+# 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 ^j ^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
+                        00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# padding (there might be more keyboard tables)
+# d00:
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# e00:
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# f00:
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+# }}}
+
+# offset 1000 (address 0x8c00)
+# 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
+# }}}
+
+# offset 1800 (address 0x9400)
+
+# vim:ft=subx