From ead6c01fa4f9291ecbac9e6a4567be87cb0e4a8b Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Thu, 11 Feb 2021 20:59:39 -0800 Subject: 7724 - baremetal: rendering array values --- baremetal/118parse-hex-int.subx | 897 ++++++++++++++++++++++++++++++++++++++++ baremetal/301array-equal.subx | 432 +++++++++++++++++++ baremetal/311decimal-int.subx | 206 +++++++++ baremetal/400.mu | 4 + baremetal/shell/value.mu | 105 +++++ 5 files changed, 1644 insertions(+) create mode 100644 baremetal/118parse-hex-int.subx create mode 100644 baremetal/301array-equal.subx diff --git a/baremetal/118parse-hex-int.subx b/baremetal/118parse-hex-int.subx new file mode 100644 index 00000000..153def3c --- /dev/null +++ b/baremetal/118parse-hex-int.subx @@ -0,0 +1,897 @@ +# some utilities for converting numbers from hex +# lowercase letters only for now + +== 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 + +is-hex-int?: # in: (addr slice) -> 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 + 52/push-edx + 53/push-ebx + # ecx = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # edx = s->end + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx + # var curr/ecx: (addr byte) = s->start + 8b/copy 0/mod/indirect 1/rm32/ecx . . . 1/r32/ecx . . # copy *ecx to ecx + # if s is empty return false + b8/copy-to-eax 0/imm32/false + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-addr>= $is-hex-int?:end/disp8 + # skip past leading '-' + # . if (*curr == '-') ++curr + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 3/r32/BL . . # copy byte at *ecx to BL + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x2d/imm32/- # compare ebx + 75/jump-if-!= $is-hex-int?:initial-0/disp8 + # . ++curr + 41/increment-ecx + # skip past leading '0x' +$is-hex-int?:initial-0: + # . if (*curr != '0') jump to loop + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 3/r32/BL . . # copy byte at *ecx to BL + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x30/imm32/0 # compare ebx + 75/jump-if-!= $is-hex-int?:loop/disp8 + # . ++curr + 41/increment-ecx +$is-hex-int?:initial-0x: + # . if (curr >= in->end) return true + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-addr>= $is-hex-int?:true/disp8 + # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 3/r32/BL . . # copy byte at *ecx to BL + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x78/imm32/x # compare ebx + 75/jump-if-!= $is-hex-int?:loop/disp8 + # . ++curr + 41/increment-ecx +$is-hex-int?:loop: + # if (curr >= in->end) return true + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-addr>= $is-hex-int?:true/disp8 + # var eax: boolean = is-hex-digit?(*curr) + # . . push args + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + 50/push-eax + # . . call + e8/call is-hex-digit?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # if (eax == false) return false + 3d/compare-eax-and 0/imm32/false + 74/jump-if-= $is-hex-int?:end/disp8 + # ++curr + 41/increment-ecx + # loop + eb/jump $is-hex-int?:loop/disp8 +$is-hex-int?:true: + # return true + b8/copy-to-eax 1/imm32/true +$is-hex-int?:end: + # . restore registers + 5b/pop-to-ebx + 5a/pop-to-edx + 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-hex-int: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "34" + b8/copy-to-eax "34"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-hex-int?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-hex-int?/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-hex-int"/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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-hex-int-handles-letters: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "34a" + b8/copy-to-eax "34a"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-hex-int?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-hex-int?/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-hex-int-handles-letters"/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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-hex-int-with-trailing-char: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "34q" + b8/copy-to-eax "34q"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-hex-int?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-hex-int?/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-hex-int-with-trailing-char"/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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-hex-int-with-leading-char: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "q34" + b8/copy-to-eax "q34"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-hex-int?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-hex-int?/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-hex-int-with-leading-char"/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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-hex-int-empty: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var slice/ecx: slice = "" + 68/push 0/imm32 + 68/push 0/imm32 + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-hex-int?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-hex-int?/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-hex-int-empty"/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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-hex-int-handles-0x-prefix: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "0x3a" + b8/copy-to-eax "0x3a"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-hex-int?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-hex-int?/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-hex-int-handles-0x-prefix"/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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-hex-int-handles-negative: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "-34a" + b8/copy-to-eax "-34a"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-hex-int?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-hex-int?/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-hex-int-handles-negative"/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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-hex-int-handles-negative-0x-prefix: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "-0x3a" + b8/copy-to-eax "-0x3a"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-hex-int?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-hex-int?/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-hex-int-handles-negative-0x-prefix"/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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +parse-hex-int: # in: (addr array byte) -> result/eax: int + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + # eax = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax + # var curr/ecx: (addr byte) = &in->data + 8d/copy-address 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 4/disp8 . # copy eax+4 to ecx + # var max/edx: (addr byte) = &in->data[in->size] + # . edx = in->size + 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx + # . edx = in->data + in->size + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx + # return parse-hex-int-helper(curr, max) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call parse-hex-int-helper/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$parse-hex-int:end: + # . restore registers + 5a/pop-to-edx + 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 + +parse-hex-int-from-slice: # in: (addr slice) -> result/eax: int + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + # ecx = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # var max/edx: (addr byte) = in->end + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx + # var curr/ecx: (addr byte) = in->start + 8b/copy 0/mod/indirect 1/rm32/ecx . . . 1/r32/ecx . . # copy *ecx to ecx + # return parse-hex-int-helper(curr, max) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call parse-hex-int-helper/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$parse-hex-int-from-slice:end: + # . restore registers + 5a/pop-to-edx + 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 + +parse-hex-int-helper: # start: (addr byte), end: (addr byte) -> result/eax: int + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + # var curr/ecx: (addr byte) = start + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # edx = max + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx + # var result/ebx: int = 0 + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx + # var negate?/esi: boolean = false + 31/xor 3/mod/direct 6/rm32/esi . . . 6/r32/esi . . # clear esi +$parse-hex-int-helper:negative: + # if (*curr == '-') ++curr, negate = true + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + 3d/compare-eax-and 0x2d/imm32/- + 75/jump-if-!= $parse-hex-int-helper:initial-0/disp8 + # . ++curr + 41/increment-ecx + # . negate = true + be/copy-to-esi 1/imm32/true +$parse-hex-int-helper:initial-0: + # skip past leading '0x' + # . if (*curr != '0') jump to loop + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + 3d/compare-eax-and 0x30/imm32/0 + 75/jump-if-!= $parse-hex-int-helper:loop/disp8 + # . ++curr + 41/increment-ecx +$parse-hex-int-helper:initial-0x: + # . if (curr >= in->end) return result + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-addr>= $parse-hex-int-helper:end/disp8 + # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + 3d/compare-eax-and 0x78/imm32/x + 75/jump-if-!= $parse-hex-int-helper:loop/disp8 + # . ++curr + 41/increment-ecx +$parse-hex-int-helper:loop: + # if (curr >= in->end) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-addr>= $parse-hex-int-helper:negate/disp8 + # var eax: int = from-hex-char(*curr) + # . . copy arg to eax + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + # . . call + e8/call from-hex-char/disp32 + # result = result * 16 + eax + c1/shift 4/subop/left 3/mod/direct 3/rm32/ebx . . . . . 4/imm8 # shift ebx left by 4 bits + 01/add 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # add eax to ebx + # ++curr + 41/increment-ecx + # loop + eb/jump $parse-hex-int-helper:loop/disp8 +$parse-hex-int-helper:negate: + # if (negate?) result = -result + 81 7/subop/compare 3/mod/direct 6/rm32/esi . . . . . 0/imm32/false # compare esi + 74/jump-if-= $parse-hex-int-helper:end/disp8 + f7 3/subop/negate 3/mod/direct 3/rm32/ebx . . . . . . # negate ebx +$parse-hex-int-helper:end: + # return result + 89/copy 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # copy ebx to eax + # . restore registers + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 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-parse-hex-int-from-slice-single-digit: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "a" + b8/copy-to-eax "a"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = parse-hex-int-from-slice(slice) + # . . push args + 51/push-ecx + # . . call + e8/call parse-hex-int-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0xa, msg) + # . . push args + 68/push "F - test-parse-hex-int-from-slice-single-digit"/imm32 + 68/push 0xa/imm32 + 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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-hex-int-from-slice-multi-digit: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "34a" + b8/copy-to-eax "34a"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = parse-hex-int-from-slice(slice) + # . . push args + 51/push-ecx + # . . call + e8/call parse-hex-int-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0x34a, msg) + # . . push args + 68/push "F - test-parse-hex-int-from-slice-multi-digit"/imm32 + 68/push 0x34a/imm32 + 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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-hex-int-from-slice-0x-prefix: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "0x34" + b8/copy-to-eax "0x34"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = parse-hex-int-from-slice(slice) + # . . push args + 51/push-ecx + # . . call + e8/call parse-hex-int-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0x34, msg) + # . . push args + 68/push "F - test-parse-hex-int-from-slice-0x-prefix"/imm32 + 68/push 0x34/imm32 + 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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-hex-int-from-slice-zero: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "0" + b8/copy-to-eax "0"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = parse-hex-int-from-slice(slice) + # . . push args + 51/push-ecx + # . . call + e8/call parse-hex-int-from-slice/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-parse-hex-int-from-slice-zero"/imm32 + 68/push 0/imm32 + 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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-hex-int-from-slice-0-prefix: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "03" + b8/copy-to-eax "03"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = parse-hex-int-from-slice(slice) + # . . push args + 51/push-ecx + # . . call + e8/call parse-hex-int-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0x3, msg) + # . . push args + 68/push "F - test-parse-hex-int-from-slice-0-prefix"/imm32 + 68/push 0x3/imm32 + 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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-hex-int-from-slice-negative: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "-03" + b8/copy-to-eax "-03"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = parse-hex-int-from-slice(slice) + # . . push args + 51/push-ecx + # . . call + e8/call parse-hex-int-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, -3, msg) + # . . push args + 68/push "F - test-parse-hex-int-from-slice-negative"/imm32 + 68/push -3/imm32 + 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 + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +is-hex-digit?: # c: byte -> 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 + # return false if c < '0' + 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x30/imm32 # compare ecx + 7c/jump-if-< $is-hex-digit?:false/disp8 + # return true if c <= '9' + 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x39/imm32 # compare ecx + 7e/jump-if-<= $is-hex-digit?:true/disp8 + # drop case + 25/and-eax-with 0x5f/imm32 + # return false if c > 'f' + 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x66/imm32 # compare ecx + 7f/jump-if-> $is-hex-digit?:false/disp8 + # return true if c >= 'a' + 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x61/imm32 # compare ecx + 7d/jump-if->= $is-hex-digit?:true/disp8 + # otherwise return false +$is-hex-digit?:false: + b8/copy-to-eax 0/imm32/false + eb/jump $is-hex-digit?:end/disp8 +$is-hex-digit?:true: + b8/copy-to-eax 1/imm32/true +$is-hex-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-hex-below-0: + # eax = is-hex-digit?(0x2f) + # . . push args + 68/push 0x2f/imm32 + # . . call + e8/call is-hex-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-hex-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-hex-0-to-9: + # eax = is-hex-digit?(0x30) + # . . push args + 68/push 0x30/imm32 + # . . call + e8/call is-hex-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-hex-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-hex-digit?(0x39) + # . . push args + 68/push 0x39/imm32 + # . . call + e8/call is-hex-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-hex-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-hex-above-9-to-a: + # eax = is-hex-digit?(0x3a) + # . . push args + 68/push 0x3a/imm32 + # . . call + e8/call is-hex-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-hex-above-9-to-a"/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-hex-a-to-f: + # eax = is-hex-digit?(0x61) + # . . push args + 68/push 0x61/imm32 + # . . call + e8/call is-hex-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-hex-at-a"/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-hex-digit?(0x66) + # . . push args + 68/push 0x66/imm32 + # . . call + e8/call is-hex-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-hex-at-f"/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-hex-above-f: + # eax = is-hex-digit?(0x67) + # . . push args + 68/push 0x67/imm32 + # . . call + e8/call is-hex-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-hex-above-f"/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 + +from-hex-char: # in/eax: byte -> out/eax: nibble +$from-hex-char:check0: + # if (eax < '0') goto abort + 3d/compare-eax-with 0x30/imm32/0 + 7c/jump-if-< $from-hex-char:abort/disp8 +$from-hex-char:check1: + # if (eax > 'f') goto abort + 3d/compare-eax-with 0x66/imm32/f + 7f/jump-if-> $from-hex-char:abort/disp8 +$from-hex-char:check2: + # if (eax > '9') goto next check + 3d/compare-eax-with 0x39/imm32/9 + 7f/jump-if-> $from-hex-char:check3/disp8 +$from-hex-char:digit: + # return eax - '0' + 2d/subtract-from-eax 0x30/imm32/0 + c3/return +$from-hex-char:check3: + # if (eax < 'a') goto abort + 3d/compare-eax-with 0x61/imm32/a + 7c/jump-if-< $from-hex-char:abort/disp8 +$from-hex-char:letter: + # return eax - ('a'-10) + 2d/subtract-from-eax 0x57/imm32/a-10 + c3/return + +$from-hex-char:abort: + (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "invalid hex char" 3 0) # 3=cyan + { + eb/jump loop/disp8 + } + # never gets here + +# . . vim:nowrap:textwidth=0 diff --git a/baremetal/301array-equal.subx b/baremetal/301array-equal.subx new file mode 100644 index 00000000..fd46ccb6 --- /dev/null +++ b/baremetal/301array-equal.subx @@ -0,0 +1,432 @@ +# Comparing arrays of numbers. + +== code + +array-equal?: # a: (addr array int), b: (addr array int) -> result/eax: boolean + # pseudocode: + # asize = a->size + # if (asize != b->size) return false + # i = 0 + # curra = a->data + # currb = b->data + # while i < asize + # i1 = *curra + # i2 = *currb + # if (c1 != c2) return false + # i+=4, curra+=4, currb+=4 + # return true + # + # registers: + # i: ecx + # asize: edx + # curra: esi + # currb: edi + # i1: eax + # i2: ebx + # + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # esi = a + 8b/-> *(ebp+8) 6/r32/esi + # edi = b + 8b/-> *(ebp+0xc) 7/r32/edi + # var asize/edx: int = a->size + 8b/-> *esi 2/r32/edx +$array-equal?:sizes: + # if (asize != b->size) return false + 39/compare *edi 2/r32/edx + 75/jump-if-!= $array-equal?:false/disp8 + # var curra/esi: (addr byte) = a->data + 81 0/subop/add %esi 4/imm32 + # var currb/edi: (addr byte) = b->data + 81 0/subop/add %edi 4/imm32 + # var i/ecx: int = 0 + 31/xor-with %ecx 1/r32/ecx + # var vala/eax: int + # var valb/ebx: int +$array-equal?:loop: + # if (i >= asize) return true + 39/compare %ecx 2/r32/edx + 7d/jump-if->= $array-equal?:true/disp8 + # var vala/eax: int = *curra + 8b/-> *esi 0/r32/eax + # var valb/ebx: int = *currb + 8b/-> *edi 3/r32/ebx + # if (vala != valb) return false + 39/compare %eax 3/r32/ebx + 75/jump-if-!= $array-equal?:false/disp8 + # i += 4 + 81 0/subop/add %ecx 4/imm32 + # currs += 4 + 81 0/subop/add %esi 4/imm32 + # currb += 4 + 81 0/subop/add %edi 4/imm32 + eb/jump $array-equal?:loop/disp8 +$array-equal?:true: + b8/copy-to-eax 1/imm32 + eb/jump $array-equal?:end/disp8 +$array-equal?:false: + b8/copy-to-eax 0/imm32 +$array-equal?:end: + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-compare-empty-with-empty-array: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var ecx: (array _) = [] + 68/push 0/imm32/size + 89/<- %ecx 4/r32/esp + # var edx: (array _) = [] + 68/push 0/imm32/size + 89/<- %edx 4/r32/esp + # + (array-equal? %ecx %edx) # => eax + (check-ints-equal %eax 1 "F - test-compare-empty-with-empty-array") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-compare-empty-with-non-empty-array: # also checks size-mismatch code path + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var ecx: (array int) = [1] + 68/push 1/imm32 + 68/push 4/imm32/size + 89/<- %ecx 4/r32/esp + # var edx: (array int) = [] + 68/push 0/imm32/size + 89/<- %edx 4/r32/esp + # + (array-equal? %ecx %edx) # => eax + (check-ints-equal %eax 0 "F - test-compare-empty-with-non-empty-array") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-compare-equal-arrays: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var ecx: (array int) = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %ecx 4/r32/esp + # var edx: (array int) = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %edx 4/r32/esp + # + (array-equal? %ecx %edx) # => eax + (check-ints-equal %eax 1 "F - test-compare-equal-arrays") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-compare-inequal-arrays-equal-sizes: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var ecx: (array int) = [1, 4, 3] + 68/push 3/imm32 + 68/push 4/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %ecx 4/r32/esp + # var edx: (array int) = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %edx 4/r32/esp + # + (array-equal? %ecx %edx) # => eax + (check-ints-equal %eax 0 "F - test-compare-inequal-arrays-equal-sizes") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +_parse-array-of-ints: # ad: (addr allocation-descriptor), s: (addr array byte), out: (addr handle array int) + # pseudocode + # end = &s->data[s->size] + # curr = s->data + # size = 0 + # while true + # if (curr >= end) break + # curr = skip-chars-matching-in-slice(curr, end, ' ') + # if (curr >= end) break + # curr = skip-chars-not-matching-in-slice(curr, end, ' ') + # ++size + # allocate-array(ad, size*4, out) + # var slice: slice = {s->data, 0} + # curr = lookup(out)->data + # while true + # if (slice->start >= end) break + # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') + # if (slice->start >= end) break + # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') + # *curr = parse-hex-int-from-slice(slice) + # curr += 4 + # slice->start = slice->end + # return result + # + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # esi = s + 8b/-> *(ebp+0xc) 6/r32/esi + # var curr/ecx: (addr byte) = s->data + 8d/copy-address *(esi+4) 1/r32/ecx + # var end/edx: (addr byte) = &s->data[s->size] + # . edx = s->size + 8b/-> *esi 2/r32/edx + # . edx += curr + 01/add-to %edx 1/r32/ecx + # var size/ebx: int = 0 + 31/xor-with %ebx 3/r32/ebx +$_parse-array-of-ints:loop1: + # if (curr >= end) break + 39/compare %ecx 2/r32/edx + 73/jump-if-addr>= $_parse-array-of-ints:break1/disp8 + # curr = skip-chars-matching-in-slice(curr, end, ' ') + (skip-chars-matching-in-slice %ecx %edx 0x20) # => eax + 89/<- %ecx 0/r32/eax + # if (curr >= end) break + 39/compare %ecx 2/r32/edx + 73/jump-if-addr>= $_parse-array-of-ints:break1/disp8 + # curr = skip-chars-not-matching-in-slice(curr, end, ' ') + (skip-chars-not-matching-in-slice %ecx %edx 0x20) # => eax + 89/<- %ecx 0/r32/eax + # size += 4 + 81 0/subop/add %ebx 4/imm32 + eb/jump $_parse-array-of-ints:loop1/disp8 +$_parse-array-of-ints:break1: + (allocate-array *(ebp+8) %ebx *(ebp+0x10)) +$_parse-array-of-ints:pass2: + # var slice/edi: slice = {s->data, 0} + 68/push 0/imm32/end + 8d/copy-address *(esi+4) 7/r32/edi + 57/push-edi + 89/<- %edi 4/r32/esp + # curr = lookup(out)->data + 8b/-> *(ebp+0x10) 0/r32/eax + (lookup *eax *(eax+4)) # => eax + 8d/copy-address *(eax+4) 1/r32/ecx +$_parse-array-of-ints:loop2: + # if (slice->start >= end) break + 39/compare *edi 2/r32/edx + 73/jump-if-addr>= $_parse-array-of-ints:end/disp8 + # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') + (skip-chars-matching-in-slice *edi %edx 0x20) # => eax + 89/<- *edi 0/r32/eax + # if (slice->start >= end) break + 39/compare *edi 2/r32/edx + 73/jump-if-addr>= $_parse-array-of-ints:end/disp8 + # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') + (skip-chars-not-matching-in-slice *edi %edx 0x20) # => eax + 89/<- *(edi+4) 0/r32/eax + # *curr = parse-hex-int-from-slice(slice) + (parse-hex-int-from-slice %edi) + 89/<- *ecx 0/r32/eax + # curr += 4 + 81 0/subop/add %ecx 4/imm32 + # slice->start = slice->end + 8b/-> *(edi+4) 0/r32/eax + 89/<- *edi 0/r32/eax + eb/jump $_parse-array-of-ints:loop2/disp8 +$_parse-array-of-ints:end: + # . reclaim locals + 81 0/subop/add %esp 8/imm32 + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-ints: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var h/esi: (handle array int) + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # var ecx: (array int) = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %ecx 4/r32/esp + # + (_parse-array-of-ints Heap "1 2 3" %esi) + (lookup *esi *(esi+4)) # => eax + (array-equal? %ecx %eax) # => eax + (check-ints-equal %eax 1 "F - test-parse-array-of-ints") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-ints-empty: + # - empty string = empty array + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var h/esi: handle + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # + (_parse-array-of-ints Heap "" %esi) + (lookup *esi *(esi+4)) # => eax + (check-ints-equal *eax 0 "F - test-parse-array-of-ints-empty") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-ints-just-whitespace: + # - just whitespace = empty array + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var h/esi: handle + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # + (_parse-array-of-ints Heap Space %esi) + (lookup *esi *(esi+4)) # => eax + (check-ints-equal *eax 0 "F - test-parse-array-of-ints-just-whitespace") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-ints-extra-whitespace: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var h/esi: handle + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # var ecx: (array int) = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %ecx 4/r32/esp + # + (_parse-array-of-ints Heap " 1 2 3 " %esi) + (lookup *esi *(esi+4)) # => eax + (array-equal? %ecx %eax) # => eax + (check-ints-equal %eax 1 "F - test-parse-array-of-ints-extra-whitespace") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +parse-array-of-ints: # s: (addr array byte), out: (addr handle array int) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # + (_parse-array-of-ints Heap *(ebp+8) *(ebp+0xc)) +$parse-array-of-ints:end: + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +# helper for later tests +# compare an array with a string representation of an array literal +check-array-equal: # a: (addr array int), expected: (addr string), msg: (addr string) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 50/push-eax + 56/push-esi + # var h/esi: handle + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # var b/eax: (addr array int) = parse-array-of-ints(Heap, expected) + (parse-array-of-ints *(ebp+0xc) %esi) + (lookup *esi *(esi+4)) # => eax + # + (array-equal? *(ebp+8) %eax) + (check-ints-equal %eax 1 *(ebp+0x10)) +$check-array-equal:end: + # . restore registers + 5e/pop-to-esi + 58/pop-to-eax + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-check-array-equal: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var ecx: (array int) = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %ecx 4/r32/esp + # + (check-array-equal %ecx "1 2 3" "F - test-check-array-equal") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +== data + +# length-prefixed string containing just a single space +Space: # (array byte) + # size: int + 1/imm32 + # data + 20/space diff --git a/baremetal/311decimal-int.subx b/baremetal/311decimal-int.subx index 9434ce72..d0d8dde7 100644 --- a/baremetal/311decimal-int.subx +++ b/baremetal/311decimal-int.subx @@ -425,3 +425,209 @@ $test-decimal-size-multi-digit-negative:end: 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return + +_parse-array-of-decimal-ints: # ad: (addr allocation-descriptor), s: (addr array byte), out: (addr handle array int) + # pseudocode + # end = &s->data[s->size] + # curr = s->data + # size = 0 + # while true + # if (curr >= end) break + # curr = skip-chars-matching-in-slice(curr, end, ' ') + # if (curr >= end) break + # curr = skip-chars-not-matching-in-slice(curr, end, ' ') + # ++size + # allocate-array(ad, size*4, out) + # var slice: slice = {s->data, 0} + # curr = lookup(out)->data + # while true + # if (slice->start >= end) break + # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') + # if (slice->start >= end) break + # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') + # *curr = parse-hex-int-from-slice(slice) + # curr += 4 + # slice->start = slice->end + # return result + # + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # esi = s + 8b/-> *(ebp+0xc) 6/r32/esi + # var curr/ecx: (addr byte) = s->data + 8d/copy-address *(esi+4) 1/r32/ecx + # var end/edx: (addr byte) = &s->data[s->size] + # . edx = s->size + 8b/-> *esi 2/r32/edx + # . edx += curr + 01/add-to %edx 1/r32/ecx + # var size/ebx: int = 0 + 31/xor-with %ebx 3/r32/ebx +$_parse-array-of-decimal-ints:loop1: + # if (curr >= end) break + 39/compare %ecx 2/r32/edx + 73/jump-if-addr>= $_parse-array-of-decimal-ints:break1/disp8 + # curr = skip-chars-matching-in-slice(curr, end, ' ') + (skip-chars-matching-in-slice %ecx %edx 0x20) # => eax + 89/<- %ecx 0/r32/eax + # if (curr >= end) break + 39/compare %ecx 2/r32/edx + 73/jump-if-addr>= $_parse-array-of-decimal-ints:break1/disp8 + # curr = skip-chars-not-matching-in-slice(curr, end, ' ') + (skip-chars-not-matching-in-slice %ecx %edx 0x20) # => eax + 89/<- %ecx 0/r32/eax + # size += 4 + 81 0/subop/add %ebx 4/imm32 + eb/jump $_parse-array-of-decimal-ints:loop1/disp8 +$_parse-array-of-decimal-ints:break1: + (allocate-array *(ebp+8) %ebx *(ebp+0x10)) +$_parse-array-of-decimal-ints:pass2: + # var slice/edi: slice = {s->data, 0} + 68/push 0/imm32/end + 8d/copy-address *(esi+4) 7/r32/edi + 57/push-edi + 89/<- %edi 4/r32/esp + # curr = lookup(out)->data + 8b/-> *(ebp+0x10) 0/r32/eax + (lookup *eax *(eax+4)) # => eax + 8d/copy-address *(eax+4) 1/r32/ecx +$_parse-array-of-decimal-ints:loop2: + # if (slice->start >= end) break + 39/compare *edi 2/r32/edx + 73/jump-if-addr>= $_parse-array-of-decimal-ints:end/disp8 + # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') + (skip-chars-matching-in-slice *edi %edx 0x20) # => eax + 89/<- *edi 0/r32/eax + # if (slice->start >= end) break + 39/compare *edi 2/r32/edx + 73/jump-if-addr>= $_parse-array-of-decimal-ints:end/disp8 + # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') + (skip-chars-not-matching-in-slice *edi %edx 0x20) # => eax + 89/<- *(edi+4) 0/r32/eax + # *curr = parse-hex-int-from-slice(slice) + (parse-decimal-int-from-slice %edi) + 89/<- *ecx 0/r32/eax + # curr += 4 + 81 0/subop/add %ecx 4/imm32 + # slice->start = slice->end + 8b/-> *(edi+4) 0/r32/eax + 89/<- *edi 0/r32/eax + eb/jump $_parse-array-of-decimal-ints:loop2/disp8 +$_parse-array-of-decimal-ints:end: + # . reclaim locals + 81 0/subop/add %esp 8/imm32 + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-decimal-ints: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var h/esi: (handle array int) + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # var ecx: (array int) = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %ecx 4/r32/esp + # + (_parse-array-of-decimal-ints Heap "1 2 3" %esi) + (lookup *esi *(esi+4)) # => eax + (array-equal? %ecx %eax) # => eax + (check-ints-equal %eax 1 "F - test-parse-array-of-decimal-ints") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-decimal-ints-empty: + # - empty string = empty array + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var h/esi: handle + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # + (_parse-array-of-decimal-ints Heap "" %esi) + (lookup *esi *(esi+4)) # => eax + (check-ints-equal *eax 0 "F - test-parse-array-of-decimal-ints-empty") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-decimal-ints-just-whitespace: + # - just whitespace = empty array + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var h/esi: handle + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # + (_parse-array-of-decimal-ints Heap Space %esi) + (lookup *esi *(esi+4)) # => eax + (check-ints-equal *eax 0 "F - test-parse-array-of-decimal-ints-just-whitespace") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-decimal-ints-extra-whitespace: + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # var h/esi: handle + 68/push 0/imm32 + 68/push 0/imm32 + 89/<- %esi 4/r32/esp + # var ecx: (array int) = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/<- %ecx 4/r32/esp + # + (_parse-array-of-decimal-ints Heap " 1 2 3 " %esi) + (lookup *esi *(esi+4)) # => eax + (array-equal? %ecx %eax) # => eax + (check-ints-equal %eax 1 "F - test-parse-array-of-decimal-ints-extra-whitespace") + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +parse-array-of-decimal-ints: # s: (addr array byte), out: (addr handle array int) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # + (_parse-array-of-decimal-ints Heap *(ebp+8) *(ebp+0xc)) +$parse-array-of-decimal-ints:end: + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return diff --git a/baremetal/400.mu b/baremetal/400.mu index 99195b2c..3a5e2174 100644 --- a/baremetal/400.mu +++ b/baremetal/400.mu @@ -67,5 +67,9 @@ sig stream-final s: (addr stream byte) -> _/eax: byte #sig copy-bytes src: (addr byte), dest: (addr byte), n: int sig copy-array-object src: (addr array _), dest-ah: (addr handle array _) +sig array-equal? a: (addr array int), b: (addr array int) -> _/eax: boolean +sig parse-array-of-ints s: (addr array byte), out: (addr handle array int) +sig parse-array-of-decimal-ints s: (addr array byte), out: (addr handle array int) +sig check-array-equal a: (addr array int), expected: (addr string), msg: (addr string) sig integer-divide a: int, b: int -> _/eax: int, _/edx: int diff --git a/baremetal/shell/value.mu b/baremetal/shell/value.mu index 64b1d772..4a93227e 100644 --- a/baremetal/shell/value.mu +++ b/baremetal/shell/value.mu @@ -6,6 +6,35 @@ type value { array-data: (handle array value) # if type = 2 } +# top-level? is a hack just for numbers +# we'll eventually need to return a y coordinate as well to render 2D values +fn render-value screen: (addr screen), _val: (addr value), x: int, y: int, top-level?: boolean -> _/eax: int { + var val/esi: (addr value) <- copy _val + var val-type/ecx: (addr int) <- get val, type + compare *val-type, 1/string + { + break-if-!= + var val-ah/eax: (addr handle array byte) <- get val, text-data + var _val-string/eax: (addr array byte) <- lookup *val-ah + var val-string/ecx: (addr array byte) <- copy _val-string + var new-x/eax: int <- render-string screen, val-string, x, y + return new-x + } + compare *val-type, 2/array + { + break-if-!= + var val-ah/eax: (addr handle array value) <- get val, array-data + var _val-array/eax: (addr array value) <- lookup *val-ah + var val-array/edx: (addr array value) <- copy _val-array + var new-x/eax: int <- render-array screen, val-array, x, y + return new-x + } + # render ints by default for now + var val-num/eax: (addr float) <- get val, number-data + var new-x/eax: int <- render-number screen, *val-num, x, y, top-level? + return new-x +} + fn initialize-value-with-integer _self: (addr value), n: int { var self/esi: (addr value) <- copy _self var type/eax: (addr int) <- get self, type @@ -127,3 +156,79 @@ fn test-render-string { check-screen-row screen, 0/y, "\"abc\"", "F - test-render-string" check-ints-equal new-x, 5, "F - test-render-string: result" } + +fn initialize-value-with-array-of-integers _self: (addr value), s: (addr array byte) { + # parse s into a temporary array of ints + var tmp-storage: (handle array int) + var tmp-ah/eax: (addr handle array int) <- address tmp-storage + parse-array-of-decimal-ints s, tmp-ah # leak + var _tmp/eax: (addr array int ) <- lookup *tmp-ah + var tmp/esi: (addr array int ) <- copy _tmp + # load the array into values + var self/edi: (addr value) <- copy _self + var type/eax: (addr int) <- get self, type + copy-to *type, 2/string + var dest-array-ah/eax: (addr handle array value) <- get self, array-data + var len/ebx: int <- length tmp + populate dest-array-ah, len + var _dest-array/eax: (addr array value) <- lookup *dest-array-ah + var dest-array/edi: (addr array value) <- copy _dest-array + var i/eax: int <- copy 0 + { + compare i, len + break-if->= + var src-addr/ecx: (addr int) <- index tmp, i + var src/ecx: int <- copy *src-addr + var src-f/xmm0: float <- convert src + var dest-offset/edx: (offset value) <- compute-offset dest-array, i + var dest-val/edx: (addr value) <- index dest-array, dest-offset + var dest/edx: (addr float) <- get dest-val, number-data + copy-to *dest, src-f + i <- increment + loop + } +} + +fn render-array screen: (addr screen), _arr: (addr array value), x: int, y: int -> _/eax: int { + # don't surround in spaces + draw-code-point screen, 0x5b/open-bracket, x, y, 7/fg, 0/bg + increment x + var arr/esi: (addr array value) <- copy _arr + var max/ecx: int <- length arr + var i/edx: int <- copy 0 + var new-x/eax: int <- copy x + { + compare i, max + break-if->= + { + compare i, 0 + break-if-= + draw-code-point screen, 0x20/space, new-x, y, 7/fg, 0/bg + new-x <- increment + } + var off/ecx: (offset value) <- compute-offset arr, i + var x/ecx: (addr value) <- index arr, off + new-x <- render-value screen, x, new-x, y, 0/nested + i <- increment + loop + } + draw-code-point screen, 0x5d/close-bracket, new-x, y, 7/fg, 0/bg + new-x <- increment + return new-x +} + +fn test-render-array { + # setup: screen + var screen-on-stack: screen + var screen/edi: (addr screen) <- address screen-on-stack + initialize-screen screen, 0x20, 4 + # + var val-storage: value + var val/eax: (addr value) <- address val-storage + initialize-value-with-array-of-integers val, "0 1 2" + var val-array-ah/eax: (addr handle array value) <- get val, array-data + var val-array/eax: (addr array value) <- lookup *val-array-ah + var new-x/eax: int <- render-array screen, val-array, 0/x, 0/y + check-screen-row screen, 0/y, "[0 1 2]", "F - test-render-array" + check-ints-equal new-x, 7, "F - test-render-array: result" +} -- cgit 1.4.1-2-gfad0