https://github.com/akkartik/mu/blob/main/baremetal/103grapheme.subx
  1 draw-grapheme:  # screen: (addr screen), g: grapheme, x: int, y: int, color: int
  2     # . prologue
  3     55/push-ebp
  4     89/<- %ebp 4/r32/esp
  5     # . save registers
  6     50/push-eax
  7     51/push-ecx
  8     52/push-edx
  9     53/push-ebx
 10     56/push-esi
 11     # TODO: support fake screen; we currently assume 'screen' is always 0 (real)
 12     # var letter-bitmap/esi = font[g]
 13     8b/-> *(ebp+0xc) 6/r32/esi
 14     c1 4/subop/shift-left %esi 4/imm8
 15     8d/copy-address *(esi+0x8800) 6/r32/esi  # font-start
 16     # if (letter-bitmap >= 0x9000) return  # characters beyond ASCII currently not supported
 17     81 7/subop/compare %esi 0x9000/imm32
 18     7d/jump-if->= $draw-grapheme:end/disp8
 19     # edx = y
 20     8b/-> *(ebp+0x14) 2/r32/edx
 21     # var ymax/ebx: int = y + 16
 22     8b/-> *(ebp+0x14) 3/r32/ebx
 23     81 0/subop/add %ebx 0x10/imm32
 24     {
 25       # if (y >= ymax) break
 26       39/compare %edx 3/r32/ebx
 27       7d/jump-if->= break/disp8
 28       # eax = x + 7
 29       8b/-> *(ebp+0x10) 0/r32/eax
 30       81 0/subop/add %eax 7/imm32
 31       # var xmin/ecx: int = x
 32       8b/-> *(ebp+0x10) 1/r32/ecx
 33       # var row-bitmap/ebx: int = *letter-bitmap
 34       53/push-ebx
 35       8b/-> *esi 3/r32/ebx
 36       {
 37         # if (x < xmin) break
 38         39/compare %eax 1/r32/ecx
 39         7c/jump-if-< break/disp8
 40         # shift LSB from row-bitmap into carry flag (CF)
 41         c1 5/subop/shift-right-logical %ebx 1/imm8
 42         # if LSB, draw a pixel
 43         {
 44           73/jump-if-not-CF break/disp8
 45           (pixel *(ebp+8) %eax %edx *(ebp+0x18))
 46         }
 47         # --x
 48         48/decrement-eax
 49         #
 50         eb/jump loop/disp8
 51       }
 52       # reclaim row-bitmap
 53       5b/pop-to-ebx
 54       # ++y
 55       42/increment-edx
 56       # next bitmap row
 57       46/increment-esi
 58       #
 59       eb/jump loop/disp8
 60     }
 61 $draw-grapheme:end:
 62     # . restore registers
 63     5e/pop-to-esi
 64     5b/pop-to-ebx
 65     5a/pop-to-edx
 66     59/pop-to-ecx
 67     58/pop-to-eax
 68     # . epilogue
 69     89/<- %esp 5/r32/ebp
 70     5d/pop-to-ebp
 71     c3/return
 72 
 73 cursor-position:  # screen: (addr screen) -> _/eax: int, _/ecx: int
 74     # . prologue
 75     55/push-ebp
 76     89/<- %ebp 4/r32/esp
 77     # TODO: support fake screen; we currently assume 'screen' is always 0 (real)
 78     8b/-> *Default-next-x 0/r32/eax
 79     8b/-> *Default-next-y 1/r32/ecx
 80 $cursor-position:end:
 81     # . epilogue
 82     89/<- %esp 5/r32/ebp
 83     5d/pop-to-ebp
 84     c3/return
 85 
 86 set-cursor-position:  # screen: (addr screen), x: int, y: int
 87     # . prologue
 88     55/push-ebp
 89     89/<- %ebp 4/r32/esp
 90     # . save registers
 91     50/push-eax
 92     # TODO: support fake screen; we currently assume 'screen' is always 0 (real)
 93     8b/-> *(ebp+0xc) 0/r32/eax
 94     89/<- *Default-next-x 0/r32/eax
 95     8b/-> *(ebp+0x10) 0/r32/eax
 96     89/<- *Default-next-y 0/r32/eax
 97 $set-cursor-position:end:
 98     # . restore registers
 99     58/pop-to-eax
100     # . epilogue
101     89/<- %esp 5/r32/ebp
102     5d/pop-to-ebp
103     c3/return
104 
105 == data
106 
107 Default-next-x:
108   0/imm32
109 
110 Default-next-y:
111   0/imm32