From 117a710a9d04c7176ce192cdb26134d2828478f9 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sat, 16 Jan 2021 16:24:38 -0800 Subject: [PATCH] 7530 - baremetal: print ints to screen --- baremetal/117write-int-hex.subx | 202 +++++++++++++ baremetal/126write-int-decimal.subx | 428 +++++++++++++++++++++++++++ baremetal/313index-bounds-check.subx | 13 +- baremetal/400.mu | 14 +- baremetal/501draw-text.mu | 150 ++++++++++ baremetal/mu-init.subx | 2 + 6 files changed, 807 insertions(+), 2 deletions(-) create mode 100644 baremetal/117write-int-hex.subx create mode 100644 baremetal/126write-int-decimal.subx diff --git a/baremetal/117write-int-hex.subx b/baremetal/117write-int-hex.subx new file mode 100644 index 00000000..091bd874 --- /dev/null +++ b/baremetal/117write-int-hex.subx @@ -0,0 +1,202 @@ +# Write out the (hex) textual representation of numbers. + +== code +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +# convert the lowest nibble of eax to ascii and return it in the lowest byte of eax +to-hex-char: # in/eax: int -> out/eax: int + # no error checking; accepts argument in eax + # if (eax <= 9) return eax + '0' + 3d/compare-eax-with 0x9/imm32/9 + 7f/jump-if-> $to-hex-char:else/disp8 + 05/add-to-eax 0x30/imm32/0 + c3/return +$to-hex-char:else: + # otherwise return eax + 'a' - 10 + 05/add-to-eax 0x57/imm32/a-10 + c3/return + +append-byte-hex: # f: (addr stream byte), n: int + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + # AL = convert upper nibble to hex + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax + c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/eax . . . . . 4/imm8 # shift eax right by 4 bits, while padding zeroes + 25/and-eax 0xf/imm32 + # . AL = to-hex-char(AL) + e8/call to-hex-char/disp32 + # append-byte(f, AL) + # . . push args + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call append-byte/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # AL = convert lower nibble to hex + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax + 25/and-eax 0xf/imm32 + # . AL = to-hex-char(AL) + e8/call to-hex-char/disp32 + # append-byte(f, AL) + # . . push args + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call append-byte/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$append-byte-hex:end: + # . restore registers + 58/pop-to-eax + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-append-byte-hex: + # - check that append-byte-hex adds the hex textual representation + # setup + # . clear-stream(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # append-byte-hex(_test-stream, 0xa) # exercises digit, non-digit as well as leading zero + # . . push args + 68/push 0xa/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call append-byte-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "0a", msg) + # . . push args + 68/push "F - test-append-byte-hex"/imm32 + 68/push "0a"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +write-int32-hex: # f: (addr stream byte), n: int + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp +$write-int32-hex:hex-prefix: + # write(f, "0x") + # . . push args + 68/push "0x"/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$write-int32-hex:rest: + # write-int32-hex-bits(f, n, 32) + # . . push args + 68/push 0x20/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call write-int32-hex-bits/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$write-int32-hex:end: + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# print rightmost 'bits' of 'n' +# bits must be multiple of 4 +write-int32-hex-bits: # f: (addr stream byte), n: int, bits: int + # pseudocode: + # bits -= 4 + # while true + # if (bits < 0) break + # eax = n >> bits + # eax = eax & 0xf + # append-byte(f, AL) + # bits -= 4 + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + # ecx = bits-4 + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx + 81 5/subop/subtract 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # subtract from ecx +$write-int32-hex-bits:loop: + # if (bits < 0) break + 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0/imm32 # compare ecx + 7c/jump-if-< $write-int32-hex-bits:end/disp8 + # eax = n >> bits + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax + d3/>>ecx 5/subop/pad-zeroes 3/mod/direct 0/rm32/eax . . . . . . # shift eax right by ecx bits, padding zeroes + # eax = to-hex-char(AL) + 25/and-eax 0xf/imm32 + e8/call to-hex-char/disp32 + # append-byte(f, AL) + # . . push args + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call append-byte/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # bits -= 4 + 81 5/subop/subtract 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # subtract from ecx + eb/jump $write-int32-hex-bits:loop/disp8 +$write-int32-hex-bits:end: + # . restore registers + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-write-int32-hex: + # - check that write-int32-hex prints the hex textual representation + # setup + # . clear-stream(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # write-int32-hex(_test-stream, 0x8899aa) + # . . push args + 68/push 0x8899aa/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int32-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "0x008899aa", msg) + # . . push args + 68/push "F - test-write-int32-hex"/imm32 + 68/push "0x008899aa"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/baremetal/126write-int-decimal.subx b/baremetal/126write-int-decimal.subx new file mode 100644 index 00000000..eae331a1 --- /dev/null +++ b/baremetal/126write-int-decimal.subx @@ -0,0 +1,428 @@ +# Helper to print an int32 in decimal. + +== code +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +write-int32-decimal: # out: (addr stream byte), n: int + # works by generating characters from lowest to highest and pushing them + # to the stack, before popping them one by one into the stream + # + # pseudocode: + # push sentinel + # eax = abs(n) + # while true + # sign-extend eax into edx + # eax, edx = eax/10, eax%10 + # edx += '0' + # push edx + # if (eax == 0) break + # if n < 0 + # push '-' + # w = out->write + # curr = &out->data[out->write] + # max = &out->data[out->size] + # while true + # pop into eax + # if (eax == sentinel) break + # if (curr >= max) abort + # *curr = AL + # ++curr + # ++w + # out->write = w + # (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa) + # (this pseudocode contains registers because operations like division + # require specific registers in x86) + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 57/push-edi + # const ten/ecx = 10 + b9/copy-to-ecx 0xa/imm32 + # push sentinel + 68/push 0/imm32/sentinel + # var eax: int = abs(n) + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax + 3d/compare-eax-with 0/imm32 + 7d/jump-if->= $write-int32-decimal:read-loop/disp8 +$write-int32-decimal:negative: + f7 3/subop/negate 3/mod/direct 0/rm32/eax . . . . . . # negate eax +$write-int32-decimal:read-loop: + # eax, edx = eax / 10, eax % 10 + 99/sign-extend-eax-into-edx + f7 7/subop/idiv 3/mod/direct 1/rm32/ecx . . . . . . # divide edx:eax by ecx, storing quotient in eax and remainder in edx + # edx += '0' + 81 0/subop/add 3/mod/direct 2/rm32/edx . . . . . 0x30/imm32 # add to edx + # push edx + 52/push-edx + # if (eax == 0) break + 3d/compare-eax-and 0/imm32 + 7f/jump-if-> $write-int32-decimal:read-loop/disp8 +$write-int32-decimal:read-break: + # if (n < 0) push('-') + 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 0/imm32 # compare *(ebp+12) + 7d/jump-if->= $write-int32-decimal:write/disp8 +$write-int32-decimal:push-negative: + 68/push 0x2d/imm32/- +$write-int32-decimal:write: + # edi = out + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi + # var w/edx: int = out->write + 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx + # var curr/ecx: (addr byte) = &out->data[out->write] + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 1/r32/ecx 0xc/disp8 . # copy ebx+edx+12 to ecx + # var max/ebx: (addr byte) = &out->data[out->size] + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 8/disp8 . # copy *(edi+8) to ebx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 3/index/ebx . 3/r32/ebx 0xc/disp8 . # copy edi+ebx+12 to ebx +$write-int32-decimal:write-loop: + # pop into eax + 58/pop-to-eax + # if (eax == sentinel) break + 3d/compare-eax-and 0/imm32/sentinel + 74/jump-if-= $write-int32-decimal:write-break/disp8 + # if (curr >= max) abort + 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx + 73/jump-if-addr>= $write-int32-decimal:abort/disp8 +$write-int32-decimal:write-char: + # *curr = AL + 88/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy AL to byte at *ecx + # ++curr + 41/increment-ecx + # ++w + 42/increment-edx + eb/jump $write-int32-decimal:write-loop/disp8 +$write-int32-decimal:write-break: + # out->write = w + 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi +$write-int32-decimal:end: + # . restore registers + 5f/pop-to-edi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$write-int32-decimal:abort: + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "write-int32-decimal: stream out of space" 3) # 3=cyan + { + eb/jump loop/disp8 + } + # never gets here + +test-write-int32-decimal: + # - check that a single-digit number converts correctly + # setup + # . clear-stream(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # write-int32-decimal(_test-stream, 9) + # . . push args + 68/push 9/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "9", msg) + # . . push args + 68/push "F - test-write-int32-decimal"/imm32 + 68/push "9"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-write-int32-decimal-zero: + # - check that 0 converts correctly + # setup + # . clear-stream(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # write-int32-decimal(_test-stream, 0) + # . . push args + 68/push 0/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "0", msg) + # . . push args + 68/push "F - test-write-int32-decimal-zero"/imm32 + 68/push "0"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-write-int32-decimal-multiple-digits: + # - check that a multi-digit number converts correctly + # setup + # . clear-stream(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # write-int32-decimal(_test-stream, 10) + # . . push args + 68/push 0xa/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "10", msg) + # . . push args + 68/push "F - test-write-int32-decimal-multiple-digits"/imm32 + 68/push "10"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-write-int32-decimal-negative: + # - check that a negative single-digit number converts correctly + # setup + # . clear-stream(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # write-int32-decimal(_test-stream, -9) + # . . push args + 68/push -9/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # dump _test-stream {{{ +#? # . write(2/stderr, "^") +#? # . . push args +#? 68/push "^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, _test-stream) +#? # . . push args +#? 68/push _test-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} + # check-stream-equal(_test-stream, "-9", msg) + # . . push args + 68/push "F - test-write-int32-decimal-negative"/imm32 + 68/push "-9"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-write-int32-decimal-negative-multiple-digits: + # - check that a multi-digit number converts correctly + # setup + # . clear-stream(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # write-int32-decimal(_test-stream, -10) + # . . push args + 68/push -0xa/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "-10", msg) + # . . push args + 68/push "F - test-write-int32-decimal-negative-multiple-digits"/imm32 + 68/push "-10"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +is-decimal-digit?: # c: grapheme -> result/eax: boolean + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + # ecx = c + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # result = false + b8/copy-to-eax 0/imm32/false + # return false if c < '0' + 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x30/imm32 # compare ecx + 7c/jump-if-< $is-decimal-digit?:end/disp8 + # return (c <= '9') + 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x39/imm32 # compare ecx + 7f/jump-if-> $is-decimal-digit?:end/disp8 +$is-decimal-digit?:true: + b8/copy-to-eax 1/imm32/true +$is-decimal-digit?:end: + # . restore registers + 59/pop-to-ecx + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-decimal-digit-below-0: + # eax = is-decimal-digit?(0x2f) + # . . push args + 68/push 0x2f/imm32 + # . . call + e8/call is-decimal-digit?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-is-decimal-digit-below-0"/imm32 + 68/push 0/imm32/false + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + c3/return + +test-is-decimal-digit-0-to-9: + # eax = is-decimal-digit?(0x30) + # . . push args + 68/push 0x30/imm32 + # . . call + e8/call is-decimal-digit?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-is-decimal-digit-at-0"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # eax = is-decimal-digit?(0x39) + # . . push args + 68/push 0x39/imm32 + # . . call + e8/call is-decimal-digit?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-is-decimal-digit-at-9"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + c3/return + +test-is-decimal-digit-above-9: + # eax = is-decimal-digit?(0x3a) + # . . push args + 68/push 0x3a/imm32 + # . . call + e8/call is-decimal-digit?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-is-decimal-digit-above-9"/imm32 + 68/push 0/imm32/false + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + c3/return + +to-decimal-digit: # in: grapheme -> out/eax: int + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # eax = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax +$to-decimal-digit:check0: + # if (eax < '0') goto abort + 3d/compare-eax-with 0x30/imm32/0 + 7c/jump-if-< $to-decimal-digit:abort/disp8 +$to-decimal-digit:check1: + # if (eax > '9') goto abort + 3d/compare-eax-with 0x39/imm32/f + 7f/jump-if-> $to-decimal-digit:abort/disp8 +$to-decimal-digit:digit: + # return eax - '0' + 2d/subtract-from-eax 0x30/imm32/0 +$to-decimal-digit:end: + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$to-decimal-digit:abort: + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "to-decimal-digit: not a digit character" 3) # 3=cyan + { + eb/jump loop/disp8 + } + # never gets here + +# . . vim:nowrap:textwidth=0 diff --git a/baremetal/313index-bounds-check.subx b/baremetal/313index-bounds-check.subx index 8a7700c6..6970e23a 100644 --- a/baremetal/313index-bounds-check.subx +++ b/baremetal/313index-bounds-check.subx @@ -26,7 +26,13 @@ __check-mu-array-bounds: # index: int, elem-size: int, arr-size: int, function- 39/compare %eax 1/r32/ecx 0f 82/jump-if-unsigned< $__check-mu-array-bounds:end/disp32 # negative index should always abort # abort if necessary - (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "offset too large for array" 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "fn " 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+0x14) 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 ": offset " 3) # 3=cyan + (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 %eax 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 " is too large for array '" 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+0x18) 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "'" 3) # 3=cyan { eb/jump loop/disp8 } @@ -42,7 +48,12 @@ $__check-mu-array-bounds:end: c3/return __check-mu-array-bounds:overflow: + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "fn " 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+0x14) 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 ": offset to array '" 3) # 3=cyan (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "offset to array overflowed 32 bits" 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 *(ebp+0x18) 3) # 3=cyan + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "' overflowed 32 bits" 3) # 3=cyan { eb/jump loop/disp8 } diff --git a/baremetal/400.mu b/baremetal/400.mu index 94820e68..4dfb9951 100644 --- a/baremetal/400.mu +++ b/baremetal/400.mu @@ -15,8 +15,17 @@ sig num-test-failures -> _/eax: int sig clear-stream f: (addr stream _) sig rewind-stream f: (addr stream _) sig write f: (addr stream byte), s: (addr array byte) -sig append-byte f: (addr stream byte), n: int sig read-byte s: (addr stream byte) -> _/eax: byte +sig append-byte f: (addr stream byte), n: int +#sig to-hex-char in/eax: int -> out/eax: int +sig append-byte-hex f: (addr stream byte), n: int +sig write-int32-hex f: (addr stream byte), n: int +sig write-int32-hex-bits f: (addr stream byte), n: int, bits: int +sig is-hex-int? in: (addr slice) -> _/eax: boolean +sig parse-hex-int in: (addr array byte) -> _/eax: int +sig parse-hex-int-from-slice in: (addr slice) -> _/eax: int +#sig parse-hex-int-helper start: (addr byte), end: (addr byte) -> _/eax: int +sig is-hex-digit? c: byte -> _/eax: boolean #sig allocate ad: (addr allocation-descriptor), n: int, out: (addr handle _) #sig allocate-raw ad: (addr allocation-descriptor), n: int, out: (addr handle _) sig lookup h: (handle _T) -> _/eax: (addr _T) @@ -26,4 +35,7 @@ sig copy-handle src: (handle _T), dest: (addr handle _T) #sig allocate-array ad: (addr allocation-descriptor), n: int, out: (addr handle _) sig copy-array ad: (addr allocation-descriptor), src: (addr array _T), out: (addr handle array _T) #sig zero-out start: (addr byte), size: int +sig write-int32-decimal out: (addr stream byte), n: int +sig is-decimal-digit? c: grapheme -> _/eax: boolean +sig to-decimal-digit in: grapheme -> _/eax: int sig stream-empty? s: (addr stream _) -> _/eax: boolean diff --git a/baremetal/501draw-text.mu b/baremetal/501draw-text.mu index be8a06c5..5ab280db 100644 --- a/baremetal/501draw-text.mu +++ b/baremetal/501draw-text.mu @@ -184,6 +184,156 @@ fn draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr draw-text-wrapping-right-then-down-from-cursor screen, text, 0, 0, 0x400, 0x300, color # 1024, 768 } +fn draw-int32-hex-wrapping-right-then-down screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int -> _/eax: int, _/ecx: int { + var stream-storage: (stream byte 0x100) + var stream/esi: (addr stream byte) <- address stream-storage + write-int32-hex stream, n + # check if we have enough space + var xcurr/edx: int <- copy x + var ycurr/ecx: int <- copy y + { + compare ycurr, ymax + break-if->= + var g/eax: grapheme <- read-grapheme stream + compare g, 0xffffffff # end-of-file + break-if-= + xcurr <- add 8 # font-width + compare xcurr, xmax + { + break-if-< + xcurr <- copy xmin + ycurr <- add 0x10 # font-height + } + loop + } + compare ycurr, ymax + { + break-if-< + return 0, 0 + } + # we do; actually draw + rewind-stream stream + xcurr <- copy x + ycurr <- copy y + { + var g/eax: grapheme <- read-grapheme stream + compare g, 0xffffffff # end-of-file + break-if-= + draw-grapheme screen, g, xcurr, ycurr, color + xcurr <- add 8 # font-width + compare xcurr, xmax + { + break-if-< + xcurr <- copy xmin + ycurr <- add 0x10 # font-height + } + loop + } + set-cursor-position screen, xcurr, ycurr + return xcurr, ycurr +} + +fn draw-int32-hex-wrapping-right-then-down-over-full-screen screen: (addr screen), n: int, x: int, y: int, color: int -> _/eax: int, _/ecx: int { + var cursor-x/eax: int <- copy 0 + var cursor-y/ecx: int <- copy 0 + cursor-x, cursor-y <- draw-int32-hex-wrapping-right-then-down screen, n, 0, 0, 0x400, 0x300, x, y, color # 1024, 768 + return cursor-x, cursor-y +} + +fn draw-int32-hex-wrapping-right-then-down-from-cursor screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, color: int { + var cursor-x/eax: int <- copy 0 + var cursor-y/ecx: int <- copy 0 + cursor-x, cursor-y <- cursor-position screen + var end-x/edx: int <- copy cursor-x + end-x <- add 8 # font-width + compare end-x, xmax + { + break-if-< + cursor-x <- copy xmin + cursor-y <- add 0x10 # font-height + } + cursor-x, cursor-y <- draw-int32-hex-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color +} + +fn draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int { + draw-int32-hex-wrapping-right-then-down-from-cursor screen, n, 0, 0, 0x400, 0x300, color # 1024, 768 +} + +fn draw-int32-decimal-wrapping-right-then-down screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, x: int, y: int, color: int -> _/eax: int, _/ecx: int { + var stream-storage: (stream byte 0x100) + var stream/esi: (addr stream byte) <- address stream-storage + write-int32-decimal stream, n + # check if we have enough space + var xcurr/edx: int <- copy x + var ycurr/ecx: int <- copy y + { + compare ycurr, ymax + break-if->= + var g/eax: grapheme <- read-grapheme stream + compare g, 0xffffffff # end-of-file + break-if-= + xcurr <- add 8 # font-width + compare xcurr, xmax + { + break-if-< + xcurr <- copy xmin + ycurr <- add 0x10 # font-height + } + loop + } + compare ycurr, ymax + { + break-if-< + return 0, 0 + } + # we do; actually draw + rewind-stream stream + xcurr <- copy x + ycurr <- copy y + { + var g/eax: grapheme <- read-grapheme stream + compare g, 0xffffffff # end-of-file + break-if-= + draw-grapheme screen, g, xcurr, ycurr, color + xcurr <- add 8 # font-width + compare xcurr, xmax + { + break-if-< + xcurr <- copy xmin + ycurr <- add 0x10 # font-height + } + loop + } + set-cursor-position screen, xcurr, ycurr + return xcurr, ycurr +} + +fn draw-int32-decimal-wrapping-right-then-down-over-full-screen screen: (addr screen), n: int, x: int, y: int, color: int -> _/eax: int, _/ecx: int { + var cursor-x/eax: int <- copy 0 + var cursor-y/ecx: int <- copy 0 + cursor-x, cursor-y <- draw-int32-decimal-wrapping-right-then-down screen, n, 0, 0, 0x400, 0x300, x, y, color # 1024, 768 + return cursor-x, cursor-y +} + +fn draw-int32-decimal-wrapping-right-then-down-from-cursor screen: (addr screen), n: int, xmin: int, ymin: int, xmax: int, ymax: int, color: int { + var cursor-x/eax: int <- copy 0 + var cursor-y/ecx: int <- copy 0 + cursor-x, cursor-y <- cursor-position screen + var end-x/edx: int <- copy cursor-x + end-x <- add 8 # font-width + compare end-x, xmax + { + break-if-< + cursor-x <- copy xmin + cursor-y <- add 0x10 # font-height + } + cursor-x, cursor-y <- draw-int32-decimal-wrapping-right-then-down screen, n, xmin, ymin, xmax, ymax, cursor-x, cursor-y, color +} + +fn draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen: (addr screen), n: int, color: int { + draw-int32-decimal-wrapping-right-then-down-from-cursor screen, n, 0, 0, 0x400, 0x300, color # 1024, 768 +} + ## Text direction: down then right # draw a single line of text vertically from x, y to ymax diff --git a/baremetal/mu-init.subx b/baremetal/mu-init.subx index 5bf4a3bb..3c52a0b7 100644 --- a/baremetal/mu-init.subx +++ b/baremetal/mu-init.subx @@ -10,6 +10,8 @@ bd/copy-to-ebp 0/imm32 # always first run tests (run-tests) +c7 0/subop/copy *Default-next-x 0/imm32 +c7 0/subop/copy *Default-next-y 0/imm32 (num-test-failures) # => eax # call main if tests all passed {