From 4106cf7538b83f4985d1c9b5c69e0501213662ea Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 31 Jan 2021 21:08:28 -0800 Subject: 7680 --- html/baremetal/boot.hex.html | 974 +++++++++++++++++++++++-------------------- 1 file changed, 516 insertions(+), 458 deletions(-) (limited to 'html/baremetal/boot.hex.html') diff --git a/html/baremetal/boot.hex.html b/html/baremetal/boot.hex.html index 7e72f177..1a488154 100644 --- a/html/baremetal/boot.hex.html +++ b/html/baremetal/boot.hex.html @@ -14,8 +14,8 @@ pre { white-space: pre-wrap; font-family: monospace; color: #000000; background- body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.subxS1Comment { color: #0000af; } .LineNr { } .Folded { color: #080808; background-color: #949494; } --> @@ -53,463 +53,521 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/main/baremetal/boot.hex
-  1 # Code for the first few disk sectors that all programs in this directory need:
-  2 #   - load sectors past the first (using BIOS primitives) since only the first is available by default
-  3 #     - if this fails, print 'D' at top-left of screen and halt
-  4 #   - initialize a minimal graphics mode
-  5 #   - switch to 32-bit mode (giving up access to BIOS primitives)
-  6 #   - set up a handler for keyboard events
-  7 #   - jump to start of program
-  8 #
-  9 # To convert to a disk image, first prepare a realistically sized disk image:
- 10 #   dd if=/dev/zero of=disk.img count=20160  # 512-byte sectors, so 10MB
- 11 # Create initial sectors from this file:
- 12 #   ./bootstrap run apps/hex < baremetal/boot.hex > boot.bin
- 13 # Translate other sectors into a file called a.img
- 14 # Load all sectors into the disk image:
- 15 #   cat boot.bin a.img > disk.bin
- 16 #   dd if=disk.bin of=disk.img conv=notrunc
- 17 # To run:
- 18 #   qemu-system-i386 disk.img
- 19 # Or:
- 20 #   bochs -f baremetal/boot.bochsrc  # boot.bochsrc loads disk.img
- 21 #
- 22 # Since we start out in 16-bit mode, we need instructions SubX doesn't
- 23 # support.
- 24 # This file contains just lowercase hex bytes and comments. Zero
- 25 # error-checking. Make liberal use of:
- 26 #   - comments documenting expected offsets
- 27 #   - size checks on the emitted file (currently: 5120 bytes)
- 28 #   - xxd to eyeball that offsets contain expected bytes
- 29 #
- 30 # Programs using this initialization:
- 31 #   - can't use any syscalls
- 32 #   - can't print text to video memory (past these boot sectors)
- 33 #   - must only print raw pixels (256 colors) to video memory (resolution 1024x768)
- 34 #   - must store their entry-point at address 0x9000
- 35 
- 36 ## 16-bit entry point
- 37 
- 38 # Upon reset, the IBM PC:
- 39 #   - loads the first sector (512 bytes)
- 40 #     from some bootable image (see the boot sector marker at the end of this file)
- 41 #     to the address range [0x7c00, 0x7e00)
- 42 #   - starts executing code at address 0x7c00
- 43 
- 44 # offset 00 (address 0x7c00):
- 45   # disable interrupts for this initialization
- 46   fa  # cli
- 47 
- 48   # initialize segment registers
- 49   # this isn't always needed, but the recommendation is to not make assumptions
- 50   b8 00 00  # ax <- 0
- 51   8e d8  # ds <- ax
- 52   8e d0  # ss <- ax
- 53   8e c0  # es <- ax
- 54   8e e0  # fs <- ax
- 55   8e e8  # gs <- ax
- 56 
- 57   # We don't read or write the stack before we get to 32-bit mode. No function
- 58   # calls, so we don't need to initialize the stack.
- 59 
- 60 # 0e:
- 61   # load some sectors from disk
- 62   b4 02  # ah <- 2  # read sectors from disk
- 63   # dl comes conveniently initialized at boot time with the index of the device being booted
- 64   b5 00  # ch <- 0  # cylinder 0
- 65   b6 00  # dh <- 0  # track 0
- 66   b1 02  # cl <- 2  # second sector, 1-based
- 67   b0 80  # al <- 128  # number of sectors to read; TODO - all sectors might need to be in a single track on real hardware (so 63 sectors at most including the boot sector)
- 68   # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment
- 69   bb 00 00  # bx <- 0
- 70   8e c3  # es <- bx
- 71   bb 00 7e  # bx <- 0x7e00 [label]
- 72   cd 13  # int 13h, BIOS disk service
- 73   0f 82 8a 00  # jump-if-carry disk-error [label]
- 74 
- 75 # 26:
- 76   # undo the A20 hack: https://en.wikipedia.org/wiki/A20_line
- 77   # this is from https://github.com/mit-pdos/xv6-public/blob/master/bootasm.S
- 78   # seta20.1:
- 79   e4 64  # al <- port 0x64
- 80   a8 02  # set zf if bit 1 (second-least significant) is not set
- 81   75 fa  # if zf not set, goto seta20.1 (-6)
- 82 
- 83   b0 d1  # al <- 0xd1
- 84   e6 64  # port 0x64 <- al
- 85 
- 86 # 30:
- 87   # seta20.2:
- 88   e4 64  # al <- port 0x64
- 89   a8 02  # set zf if bit 1 (second-least significant) is not set
- 90   75 fa  # if zf not set, goto seta20.2 (-6)
- 91 
- 92   b0 df  # al <- 0xdf
- 93   e6 64  # port 0x64 <- al
- 94 
- 95 # 3a:
- 96   # adjust video mode
- 97   b4 4f  # ah <- 4f (VBE)
- 98   b0 02  # al <- 02 (set video mode)
- 99   bb 05 41  # bx <- 0x0105 (graphics 1024x768x256
-100             #               0x4000 bit = configure linear frame buffer in Bochs emulator; hopefully this doesn't hurt anything when running natively)
-101             # fallback mode: 0x0101 (640x480x256)
-102   cd 10  # int 10h, Vesa BIOS extensions
-103 
-104 # 43:
-105   # load information for the (hopefully) current video mode
-106   # mostly just for the address to the linear frame buffer
-107   b4 4f  # ah <- 4f (VBE)
-108   b0 01  # al <- 01 (get video mode)
-109   b9 07 01  # cx <- 0x0107 (mode we requested)
-110   bf 00 7f  # di <- 0x7f00 (video mode info) [label]
-111   cd 10
-112 
-113 # 4f:
-114   # switch to 32-bit mode
-115   0f 01 16  # lgdt 00/mod/indirect 010/subop 110/rm/use-disp16
-116     a0 7c  # *gdt_descriptor [label]
-117   0f 20 c0  # eax <- cr0
-118   66 83 c8 01  # eax <- or 0x1
-119   0f 22 c0  # cr0 <- eax
-120   ea e0 7c 08 00  # far jump to initialize_32bit_mode after setting cs to the record at offset 8 in the gdt (gdt_code) [label]
-121 
-122 # padding
-123 # 63:
-124          00 00 00 00 00 00 00 00 00 00 00 00 00
-125 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-126 
-127 ## GDT: 3 records of 8 bytes each
-128 
-129 # 80:
-130 # gdt_start:
-131 # gdt_null:  mandatory null descriptor
-132   00 00 00 00 00 00 00 00
-133 # gdt_code:  (offset 8 from gdt_start)
-134   ff ff  # limit[0:16]
-135   00 00 00  # base[0:24]
-136   9a  # 1/present 00/privilege 1/descriptor type = 1001b
-137       # 1/code 0/conforming 1/readable 0/accessed = 1010b
-138   cf  # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b
-139       # limit[16:20] = 1111b
-140   00  # base[24:32]
-141 # gdt_data:  (offset 16 from gdt_start)
-142   ff ff  # limit[0:16]
-143   00 00 00  # base[0:24]
-144   92  # 1/present 00/privilege 1/descriptor type = 1001b
-145       # 0/data 0/conforming 1/readable 0/accessed = 0010b
-146   cf  # same as gdt_code
-147   00  # base[24:32]
-148 # gdt_end:
-149 
-150 # padding
-151 # 98:
-152                         00 00 00 00 00 00 00 00
-153 
-154 # a0:
-155 # gdt_descriptor:
-156   17 00  # final index of gdt = gdt_end - gdt_start - 1
-157   80 7c 00 00  # start = gdt_start [label]
-158 
-159 # padding
-160 # a5:
-161                   00 00 00 00 00 00 00 00 00 00
-162 
-163 # b0:
-164 # disk_error:
-165   # print 'D' to top-left of screen to indicate disk error
-166   # *0xb8000 <- 0x0f44
-167   # bx <- 0xb800
-168   bb 00 b8
-169   # ds <- bx
-170   8e db  # 11b/mod 011b/reg/ds 011b/rm/bx
-171   # al <- 'D'
-172   b0 44
-173   # ah <- 0x0f  # white on black
-174   b4 0f
-175   # bx <- 0
-176   bb 00 00
-177   # *ds:bx <- ax
-178   89 07  # 00b/mod/indirect 000b/reg/ax 111b/rm/bx
-179 
-180 e9 fb ff  # loop forever
-181 
-182 # padding
-183 # c1:
-184    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-185 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-186 
-187 ## 32-bit code from this point (still some instructions not in SubX)
-188 
-189 # e0:
-190 # initialize_32bit_mode:
-191   66 b8 10 00  # ax <- offset 16 from gdt_start
-192   8e d8  # ds <- ax
-193   8e d0  # ss <- ax
-194   8e c0  # es <- ax
-195   8e e0  # fs <- ax
-196   8e e8  # gs <- ax
-197 
-198   # load interrupt handlers
-199   0f 01 1d  # lidt 00/mod/indirect 011/subop 101/rm32/use-disp32
-200     f8 7d 00 00  # *idt_descriptor [label]
-201 
-202   # For now, not bothering reprogramming the IRQ to not conflict with software
-203   # exceptions.
-204   #   https://wiki.osdev.org/index.php?title=8259_PIC&oldid=24650#Protected_Mode
-205   #
-206   # Interrupt 1 (keyboard) conflicts with debugger faults. We don't use a
-207   # debugger.
-208   # Reference:
-209   #   https://wiki.osdev.org/Exceptions
-210 
-211   # enable keyboard IRQ (1)
-212   b0 fd  # al <- 0xfd  # disable mask for IRQ1
-213   e6 21  # port 0x21 <- al
-214 
-215   # initialization is done; enable interrupts
-216   fb
-217   e9 01 13 00 00  # jump to 0x9000 [label]
-218 
-219 # padding
-220 # ff:
-221                                              00
-222 
-223 # 100:
-224 # null interrupt handler:
-225   cf  # iret
-226 
-227 # padding
-228 # 101:
-229    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-230 
-231 # 110:
-232 # keyboard interrupt handler:
-233   # prologue
-234   fa  # disable interrupts
-235   60  # push all registers to stack
-236   # acknowledge interrupt
-237   b0 20  # al <- 0x20
-238   e6 20  # port 0x20 <- al
-239   # read status into eax
-240   31 c0  # eax <- xor eax;  11/direct 000/r32/eax 000/rm32/eax
-241   e4 64  # al <- port 0x64
-242   # if (status & 0x1) == 0, return
-243   24 01  # al <- and 0x1
-244   3c 00  # compare al, 0
-245   74 39  # jump to epilogue if = [label]
-246 # 120:
-247   # if keyboard buffer is full, return
-248   31 c9  # ecx <- xor ecx;  11/direct 001/r32/ecx 001/rm32/ecx
-249   # . var index/ecx: byte
-250   8a  # copy m8 at r32 to r8
-251     0d  # 00/mod/indirect 001/r8/cl 101/rm32/use-disp32
-252     c8 7d 00 00  # disp32 [label]
-253   # . al = *(keyboard buffer + index)
-254   8a  # copy m8 at r32 to r8
-255     81  # 10/mod/*+disp32 000/r8/al 001/rm32/ecx
-256     d0 7d 00 00  # disp32 [label]
-257   # . if (al != 0) return
-258   3c 00  # compare al, 0
-259 # 130:
-260   75 27  # jump to epilogue if != [label]
-261   # read keycode into al
-262   e4 60  # al <- port 0x60
-263   # if (al & 0x80) a key is being lifted; return
-264   50  # push eax
-265   24 80  # al <- and 0x80
-266   3c 00  # compare al, 0
-267   58  # pop to eax (without touching flags)
-268   75 1d  # jump to epilogue if != [label]
-269 # 13c:
-270   # al <- *(keyboard normal map + eax)
-271   8a  # copy m8 at rm32 to r8
-272     80  # 10/mod/*+disp32 000/r8/al 000/rm32/eax
-273     00 80 00 00  # disp32 [label]
-274   # if there's no character mapping, return
-275   3c 00  # compare al, 0
-276   74 13  # jump to epilogue if = [label]
-277 # 146:
-278   # store al in keyboard buffer
-279   88  # copy r8 to m8 at r32
-280     81  # 10/mod/*+disp32 000/r8/al 001/rm32/ecx
-281     d0 7d 00 00  # disp32 [label]
-282 # 14c:
-283   # increment index
-284   fe  # increment byte
-285     05  # 00/mod/indirect 000/subop/increment 101/rm32/use-disp32
-286     c8 7d 00 00  # disp32 [label]
-287   # clear top nibble of index (keyboard buffer is circular)
-288   80  # and byte
-289     25  # 00/mod/indirect 100/subop/and 101/rm32/use-disp32
-290     c8 7d 00 00  # disp32 [label]
-291     0f  # imm8
-292 # 159:
-293   # epilogue
-294   61  # pop all registers
-295   fb  # enable interrupts
-296   cf  # iret
-297 
-298 # padding
-299 # 15c:
-300                                     00 00 00 00
-301 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-302 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-303 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-304 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-305 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-306 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-307 00 00 00 00 00 00 00 00
-308 
-309 # 1c8:
-310 # var keyboard circular buffer
-311 # write index: nibble
-312 # still take up 4 bytes so SubX can handle it
-313   00 00 00 00
-314 # 1cc:
-315 # read index: nibble
-316 # still take up 4 bytes so SubX can handle it
-317   00 00 00 00
-318 # 1d0:
-319 # circular buffer: byte[16]
-320   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-321 
-322 # padding
-323 # 1e0:
-324 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-325 00 00 00 00 00 00 00 00
-326 
-327 # 1f8:
-328 # idt_descriptor:
-329   ff 00  # idt_end - idt_start - 1
-330   00 7e 00 00  # start = idt_start [label]
-331 
-332 # 1fe:
-333 # final 2 bytes of boot sector
-334 55 aa
-335 
-336 ## sector 2
-337 # loaded by load_disk, not automatically on boot
-338 
-339 # offset 200 (address 0x7e00): interrupt descriptor table
-340 # 32 entries * 8 bytes each = 256 bytes (0x100)
-341 # idt_start:
-342 
-343 00 00 00 00 00 00 00 00
-344 00 00 00 00 00 00 00 00
-345 00 00 00 00 00 00 00 00
-346 00 00 00 00 00 00 00 00
-347 00 00 00 00 00 00 00 00
-348 00 00 00 00 00 00 00 00
-349 00 00 00 00 00 00 00 00
-350 00 00 00 00 00 00 00 00
-351 
-352 # By default, BIOS maps IRQ0-7 to interrupt vectors 8-15.
-353 # https://wiki.osdev.org/index.php?title=Interrupts&oldid=25102#Default_PC_Interrupt_Vector_Assignment
-354 
-355 # entry 8: clock
-356   00 7d  # target[0:16] = null interrupt handler [label]
-357   08 00  # segment selector (gdt_code)
-358   00  # unused
-359   8e  # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate
-360   00 00  # target[16:32]
-361 
-362 # entry 9: keyboard
-363   10 7d  # target[0:16] = keyboard interrupt handler [label]
-364   08 00  # segment selector (gdt_code)
-365   00  # unused
-366   8e  # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate
-367   00 00  # target[16:32]
-368 
-369 00 00 00 00 00 00 00 00
-370 00 00 00 00 00 00 00 00
-371 00 00 00 00 00 00 00 00
-372 00 00 00 00 00 00 00 00
-373 00 00 00 00 00 00 00 00
-374 00 00 00 00 00 00 00 00
-375 00 00 00 00 00 00 00 00
-376 00 00 00 00 00 00 00 00
-377 00 00 00 00 00 00 00 00
-378 00 00 00 00 00 00 00 00
-379 00 00 00 00 00 00 00 00
-380 00 00 00 00 00 00 00 00
-381 00 00 00 00 00 00 00 00
-382 00 00 00 00 00 00 00 00
-383 00 00 00 00 00 00 00 00
-384 00 00 00 00 00 00 00 00
-385 00 00 00 00 00 00 00 00
-386 00 00 00 00 00 00 00 00
-387 00 00 00 00 00 00 00 00
-388 00 00 00 00 00 00 00 00
-389 00 00 00 00 00 00 00 00
-390 00 00 00 00 00 00 00 00
-391 # idt_end:
-392 
-393 # offset 300 (address 0x7f00):
-394 # video mode info:
-395   00 00  # attributes
-396   00  # winA
-397   00  # winB
-398 # 304
-399   00 00  # granularity
-400   00 00  # winsize
-401 # 308
-402   00 00  # segmentA
-403   00 00  # segmentB
-404 # 30c
-405   00 00 00 00  # realFctPtr (who knows)
-406 # 310
-407   00 00  # pitch
-408   00 00  # Xres
-409 # 314
-410   00 00  # Yres
-411   00 00  # Wchar Ychar
-412 # 318
-413   00  # planes
-414   00  # bpp
-415   00  # banks
-416   00  # memory_model
-417 # 31c
-418   00  # bank_size
-419   00  # image_pages
-420   00  # reserved
-421 # 31f
-422   00 00  # red_mask red_position
-423   00 00  # green_mask green_position
-424   00 00  # blue_mask blue_position
-425   00 00  # rsv_mask rsv_position
-426   00  # directcolor_attributes
-427 # 328
-428   00 00 00 00  # physbase <== linear frame buffer
-429 
-430 # 32c
-431 # reserved for video mode info
-432                                     00 00 00 00
-433 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-434 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-435 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-436 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-437 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-438 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-439 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-441 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-442 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-443 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-444 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-445 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-446 
-447 ## the rest of this file has data
-448 
-449 # offset 400 (address 0x8000):
-450 +--161 lines: # translating keys to ASCII -----------------------------------------------------------------------------------------------------------------------------------------------
-611 
-612 # offset c00 (address 0x8800)
-613 +--236 lines: # Bitmaps for some ASCII characters (soon Unicode) ------------------------------------------------------------------------------------------------------------------------
-849 
-850 # offset 1400 (address 0x9000)
-851 
-852 # vim:ft=subx
+   1 # Code for the first few disk sectors that all programs in this directory need:
+   2 #   - load sectors past the first (using BIOS primitives) since only the first is available by default
+   3 #     - if this fails, print 'D' at top-left of screen and halt
+   4 #   - initialize a minimal graphics mode
+   5 #   - switch to 32-bit mode (giving up access to BIOS primitives)
+   6 #   - set up a handler for keyboard events
+   7 #   - jump to start of program
+   8 #
+   9 # To convert to a disk image, first prepare a realistically sized disk image:
+  10 #   dd if=/dev/zero of=disk.img count=20160  # 512-byte sectors, so 10MB
+  11 # Create initial sectors from this file:
+  12 #   ./bootstrap run apps/hex < baremetal/boot.hex > boot.bin
+  13 # Translate other sectors into a file called a.img
+  14 # Load all sectors into the disk image:
+  15 #   cat boot.bin a.img > disk.bin
+  16 #   dd if=disk.bin of=disk.img conv=notrunc
+  17 # To run:
+  18 #   qemu-system-i386 disk.img
+  19 # Or:
+  20 #   bochs -f baremetal/boot.bochsrc  # boot.bochsrc loads disk.img
+  21 #
+  22 # Since we start out in 16-bit mode, we need instructions SubX doesn't
+  23 # support.
+  24 # This file contains just lowercase hex bytes and comments. It makes liberal
+  25 # use of:
+  26 #   - comments documenting expected offsets
+  27 #   - size checks on the emitted file (currently: 6144 bytes)
+  28 #   - xxd to eyeball that offsets contain expected bytes
+  29 #
+  30 # Programs using this initialization:
+  31 #   - can't use any syscalls
+  32 #   - can't print text to video memory (past these boot sectors)
+  33 #   - must only print raw pixels (256 colors) to video memory (resolution 1024x768)
+  34 #   - must start executing immediately after this file (see outline below)
+  35 #
+  36 # Don't panic! This file doesn't contain any loops or function calls. 80% of
+  37 # it is data. One pass through less than 1KB of code (there's lots of
+  38 # padding), and then we jump into a better notation. The rest of the stack
+  39 # (really only in a couple of slightly higher-level places) only needs to know
+  40 # a few magic constants:
+  41 #   Video memory: start is stored at 0x8128
+  42 #   Keyboard buffer: starts at 0x8028
+  43 
+  44 # Outline of this file with offsets and the addresses they map to at run-time:
+  45 # -- 16-bit mode code
+  46 #   offset    0 (address 7c00): boot code
+  47 # -- 16-bit mode data
+  48 #            e0 (address 7c80) global descriptor table
+  49 #            f8 (address 7ca0) <== gdt_descriptor
+  50 # -- 32-bit mode code
+  51 #   offset  100 (address 7d00): boot code
+  52 #           1fe (address 7dfe) boot sector marker (2 bytes)
+  53 #   offset  200 (address 7e00): interrupt handler code
+  54 # -- 32-bit mode data
+  55 #   offset  400 (address 8000): handler data
+  56 #           410 (address 8010): keyboard handler data
+  57 #           428 (address 8028) <== keyboard buffer
+  58 #   offset  500 (address 8100): video mode data (256 bytes)
+  59 #           528 (address 8128) <== start of video RAM stored here
+  60 #   offset  600 (address 8200): interrupt descriptor table (1KB)
+  61 #   offset  a00 (address 8600): keyboard mappings (1.5KB)
+  62 #   offset 1000 (address 8c00): bitmap font (2KB)
+  63 #   offset 1800 (address 9400): entrypoint for applications (don't forget to adjust survey_baremetal if this changes)
+  64 
+  65 # Other details of the current memory map:
+  66 #   code: first two default-sized disk tracks into [0x00007c00, 0x00017800)
+  67 #   stack grows down from 0x00070000
+  68 #     see below
+  69 #   heap: [0x01000000, 0x02000000)
+  70 #     see baremetal/120allocate.subx
+  71 # Consult https://wiki.osdev.org/Memory_Map_(x86) before modifying any of this.
+  72 
+  73 ## 16-bit entry point
+  74 
+  75 # Upon reset, the IBM PC:
+  76 #   - loads the first sector (512 bytes)
+  77 #     from some bootable image (see the boot sector marker at the end of this file)
+  78 #     to the address range [0x7c00, 0x7e00)
+  79 #     call this disk read #0
+  80 #   - starts executing code at address 0x7c00
+  81 
+  82 # offset 00 (address 0x7c00):
+  83   # disable interrupts for this initialization
+  84   fa  # cli
+  85 
+  86   # initialize segment registers
+  87   # this isn't always needed, but the recommendation is to not make assumptions
+  88   b8 00 00  # ax <- 0
+  89   8e d8  # ds <- ax
+  90   8e c0  # es <- ax
+  91   8e e0  # fs <- ax
+  92   8e e8  # gs <- ax
+  93 
+  94   # initialize stack to 0x00070000
+  95   # We don't read or write the stack before we get to 32-bit mode, but BIOS
+  96   # calls do. We need to move the stack in case BIOS initializes it to some
+  97   # low address that we want to write code into.
+  98   b8 00 70  # ax <- 0x7000
+  99   8e d0  # ss <- ax
+ 100   bc 00 00  # sp <- 0x0000
+ 101 
+ 102 # 14:
+ 103   # disk read #1: load remaining sectors from first two tracks of disk into addresses [0x7e00, 0x17800)
+ 104   b4 02  # ah <- 2  # read sectors from disk
+ 105   # dl comes conveniently initialized at boot time with the index of the device being booted
+ 106   b5 00  # ch <- 0  # cylinder 0
+ 107   b6 00  # dh <- 0  # track 0
+ 108   b1 02  # cl <- 2  # second sector, 1-based
+ 109   b0 7d  # al <- 125  # number of sectors to read
+ 110   # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment
+ 111   bb 00 00  # bx <- 0
+ 112   8e c3  # es <- bx
+ 113   bb 00 7e  # bx <- 0x7e00 [label]
+ 114   cd 13  # int 13h, BIOS disk service
+ 115   0f 82 a3 00  # jump-if-carry disk_error [label]
+ 116 
+ 117 # 2c:
+ 118   # undo the A20 hack: https://en.wikipedia.org/wiki/A20_line
+ 119   # this is from https://github.com/mit-pdos/xv6-public/blob/master/bootasm.S
+ 120   # seta20.1:
+ 121   e4 64  # al <- port 0x64
+ 122   a8 02  # set zf if bit 1 (second-least significant) is not set
+ 123   75 fa  # if zf not set, goto seta20.1 (-6)
+ 124 
+ 125   b0 d1  # al <- 0xd1
+ 126   e6 64  # port 0x64 <- al
+ 127 
+ 128 # 36:
+ 129   # seta20.2:
+ 130   e4 64  # al <- port 0x64
+ 131   a8 02  # set zf if bit 1 (second-least significant) is not set
+ 132   75 fa  # if zf not set, goto seta20.2 (-6)
+ 133 
+ 134   b0 df  # al <- 0xdf
+ 135   e6 64  # port 0x64 <- al
+ 136 
+ 137 # 40:
+ 138   # adjust video mode
+ 139   b4 4f  # ah <- 4f (VBE)
+ 140   b0 02  # al <- 02 (set video mode)
+ 141   bb 05 41  # bx <- 0x0105 (graphics 1024x768x256
+ 142             #               0x4000 bit = configure linear frame buffer in Bochs emulator; hopefully this doesn't hurt anything when running natively)
+ 143             # fallback mode: 0x0101 (640x480x256)
+ 144   cd 10  # int 10h, Vesa BIOS extensions
+ 145 
+ 146 # 49:
+ 147   # load information for the (hopefully) current video mode
+ 148   # mostly just for the address to the linear frame buffer
+ 149   b4 4f  # ah <- 4f (VBE)
+ 150   b0 01  # al <- 01 (get video mode)
+ 151   b9 07 01  # cx <- 0x0107 (mode we requested)
+ 152   bf 00 81  # di <- 0x7f00 (video mode info) [label]
+ 153   cd 10
+ 154 
+ 155 # 55:
+ 156   # switch to 32-bit mode
+ 157   0f 01 16  # lgdt 00/mod/indirect 010/subop 110/rm/use-disp16
+ 158     f8 7c  # *gdt_descriptor [label]
+ 159   0f 20 c0  # eax <- cr0
+ 160   66 83 c8 01  # eax <- or 0x1
+ 161   0f 22 c0  # cr0 <- eax
+ 162   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]
+ 163 
+ 164 # padding
+ 165 # 69:
+ 166                            00 00 00 00 00 00 00
+ 167 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 168 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 169 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 171 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 172 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 173 
+ 174 # cf:
+ 175 # disk_error:
+ 176   # print 'D' to top-left of screen to indicate disk error
+ 177   # *0xb8000 <- 0x0f44
+ 178   # bx <- 0xb800
+ 179   bb 00 b8
+ 180   # ds <- bx
+ 181   8e db  # 11b/mod 011b/reg/ds 011b/rm/bx
+ 182   # al <- 'D'
+ 183   b0 44
+ 184   # ah <- 0x0f  # white on black
+ 185   b4 0f
+ 186   # bx <- 0
+ 187   bb 00 00
+ 188   # *ds:bx <- ax
+ 189   89 07  # 00b/mod/indirect 000b/reg/ax 111b/rm/bx
+ 190 
+ 191 e9 fd ff  # loop forever
+ 192 
+ 193 ## GDT: 3 records of 8 bytes each
+ 194 
+ 195 # e0:
+ 196 # gdt_start:
+ 197 # gdt_null:  mandatory null descriptor
+ 198   00 00 00 00 00 00 00 00
+ 199 # gdt_code:  (offset 8 from gdt_start)
+ 200   ff ff  # limit[0:16]
+ 201   00 00 00  # base[0:24]
+ 202   9a  # 1/present 00/privilege 1/descriptor type = 1001b
+ 203       # 1/code 0/conforming 1/readable 0/accessed = 1010b
+ 204   cf  # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b
+ 205       # limit[16:20] = 1111b
+ 206   00  # base[24:32]
+ 207 # gdt_data:  (offset 16 from gdt_start)
+ 208   ff ff  # limit[0:16]
+ 209   00 00 00  # base[0:24]
+ 210   92  # 1/present 00/privilege 1/descriptor type = 1001b
+ 211       # 0/data 0/conforming 1/readable 0/accessed = 0010b
+ 212   cf  # same as gdt_code
+ 213   00  # base[24:32]
+ 214 # gdt_end:
+ 215 
+ 216 # f8:
+ 217 # gdt_descriptor:
+ 218   17 00  # final index of gdt = gdt_end - gdt_start - 1
+ 219   e0 7c 00 00  # start = gdt_start [label]
+ 220 
+ 221 # padding
+ 222 # fe:
+ 223                                           00 00
+ 224 
+ 225 ## 32-bit code from this point (still some instructions not in SubX)
+ 226 
+ 227 # offset 100 (address 0x7d00):
+ 228 # initialize_32bit_mode:
+ 229   66 b8 10 00  # ax <- offset 16 from gdt_start
+ 230   8e d8  # ds <- ax
+ 231   8e d0  # ss <- ax
+ 232   8e c0  # es <- ax
+ 233   8e e0  # fs <- ax
+ 234   8e e8  # gs <- ax
+ 235 
+ 236 # 10e:
+ 237   bc 00 00 07 00  # esp <- 0x00070000
+ 238 
+ 239 # 113:
+ 240   # load interrupt handlers
+ 241   0f 01 1d  # lidt 00/mod/indirect 011/subop 101/rm32/use-disp32
+ 242     00 80 00 00  # *idt_descriptor [label]
+ 243 
+ 244   # For now, not bothering reprogramming the IRQ to not conflict with software
+ 245   # exceptions.
+ 246   #   https://wiki.osdev.org/index.php?title=8259_PIC&oldid=24650#Protected_Mode
+ 247   #
+ 248   # Interrupt 1 (keyboard) conflicts with debugger faults. We don't use a
+ 249   # debugger.
+ 250   # Reference:
+ 251   #   https://wiki.osdev.org/Exceptions
+ 252 
+ 253 # 11a:
+ 254   # enable keyboard IRQ (1)
+ 255   b0 fd  # al <- 0xfd  # disable mask for IRQ1
+ 256   e6 21  # port 0x21 <- al
+ 257 
+ 258 # 11e:
+ 259   # initialization is done; enable interrupts
+ 260   fb
+ 261   e9 dc 16 00 00  # jump to 0x9400 [label]
+ 262 
+ 263 # padding
+ 264 # 124:
+ 265             00 00 00 00 00 00 00 00 00 00 00 00
+ 266 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 267 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 268 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 269 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 270 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 271 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 272 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 273 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 274 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 275 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 276 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 277 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 278 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 279 
+ 280 # 1fe:
+ 281 # final 2 bytes of boot sector
+ 282 55 aa
+ 283 
+ 284 ## sector 2 onwards loaded by load_disk, not automatically on boot
+ 285 
+ 286 # offset 200 (address 0x7e00):
+ 287 # null interrupt handler:
+ 288   cf  # iret
+ 289 
+ 290 # padding
+ 291 # 201:
+ 292    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 293 
+ 294 # 210:
+ 295 # keyboard interrupt handler:
+ 296   # prologue
+ 297   fa  # disable interrupts
+ 298   60  # push all registers to stack
+ 299   # acknowledge interrupt
+ 300   b0 20  # al <- 0x20
+ 301   e6 20  # port 0x20 <- al
+ 302   # read status into eax
+ 303   31 c0  # eax <- xor eax;  11/direct 000/r32/eax 000/rm32/eax
+ 304   e4 64  # al <- port 0x64
+ 305   # if (status & 0x1) == 0, return
+ 306   24 01  # al <- and 0x1
+ 307   3c 00  # compare al, 0
+ 308   74 89  # jump to epilogue if = [label]
+ 309 # 220:
+ 310   # - if keyboard buffer is full, return
+ 311   31 c9  # ecx <- xor ecx;  11/direct 001/r32/ecx 001/rm32/ecx
+ 312   # var index/ecx: byte
+ 313   8a  # copy m8 at r32 to r8
+ 314     0d  # 00/mod/indirect 001/r8/cl 101/rm32/use-disp32
+ 315     28 80 00 00  # disp32 [label]
+ 316   # al = *(keyboard buffer + index)
+ 317   8a  # copy m8 at r32 to r8
+ 318     81  # 10/mod/*+disp32 000/r8/al 001/rm32/ecx
+ 319     30 80 00 00  # disp32 [label]
+ 320   # if (al != 0) return
+ 321   3c 00  # compare al, 0
+ 322 # 230:
+ 323   75 77  # jump to epilogue if != [label]
+ 324   # - read keycode
+ 325   e4 60  # al <- port 0x60
+ 326 # 234:
+ 327   # - key released
+ 328   # if (al == 0xaa) shift = false  # left shift is being lifted
+ 329   3c aa  # compare al, 0xaa
+ 330   75 0a  # jump to $1 if != [label]
+ 331 # 238:
+ 332   # *shift = 0
+ 333   c7  # copy imm32 to rm32
+ 334     05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+ 335     10 80 00 00  # disp32 [label]
+ 336     00 00 00 00  # imm32
+ 337 # 242:
+ 338 # $1:
+ 339   # if (al == 0xb6) shift = false  # right shift is being lifted
+ 340   3c b6  # compare al, 0xb6
+ 341 # 244:
+ 342   75 0a  # jump to $1 if != [label]
+ 343   # *shift = 0
+ 344   c7  # copy imm32 to rm32
+ 345     05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+ 346     10 80 00 00  # disp32 [label]
+ 347     00 00 00 00  # imm32
+ 348 # 250:
+ 349 # $2:
+ 350   # if (al & 0x80) a key is being lifted; return
+ 351   50  # push eax
+ 352   24 80  # al <- and 0x80
+ 353   3c 00  # compare al, 0
+ 354   58  # pop to eax (without touching flags)
+ 355   75 51  # jump to epilogue if != [label]
+ 356 # 258:
+ 357   # - key pressed
+ 358   # if (al == 0x2a) shift = true, return  # left shift pressed
+ 359   3c 2a  # compare al, 0x2a
+ 360   75 0c  # jump to $3 if != [label]
+ 361   # *shift = 1
+ 362   c7  # copy imm32 to rm32
+ 363     05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+ 364     10 80 00 00  # disp32 [label]
+ 365     01 00 00 00  # imm32
+ 366   eb 41 # jump to epilogue [label]
+ 367 # 268:
+ 368 # $3:
+ 369   # if (al == 0x36) shift = true, return  # right shift pressed
+ 370   3c 36  # compare al, 0x36
+ 371   75 0c  # jump to $4 if != [label]
+ 372   # *shift = 1
+ 373   c7  # copy imm32 to rm32
+ 374     05  # 00/mod/indirect 000/subop/copy 101/rm32/use-disp32
+ 375     10 80 00 00  # disp32 [label]
+ 376     01 00 00 00  # imm32
+ 377   eb 31 # jump to epilogue [label]
+ 378 # $4:
+ 379 # 278:
+ 380   # - convert key to character
+ 381   # if (shift) use keyboard normal map
+ 382   81  # operate on rm32 and imm32
+ 383     3d  # 00/mod/indirect 111/subop/compare 101/rm32/use-disp32
+ 384     10 80 00 00  # disp32 = shift [label]
+ 385     00 00 00 00  # imm32
+ 386   74 08  # jump to $5 if = [label]
+ 387 # 284:
+ 388   # otherwise use keyboard shift map
+ 389   # al <- *(keyboard shift map + eax)
+ 390   8a  # copy m8 at rm32 to r8
+ 391     80  # 10/mod/*+disp32 000/r8/al 000/rm32/eax
+ 392     00 87 00 00  # disp32 [label]
+ 393   eb 06  # jump to $6 [label]
+ 394 # $5:
+ 395 # 28c:
+ 396   # al <- *(keyboard normal map + eax)
+ 397   8a  # copy m8 at rm32 to r8
+ 398     80  # 10/mod/*+disp32 000/r8/al 000/rm32/eax
+ 399     00 86 00 00  # disp32 [label]
+ 400 # $6:
+ 401 # 292:
+ 402   # - if there's no character mapping, return
+ 403   3c 00  # compare al, 0
+ 404   74 13  # jump to epilogue if = [label]
+ 405 # 296:
+ 406   # - store al in keyboard buffer
+ 407   88  # copy r8 to m8 at r32
+ 408     81  # 10/mod/*+disp32 000/r8/al 001/rm32/ecx
+ 409     30 80 00 00  # disp32 [label]
+ 410 # 29c:
+ 411   # increment index
+ 412   fe  # increment byte
+ 413     05  # 00/mod/indirect 000/subop/increment 101/rm32/use-disp32
+ 414     28 80 00 00  # disp32 [label]
+ 415 # 2a2:
+ 416   # clear top nibble of index (keyboard buffer is circular)
+ 417   80  # and byte
+ 418     25  # 00/mod/indirect 100/subop/and 101/rm32/use-disp32
+ 419     28 80 00 00  # disp32 [label]
+ 420     0f  # imm8
+ 421 # 2a9:
+ 422   # epilogue
+ 423   61  # pop all registers
+ 424   fb  # enable interrupts
+ 425   cf  # iret
+ 426 
+ 427 # padding
+ 428 # 2ac:
+ 429                                     00 00 00 00
+ 430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 431 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 432 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 433 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 434 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 435 # 300:
+ 436 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 437 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 438 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 439 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 441 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 442 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 443 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 444 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 445 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 446 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 447 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 448 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 449 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 450 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 451 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 452 
+ 453 # offset 400 (address 0x8000): interrupt handler data
+ 454 # idt_descriptor:
+ 455   ff 03  # idt_end - idt_start - 1
+ 456   00 82 00 00  # start = idt_start [label]
+ 457 
+ 458 # padding
+ 459 # 406:
+ 460                   00 00 00 00 00 00 00 00 00 00
+ 461 
+ 462 # 410:
+ 463 # var shift: boolean
+ 464   00 00 00 00
+ 465 
+ 466 # padding
+ 467 # 414:
+ 468             00 00 00 00 00 00 00 00 00 00 00 00
+ 469 00 00 00 00 00 00 00 00
+ 470 
+ 471 # 428:
+ 472 # var keyboard circular buffer
+ 473 # write index: nibble
+ 474 # still take up 4 bytes so SubX can handle it
+ 475   00 00 00 00
+ 476 # 42c:
+ 477 # read index: nibble
+ 478 # still take up 4 bytes so SubX can handle it
+ 479   00 00 00 00
+ 480 # 430:
+ 481 # circular buffer: byte[16]
+ 482   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 483 
+ 484 # padding
+ 485 # 440:
+ 486 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 487 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 488 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 489 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 491 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 492 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 493 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 494 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 495 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 496 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 497 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 498 
+ 499 # offset 500 (address 0x8100):
+ 500 +-- 53 lines: # video mode info ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 553 
+ 554 # offset 600 (address 0x8200):
+ 555 +--161 lines: # interrupt descriptor table -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 716 
+ 717 ## the rest of this file has data
+ 718 
+ 719 # offset a00 (address 0x8600):
+ 720 +--152 lines: # translating keys to ASCII --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 872 
+ 873 # offset 1000 (address 0x8c00)
+ 874 +--236 lines: # Bitmaps for some ASCII characters (soon Unicode) ---------------------------------------------------------------------------------------------------------------------------------------------------
+1110 
+1111 # offset 1800 (address 0x9400)
+1112 
+1113 # vim:ft=subx
 
-- cgit 1.4.1-2-gfad0