7530 - baremetal: print ints to screen
This commit is contained in:
parent
6efc1ebed7
commit
117a710a9d
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue