diff --git a/subx/050_write.subx b/subx/050_write.subx index 8a65fc05..1975f7a3 100644 --- a/subx/050_write.subx +++ b/subx/050_write.subx @@ -1,45 +1,45 @@ # _write: write to a file descriptor == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - # syscall(exit, 0) -- can't test _write just yet - bb/copy-to-EBX 0/imm32 - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + # syscall(exit, 0) -- can't test _write just yet + bb/copy-to-EBX 0/imm32 + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 _write: # fd : int, s : (address array byte) -> - # prolog - 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 - # syscall(write, fd, (data) s+4, (size) *s) + # prolog + 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 + # syscall(write, fd, (data) s+4, (size) *s) # fd : EBX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX # data : ECX = s+4 - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX + 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX # size : EDX = *s - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX + 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX # syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - # restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + b8/copy-to-EAX 4/imm32/write + cd/syscall 0x80/imm8 + # restore registers + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # vim:nowrap:textwidth=0 diff --git a/subx/051test.subx b/subx/051test.subx index ec80adee..e665793a 100644 --- a/subx/051test.subx +++ b/subx/051test.subx @@ -1,85 +1,85 @@ # Rudimentary test harness == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: (manual test if this is the last file loaded) - # check-ints-equal(34, 34) - 68/push "error in check-ints-equal"/imm32 - 68/push 34/imm32 - 68/push 34/imm32 - e8/call check-ints-equal/disp32 - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # syscall(exit, 0) - bb/copy-to-EBX 0/imm32 - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + # check-ints-equal(34, 34) + 68/push "error in check-ints-equal"/imm32 + 68/push 34/imm32 + 68/push 34/imm32 + e8/call check-ints-equal/disp32 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # syscall(exit, 0) + bb/copy-to-EBX 0/imm32 + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # print msg to stderr if a != b, otherwise print "." check-ints-equal: # (a : int, b : int, msg : (address array byte)) -> boolean - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # save registers - 51/push-ECX - 53/push-EBX - # load first 2 args into EAX and EBX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 0x8/disp8 . # copy *(EBP+8) to EAX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - # if EAX == b/EBX - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-unequal $check-ints-equal:else/disp8 + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 51/push-ECX + 53/push-EBX + # load first 2 args into EAX and EBX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 0x8/disp8 . # copy *(EBP+8) to EAX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX + # if EAX == b/EBX + 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX + 75/jump-if-unequal $check-ints-equal:else/disp8 # _write(2/stderr, '.') # push args - 68/push "."/imm32 - 68/push 2/imm32/stderr + 68/push "."/imm32 + 68/push 2/imm32/stderr # call - e8/call _write/disp32 + e8/call _write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # return - eb/jump $check-ints-equal:end/disp8 - # else: + eb/jump $check-ints-equal:end/disp8 + # else: $check-ints-equal:else: # _write(2/stderr, msg) # push args - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 0x10/disp8 . # copy *(EBP+16) to ECX - 51/push-ECX - 68/push 2/imm32/stderr + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 0x10/disp8 . # copy *(EBP+16) to ECX + 51/push-ECX + 68/push 2/imm32/stderr # call - e8/call _write/disp32 + e8/call _write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # _write(2/stderr, Newline) # push args - 68/push Newline/imm32 - 68/push 2/imm32/stderr + 68/push Newline/imm32 + 68/push 2/imm32/stderr # call - e8/call _write/disp32 + e8/call _write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # increment Num-test-failures - ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Num-test-failures/disp32 # increment *Num-test-failures + ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Num-test-failures/disp32 # increment *Num-test-failures $check-ints-equal:end: - # restore registers - 5b/pop-to-EBX - 59/pop-to-ECX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 5b/pop-to-EBX + 59/pop-to-ECX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return == data Newline: - # size - 01 00 00 00 - # data - 0a/newline + # size + 01 00 00 00 + # data + 0a/newline Num-test-failures: - 00 00 00 00 + 00 00 00 00 # vim:nowrap:textwidth=0 diff --git a/subx/052kernel_string_equal.subx b/subx/052kernel_string_equal.subx index 341607b5..3eb0158c 100644 --- a/subx/052kernel_string_equal.subx +++ b/subx/052kernel_string_equal.subx @@ -15,247 +15,247 @@ # would cause tests to not run, rather than to fail as we'd like.) == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # exit(Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # exit(Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # compare a null-terminated ascii string with a more idiomatic length-prefixed byte array # reason for the name: the only place we should have null-terminated ascii strings is from commandline args kernel-string-equal: # s : null-terminated ascii string, benchmark : length-prefixed ascii string -> EAX : boolean - # prolog - 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 - 57/push-EDI + # prolog + 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 + 57/push-EDI - # pseudocode: - # initialize n = b.length - # initialize s1 = s - # initialize s2 = b.data - # i = 0 - # for (i = 0; i < n; ++n) - # c1 = *s1 - # c2 = *s2 - # if c1 == 0 - # return false - # if c1 != c2 - # return false - # return *s1 == 0 - # - # initialize s into EDI - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # initialize benchmark length n into EDX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX - # initialize benchmark data into ESI - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI - # initialize loop counter i into ECX - b9/copy-to-ECX 0/imm32/exit - # while (i/ECX < n/EDX) + # pseudocode: + # initialize n = b.length + # initialize s1 = s + # initialize s2 = b.data + # i = 0 + # for (i = 0; i < n; ++n) + # c1 = *s1 + # c2 = *s2 + # if c1 == 0 + # return false + # if c1 != c2 + # return false + # return *s1 == 0 + # + # initialize s into EDI + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + # initialize benchmark length n into EDX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX + 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX + # initialize benchmark data into ESI + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI + # initialize loop counter i into ECX + b9/copy-to-ECX 0/imm32/exit + # while (i/ECX < n/EDX) $kernel-string-equal:loop: - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 74/jump-if-equal $kernel-string-equal:break/disp8 + 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX + 74/jump-if-equal $kernel-string-equal:break/disp8 # c1/EAX, c2/EBX = *s, *benchmark - b8/copy-to-EAX 0/imm32 - 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX - bb/copy-to-EBX 0/imm32 - 8a/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy byte at *ESI to lower byte of EBX + b8/copy-to-EAX 0/imm32 + 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX + bb/copy-to-EBX 0/imm32 + 8a/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy byte at *ESI to lower byte of EBX # if (c1 == 0) return false - 3d/compare-EAX 0/imm32 - 74/jump-if-equal $kernel-string-equal:false/disp8 + 3d/compare-EAX 0/imm32 + 74/jump-if-equal $kernel-string-equal:false/disp8 # if (c1 != c2) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX with EBX - 75/jump-if-not-equal $kernel-string-equal:false/disp8 + 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX with EBX + 75/jump-if-not-equal $kernel-string-equal:false/disp8 # ++s1, ++s2, ++i - 41/inc-ECX - 46/inc-ESI - 47/inc-EDI - # end while - eb/jump $kernel-string-equal:loop/disp8 + 41/inc-ECX + 46/inc-ESI + 47/inc-EDI + # end while + eb/jump $kernel-string-equal:loop/disp8 $kernel-string-equal:break: - # if (*s/EDI == 0) return true - b8/copy-to-EAX 0/imm32 - 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX - 3d/compare-EAX 0/imm32 - 75/jump-if-not-equal $kernel-string-equal:false/disp8 + # if (*s/EDI == 0) return true + b8/copy-to-EAX 0/imm32 + 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX + 3d/compare-EAX 0/imm32 + 75/jump-if-not-equal $kernel-string-equal:false/disp8 $kernel-string-equal:true: - b8/copy-to-EAX 1/imm32 - eb/jump $kernel-string-equal:end/disp8 - # return false + b8/copy-to-EAX 1/imm32 + eb/jump $kernel-string-equal:end/disp8 + # return false $kernel-string-equal:false: - b8/copy-to-EAX 0/imm32 + b8/copy-to-EAX 0/imm32 $kernel-string-equal:end: - # restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 5f/pop-to-EDI + 5e/pop-to-ESI + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return ## tests test-compare-null-kernel-string-with-empty-array: - # EAX = kernel-string-equal(Null-kernel-string, "") + # EAX = kernel-string-equal(Null-kernel-string, "") # push args - 68/push ""/imm32 - 68/push Null-kernel-string/imm32 + 68/push ""/imm32 + 68/push Null-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 1, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 1, msg) # push args - 68/push "F - test-compare-null-kernel-string-with-empty-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX + 68/push "F - test-compare-null-kernel-string-with-empty-array"/imm32 + 68/push 1/imm32/true + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-null-kernel-string-with-non-empty-array: - # EAX = kernel-string-equal(Null-kernel-string, "Abc") + # EAX = kernel-string-equal(Null-kernel-string, "Abc") # push args - 68/push "Abc"/imm32 - 68/push Null-kernel-string/imm32 + 68/push "Abc"/imm32 + 68/push Null-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0, msg) # push args - 68/push "F - test-compare-null-kernel-string-with-non-empty-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-null-kernel-string-with-non-empty-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-equal-array: - # EAX = kernel-string-equal(Abc-kernel-string, "Abc") + # EAX = kernel-string-equal(Abc-kernel-string, "Abc") # push args - 68/push "Abc"/imm32 - 68/push Abc-kernel-string/imm32 + 68/push "Abc"/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 1, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 1, msg) # push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 + 68/push 1/imm32/true + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-inequal-array: - # EAX = kernel-string-equal(Abc-kernel-string, "Adc") + # EAX = kernel-string-equal(Abc-kernel-string, "Adc") # push args - 68/push "Adc"/imm32 - 68/push Abc-kernel-string/imm32 + 68/push "Adc"/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0, msg) # push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-empty-array: - # EAX = kernel-string-equal(Abc-kernel-string, "") + # EAX = kernel-string-equal(Abc-kernel-string, "") # push args - 68/push ""/imm32 - 68/push Abc-kernel-string/imm32 + 68/push ""/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0) # push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-shorter-array: - # EAX = kernel-string-equal(Abc-kernel-string, "Ab") + # EAX = kernel-string-equal(Abc-kernel-string, "Ab") # push args - 68/push "Ab"/imm32 - 68/push Abc-kernel-string/imm32 + 68/push "Ab"/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0) # push args - 68/push "F - test-compare-kernel-string-with-shorter-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-shorter-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-longer-array: - # EAX = kernel-string-equal(Abc-kernel-string, "Abcd") + # EAX = kernel-string-equal(Abc-kernel-string, "Abcd") # push args - 68/push "Abcd"/imm32 - 68/push Abc-kernel-string/imm32 + 68/push "Abcd"/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0) # push args - 68/push "F - test-compare-kernel-string-with-longer-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-longer-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return == data Null-kernel-string: - 00/null + 00/null Abc-kernel-string: - 41/A 62/b 63/c 00/null + 41/A 62/b 63/c 00/null # vim:nowrap:textwidth=0 diff --git a/subx/053new_segment.subx b/subx/053new_segment.subx index c9a710e3..69115575 100644 --- a/subx/053new_segment.subx +++ b/subx/053new_segment.subx @@ -1,62 +1,62 @@ # Create a new segment (for data) using mmap(). == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: (manual test if this is the last file loaded) - # EAX = new-segment(0x1000) + # EAX = new-segment(0x1000) # push args - 68/push 0x1000/imm32 + 68/push 0x1000/imm32 # call - e8/call new-segment/disp32 + e8/call new-segment/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # store to *EAX - c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0x34/imm32 # copy to *EAX + # store to *EAX + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0x34/imm32 # copy to *EAX - # exit(EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + # exit(EAX) + 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 new-segment: # len : int -> address - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 53/push-EBX - # copy len to mmap-new-segment.len - # TODO: compute mmap-new-segment+4 before runtime - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - bb/copy-to-EBX mmap-new-segment/imm32 - 89/copy 1/mod/*+disp8 3/rm32/EBX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EBX+4) - # mmap(mmap-new-segment) - bb/copy-to-EBX mmap-new-segment/imm32 - b8/copy-to-EAX 0x5a/imm32/mmap - cd/syscall 0x80/imm8 - # epilog - 5b/pop-to-EBX - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 53/push-EBX + # copy len to mmap-new-segment.len + # TODO: compute mmap-new-segment+4 before runtime + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + bb/copy-to-EBX mmap-new-segment/imm32 + 89/copy 1/mod/*+disp8 3/rm32/EBX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EBX+4) + # mmap(mmap-new-segment) + bb/copy-to-EBX mmap-new-segment/imm32 + b8/copy-to-EAX 0x5a/imm32/mmap + cd/syscall 0x80/imm8 + # epilog + 5b/pop-to-EBX + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return == data # various constants used here were found in the Linux sources (search for file mman-common.h) mmap-new-segment: # type mmap_arg_struct - # addr - 00 00 00 00 # null - # len - 00 00 00 00 # 0x1000 - # protection flags - 03 00 00 00 # PROT_READ | PROT_WRITE - # sharing flags - 22 00 00 00 # MAP_PRIVATE | MAP_ANONYMOUS - # fd - ff ff ff ff # -1 since MAP_ANONYMOUS is specified - # offset - 00 00 00 00 # 0 since MAP_ANONYMOUS is specified + # addr + 00 00 00 00 # null + # len + 00 00 00 00 # 0x1000 + # protection flags + 03 00 00 00 # PROT_READ | PROT_WRITE + # sharing flags + 22 00 00 00 # MAP_PRIVATE | MAP_ANONYMOUS + # fd + ff ff ff ff # -1 since MAP_ANONYMOUS is specified + # offset + 00 00 00 00 # 0 since MAP_ANONYMOUS is specified # vim:nowrap:textwidth=0 diff --git a/subx/054string_equal.subx b/subx/054string_equal.subx index a397dd8e..2b0d6006 100644 --- a/subx/054string_equal.subx +++ b/subx/054string_equal.subx @@ -1,169 +1,169 @@ # Comparing 'regular' length-prefixed strings. == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # exit(Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # exit(Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 string-equal: # s : string, benchmark : string -> EAX : boolean - # prolog - 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 - # pseudocode: - # if s.length != b.length return false - # for i = 0; i < s.length; ++i - # if s[i] != b[i] return false - # return true - # registers: - # i: ECX - # s.length: EDX - # b.length: EBX - # b[i]: EBX - # s[i]: EAX - # - # var s/EAX : (address array byte) - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # var benchmark/EBX : (address array byte) - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - # if s.length != b.length return false + # prolog + 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 + # pseudocode: + # if s.length != b.length return false + # for i = 0; i < s.length; ++i + # if s[i] != b[i] return false + # return true + # registers: + # i: ECX + # s.length: EDX + # b.length: EBX + # b[i]: EBX + # s[i]: EAX + # + # var s/EAX : (address array byte) + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + # var benchmark/EBX : (address array byte) + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX + # if s.length != b.length return false # EDX = s.length - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX # compare s.length and b.length - 39/compare 0/mod/indirect 3/rm32/EBX . . . 2/r32/EDX . . # compare *EBX with EDX - 75/jump-if-not-equal $string-equal:false/disp8 + 39/compare 0/mod/indirect 3/rm32/EBX . . . 2/r32/EDX . . # compare *EBX with EDX + 75/jump-if-not-equal $string-equal:false/disp8 $string-equal:lengths: - # var i/ECX : int = 0 - b9/copy-to-ECX 0/imm32 - # EBX = &b[i] - 43/inc-EBX - # EAX = &s[i] - 40/inc-EAX + # var i/ECX : int = 0 + b9/copy-to-ECX 0/imm32 + # EBX = &b[i] + 43/inc-EBX + # EAX = &s[i] + 40/inc-EAX $string-equal:loop: - # if i >= s.length return true - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 7d/jump-if-greater-or-equal $string-equal:true/disp8 - # if b[i] != s[i] return false + # if i >= s.length return true + 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX + 7d/jump-if-greater-or-equal $string-equal:true/disp8 + # if b[i] != s[i] return false # ESI = s[i] - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 6/r32/ESI . . # copy *EAX to ESI + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 6/r32/ESI . . # copy *EAX to ESI # compare b[i] with ESI - 39/compare 0/mod/indirect 3/rm32/EBX . . . 6/r32/ESI . . # compare *EBX with ESI - 75/jump-if-not-equal $string-equal:false/disp8 - # ++i - 41/inc-ECX - 40/inc-EAX - 43/inc-EBX - # loop - eb/jump $string-equal:loop/disp8 + 39/compare 0/mod/indirect 3/rm32/EBX . . . 6/r32/ESI . . # compare *EBX with ESI + 75/jump-if-not-equal $string-equal:false/disp8 + # ++i + 41/inc-ECX + 40/inc-EAX + 43/inc-EBX + # loop + eb/jump $string-equal:loop/disp8 $string-equal:true: - # return true - b8/copy-to-EAX 1/imm32 - eb/jump $string-equal:end/disp8 + # return true + b8/copy-to-EAX 1/imm32 + eb/jump $string-equal:end/disp8 $string-equal:false: - # return false - b8/copy-to-EAX 0/imm32 + # return false + b8/copy-to-EAX 0/imm32 $string-equal:end: - # restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 5e/pop-to-ESI + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return ## tests test-compare-empty-with-empty-string: - # EAX = string-equal("", "") + # EAX = string-equal("", "") # push args - 68/push ""/imm32 - 68/push ""/imm32 + 68/push ""/imm32 + 68/push ""/imm32 # call - e8/call string-equal/disp32 + e8/call string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 1, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 1, msg) # push args - 68/push "F - test-compare-empty-with-empty-string"/imm32 - 68/push 1/imm32/true - 50/push-EAX + 68/push "F - test-compare-empty-with-empty-string"/imm32 + 68/push 1/imm32/true + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-empty-with-non-empty-string: # also checks length-mismatch code path - # EAX = string-equal("", "Abc") + # EAX = string-equal("", "Abc") # push args - 68/push "Abc"/imm32 - 68/push ""/imm32 + 68/push "Abc"/imm32 + 68/push ""/imm32 # call - e8/call string-equal/disp32 + e8/call string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0, msg) # push args - 68/push "F - test-compare-empty-with-non-empty-string"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-empty-with-non-empty-string"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-equal-strings: - # EAX = string-equal("Abc", "Abc") + # EAX = string-equal("Abc", "Abc") # push args - 68/push "Abc"/imm32 - 68/push "Abc"/imm32 + 68/push "Abc"/imm32 + 68/push "Abc"/imm32 # call - e8/call string-equal/disp32 + e8/call string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 1, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 1, msg) # push args - 68/push "F - test-compare-equal-strings"/imm32 - 68/push 1/imm32/true - 50/push-EAX + 68/push "F - test-compare-equal-strings"/imm32 + 68/push 1/imm32/true + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-inequal-strings-equal-lengths: - # EAX = string-equal("Abc", "Adc") + # EAX = string-equal("Abc", "Adc") # push args - 68/push "Adc"/imm32 - 68/push "Abc"/imm32 + 68/push "Adc"/imm32 + 68/push "Abc"/imm32 # call - e8/call string-equal/disp32 + e8/call string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0, msg) # push args - 68/push "F - test-compare-inequal-strings-equal-lengths"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-inequal-strings-equal-lengths"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return # vim:nowrap:textwidth=0 diff --git a/subx/055trace.subx b/subx/055trace.subx index 8bcc1797..7986cf33 100644 --- a/subx/055trace.subx +++ b/subx/055trace.subx @@ -23,337 +23,337 @@ # We'll save the address of the trace segment here. Trace-stream: - 00 00 00 00 + 00 00 00 00 # Fake trace-stream for tests. # Also illustrates the layout of the real trace-stream (segment). _test-trace-stream: - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (= 8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # syscall(exit, Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream. # The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a) initialize-trace-stream: - # EAX = new-segment(0x1000) + # EAX = new-segment(0x1000) # push args - 68/push 0x1000/imm32/N + 68/push 0x1000/imm32/N # call - e8/call new-segment/disp32 + e8/call new-segment/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # copy EAX to *Trace-stream - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream - # Trace-stream.length = 0x1000/N - 12 - c7 0/copy 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 0xff4/imm32 # copy 0xff4 to *(EAX+8) - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # copy EAX to *Trace-stream + 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream + # Trace-stream.length = 0x1000/N - 12 + c7 0/copy 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 0xff4/imm32 # copy 0xff4 to *(EAX+8) + c3/return # Append a string to the given trace stream. # Silently give up if it's already full. Or truncate the string if there isn't enough room. trace: # t : (address trace-stream), line : string - # prolog - 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 - 56/push-ESI - 57/push-EDI - # EDI = t - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # ESI = line - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # ECX = t->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX - # EDX = t->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX - # EAX = _append-3(&t->data[t->write], &t->data[t->length], line) + # prolog + 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 + 56/push-ESI + 57/push-EDI + # EDI = t + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + # ESI = line + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + # ECX = t->write + 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX + # EDX = t->length + 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX + # EAX = _append-3(&t->data[t->write], &t->data[t->length], line) # push line - 56/push-ESI + 56/push-ESI # push &t->data[t->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy EDI+EDX+12 to EBX - 53/push-EBX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy EDI+EDX+12 to EBX + 53/push-EBX # push &t->data[t->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX - 53/push-EBX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX + 53/push-EBX # call - e8/call _append-3/disp32 + e8/call _append-3/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # if EAX == 0 return - 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EDX - 74/jump-if-equal $trace:end/disp8 - # t->write += EAX - 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI - # refresh ECX = t->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX - # EAX = _append-3(&t->data[t->write], &t->data[t->length], line) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # if EAX == 0 return + 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EDX + 74/jump-if-equal $trace:end/disp8 + # t->write += EAX + 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI + # refresh ECX = t->write + 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX + # EAX = _append-3(&t->data[t->write], &t->data[t->length], line) # push line - 68/push Newline/imm32 + 68/push Newline/imm32 # push &t->data[t->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy EDI+EDX+12 to EBX - 53/push-EBX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy EDI+EDX+12 to EBX + 53/push-EBX # push &t->data[t->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX - 53/push-EBX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX + 53/push-EBX # call - e8/call _append-3/disp32 + e8/call _append-3/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # t->write += EAX - 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # t->write += EAX + 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI $trace:end: - # 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 - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # 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 + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return clear-trace-stream: # t : (address trace-stream) - # prolog - 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 - # EAX = t - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # ECX = t->length - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX - # ECX = &t->data[t->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy EAX+ECX+12 to ECX - # t->write = 0 - c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # t->read = 0 - c7/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 0/imm32 # copy to *(EAX+4) - # EAX = t->data - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0xc/imm32 # add to EAX - # while (true) + # prolog + 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 + # EAX = t + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + # ECX = t->length + 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX + # ECX = &t->data[t->length] + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy EAX+ECX+12 to ECX + # t->write = 0 + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # t->read = 0 + c7/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 0/imm32 # copy to *(EAX+4) + # EAX = t->data + 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0xc/imm32 # add to EAX + # while (true) $clear-trace-stream:loop: - # if EAX >= ECX break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 7d/jump-if-greater-or-equal $clear-trace-stream:end/disp8 - # *EAX = 0 - c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # EAX += 4 - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX - eb/jump $clear-trace-stream:loop/disp8 + # if EAX >= ECX break + 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX + 7d/jump-if-greater-or-equal $clear-trace-stream:end/disp8 + # *EAX = 0 + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # EAX += 4 + 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX + eb/jump $clear-trace-stream:loop/disp8 $clear-trace-stream:end: - # restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return ## tests test-trace-single: - # clear-trace-stream(_test-trace-stream) + # clear-trace-stream(_test-trace-stream) # push args - 68/push _test-trace-stream/imm32 + 68/push _test-trace-stream/imm32 # call - e8/call clear-trace-stream/disp32 + e8/call clear-trace-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # trace(_test-trace-stream, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # trace(_test-trace-stream, "Ab") # push args - 68/push "Ab"/imm32 - 68/push _test-trace-stream/imm32 + 68/push "Ab"/imm32 + 68/push _test-trace-stream/imm32 # call - e8/call trace/disp32 + e8/call trace/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-trace-stream.data, 41/A 62/b 0a/newline 00, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*_test-trace-stream.data, 41/A 62/b 0a/newline 00, msg) # push args - 68/push "F - test-trace-single"/imm32 - 68/push 0x0a6241/imm32/Ab-newline + 68/push "F - test-trace-single"/imm32 + 68/push 0x0a6241/imm32/Ab-newline # push *_test-trace-stream.data - b8/copy-to-EAX _test-trace-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-trace-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return test-trace-appends: - # clear-trace-stream(_test-trace-stream) + # clear-trace-stream(_test-trace-stream) # push args - 68/push _test-trace-stream/imm32 + 68/push _test-trace-stream/imm32 # call - e8/call clear-trace-stream/disp32 + e8/call clear-trace-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # trace(_test-trace-stream, "C") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # trace(_test-trace-stream, "C") # push args - 68/push "C"/imm32 - 68/push _test-trace-stream/imm32 + 68/push "C"/imm32 + 68/push _test-trace-stream/imm32 # call - e8/call trace/disp32 + e8/call trace/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # trace(_test-trace-stream, "D") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # trace(_test-trace-stream, "D") # push args - 68/push "D"/imm32 - 68/push _test-trace-stream/imm32 + 68/push "D"/imm32 + 68/push _test-trace-stream/imm32 # call - e8/call trace/disp32 + e8/call trace/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-trace-stream.data, 43/C 0a/newline 44/D 0a/newline, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*_test-trace-stream.data, 43/C 0a/newline 44/D 0a/newline, msg) # push args - 68/push "F - test-trace-appends"/imm32 - 68/push 0x0a440a43/imm32/C-newline-D-newline + 68/push "F - test-trace-appends"/imm32 + 68/push 0x0a440a43/imm32/C-newline-D-newline # push *_test-trace-stream.data - b8/copy-to-EAX _test-trace-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-trace-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return test-trace-empty-line: - # clear-trace-stream(_test-trace-stream) + # clear-trace-stream(_test-trace-stream) # push args - 68/push _test-trace-stream/imm32 + 68/push _test-trace-stream/imm32 # call - e8/call clear-trace-stream/disp32 + e8/call clear-trace-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # trace(_test-trace-stream, "") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # trace(_test-trace-stream, "") # push args - 68/push ""/imm32 - 68/push _test-trace-stream/imm32 + 68/push ""/imm32 + 68/push _test-trace-stream/imm32 # call - e8/call trace/disp32 + e8/call trace/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-trace-stream.data, 0, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*_test-trace-stream.data, 0, msg) # push args - 68/push "F - test-trace-empty-line"/imm32 - 68/push 0/imm32 + 68/push "F - test-trace-empty-line"/imm32 + 68/push 0/imm32 # push *_test-trace-stream.data - b8/copy-to-EAX _test-trace-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-trace-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return ## helpers # 3-argument variant of _append _append-3: # out : address, outend : address, s : (array byte) -> num_bytes_appended/EAX - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # save registers - 51/push-ECX - # _append-4(out, outend, &s.data[0], &s.data[s.length]) -> num_bytes_appended/EAX + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 51/push-ECX + # _append-4(out, outend, &s.data[0], &s.data[s.length]) -> num_bytes_appended/EAX # push &s.data[s.length] # EAX = s - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX # ECX = s.length - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX # ECX = &s.data[s.length] - 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 - 51/push-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 + 51/push-ECX # push &s.data[0] - 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy EAX+4 to ECX - 51/push-ECX + 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy EAX+4 to ECX + 51/push-ECX # push outend - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # push out - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # call - e8/call _append-4/disp32 + e8/call _append-4/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # restore registers - 59/pop-to-ECX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # restore registers + 59/pop-to-ECX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # 4-argument variant of _append _append-4: # out : address, outend : address, in : address, inend : address -> num_bytes_appended/EAX - # prolog - 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 - 57/push-EDI - # EAX/num_bytes_appended = 0 - b8/copy-to-EAX 0/imm32 - # EDI = out - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0x8/disp8 . # copy *(EBP+8) to EDI - # EDX = outend - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - # ESI = in - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0x10/disp8 . # copy *(EBP+16) to ESI - # ECX = inend - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 0x14/disp8 . # copy *(EBP+20) to ECX + # prolog + 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 + 57/push-EDI + # EAX/num_bytes_appended = 0 + b8/copy-to-EAX 0/imm32 + # EDI = out + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0x8/disp8 . # copy *(EBP+8) to EDI + # EDX = outend + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX + # ESI = in + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0x10/disp8 . # copy *(EBP+16) to ESI + # ECX = inend + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 0x14/disp8 . # copy *(EBP+20) to ECX $_append-4:loop: - # if ESI/src >= ECX/srcend break - 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX - 7d/jump-if-greater-or-equal $_append-4:end/disp8 - # if EDI/out >= EDX/outend break (for now silently ignore filled up buffer) - 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX - 7d/jump-if-greater-or-equal $_append-4:end/disp8 - # copy one byte from ESI/src to EDI/out - 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL - 88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI - # updates - 40/increment-EAX - 46/increment-ESI - 47/increment-EDI - eb/jump $_append-4:loop/disp8 + # if ESI/src >= ECX/srcend break + 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX + 7d/jump-if-greater-or-equal $_append-4:end/disp8 + # if EDI/out >= EDX/outend break (for now silently ignore filled up buffer) + 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX + 7d/jump-if-greater-or-equal $_append-4:end/disp8 + # copy one byte from ESI/src to EDI/out + 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL + 88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI + # updates + 40/increment-EAX + 46/increment-ESI + 47/increment-EDI + eb/jump $_append-4:loop/disp8 $_append-4:end: - # restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 5f/pop-to-EDI + 5e/pop-to-ESI + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # vim:nowrap:textwidth=0 diff --git a/subx/056write.subx b/subx/056write.subx index f793babe..cf2c3194 100644 --- a/subx/056write.subx +++ b/subx/056write.subx @@ -16,197 +16,197 @@ # data: (array byte) # prefixed by length as usual == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # syscall(exit, Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 write: # f : fd or (address stream), s : (address array byte) -> bytes_written/EAX # (If we ever leave the Linux kernel behind, it may be better to return # the number of bytes *not* written. Success would then be signaled by # returning 0.) - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # if (f < 0x08000000) _write(f, s), return # f can't be a user-mode address, so treat it as a kernel file descriptor - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 7d/jump-if-greater-or-equal $write:fake/disp8 + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # if (f < 0x08000000) _write(f, s), return # f can't be a user-mode address, so treat it as a kernel file descriptor + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) + 7d/jump-if-greater-or-equal $write:fake/disp8 # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call _write/disp32 + e8/call _write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - eb/jump $write:end/disp8 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + eb/jump $write:end/disp8 $write:fake: - # otherwise, treat 'f' as a stream to append to - # save registers - 51/push-ECX - 52/push-EDX - 53/push-EBX - # ECX = f - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - # EDX = f->write - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX - # EBX = f->length - 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 3/r32/EBX 8/disp8 . # copy *(ECX+8) to EBX - # EAX = _append-3(&f->data[f->write], &f->data[f->length], s) + # otherwise, treat 'f' as a stream to append to + # save registers + 51/push-ECX + 52/push-EDX + 53/push-EBX + # ECX = f + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + # EDX = f->write + 8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX + # EBX = f->length + 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 3/r32/EBX 8/disp8 . # copy *(ECX+8) to EBX + # EAX = _append-3(&f->data[f->write], &f->data[f->length], s) # push s - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # push &f->data[f->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 3/index/EBX . 3/r32/EBX 0xc/disp8 . # copy ECX+EBX+12 to EBX - 53/push-EBX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 3/index/EBX . 3/r32/EBX 0xc/disp8 . # copy ECX+EBX+12 to EBX + 53/push-EBX # push &f->data[f->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy ECX+EDX+12 to EBX - 53/push-EBX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 3/r32/EBX 0xc/disp8 . # copy ECX+EDX+12 to EBX + 53/push-EBX # call - e8/call _append-3/disp32 + e8/call _append-3/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # f->write += EAX - 01/add 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # add EAX to *ECX - # restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # f->write += EAX + 01/add 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # add EAX to *ECX + # restore registers + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX $write:end: - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return clear-stream: # f : (address stream) -> - # prolog - 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 - # EAX = f - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX - # ECX = f->length - 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX - # ECX = &f->data[f->length] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy EAX+ECX+12 to ECX - # f->write = 0 - c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # f->read = 0 - c7/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 0/imm32 # copy to *(EAX+4) - # EAX = f->data - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0xc/imm32 # add to EAX - # while (true) + # prolog + 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 + # EAX = f + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + # ECX = f->length + 8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX + # ECX = &f->data[f->length] + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy EAX+ECX+12 to ECX + # f->write = 0 + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # f->read = 0 + c7/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 0/imm32 # copy to *(EAX+4) + # EAX = f->data + 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 0xc/imm32 # add to EAX + # while (true) $clear-stream:loop: - # if EAX >= ECX break - 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX - 7d/jump-if-greater-or-equal $clear-stream:end/disp8 - # *EAX = 0 - c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # EAX += 4 - 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX - eb/jump $clear-stream:loop/disp8 + # if EAX >= ECX break + 39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX + 7d/jump-if-greater-or-equal $clear-stream:end/disp8 + # *EAX = 0 + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # EAX += 4 + 81 0/subop/add 3/mod/direct 0/rm32/EAX . . . . . 4/imm32 # add to EAX + eb/jump $clear-stream:loop/disp8 $clear-stream:end: - # restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return test-write-single: - # clear-stream(_test-stream) + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream, "Ab") # push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 + 68/push "Ab"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 2) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(EAX, 2) # push args - 68/push "F - test-read-single: return EAX"/imm32 - 68/push 2/imm32 - 50/push-EAX + 68/push "F - test-read-single: return EAX"/imm32 + 68/push 2/imm32 + 50/push-EAX # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg) # push args - 68/push "F - test-write-single"/imm32 - 68/push 0x006241/imm32/Ab + 68/push "F - test-write-single"/imm32 + 68/push 0x006241/imm32/Ab # push *_test-stream->data - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return test-write-appends: - # clear-stream(_test-stream) + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "C") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream, "C") # push args - 68/push "C"/imm32 - 68/push _test-stream/imm32 + 68/push "C"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(_test-stream, "D") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(_test-stream, "D") # push args - 68/push "D"/imm32 - 68/push _test-stream/imm32 + 68/push "D"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-stream->data, 43/C 44/D 00 00, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*_test-stream->data, 43/C 44/D 00 00, msg) # push args - 68/push "F - test-write-appends"/imm32 - 68/push 0x00004443/imm32/C-D + 68/push "F - test-write-appends"/imm32 + 68/push 0x00004443/imm32/C-D # push *_test-stream->data - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return == data _test-stream: - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (= 8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes # vim:nowrap:textwidth=0 diff --git a/subx/057stop.subx b/subx/057stop.subx index b7ae2fa2..483c9404 100644 --- a/subx/057stop.subx +++ b/subx/057stop.subx @@ -33,177 +33,177 @@ # Its value is its output, computed during stop and available to the test. == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. -#? e8/call test-stop-skips-returns-on-exit/disp32 - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. +#? e8/call test-stop-skips-returns-on-exit/disp32 + # syscall(exit, Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to # the stack. # Ugly that we need to know the size of args, but so it goes. tailor-exit-descriptor: # ed : (address exit-descriptor), nbytes : int -> - # prolog - 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 - # EAX = nbytes - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX - # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor. - # The return address for a call in the caller's body will be at: - # X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address) - # X-12 if the caller takes 8 bytes of args - # ..and so on - # That's the value we need to return: X-nbytes-4 - # - # However, we also need to account for the perturbance to ESP caused by the - # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4 - # bytes for the return address and 4 bytes to push EBP above. - # So EBP at this point is X-16. - # - # So the return address for the next call in the caller is: - # EBP+8 if the caller takes 4 bytes of args - # EBP+4 if the caller takes 8 bytes of args - # EBP if the caller takes 12 bytes of args - # EBP-4 if the caller takes 16 bytes of args - # ..and so on - # That's EBP+12-nbytes. + # prolog + 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 + # EAX = nbytes + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor. + # The return address for a call in the caller's body will be at: + # X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address) + # X-12 if the caller takes 8 bytes of args + # ..and so on + # That's the value we need to return: X-nbytes-4 + # + # However, we also need to account for the perturbance to ESP caused by the + # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4 + # bytes for the return address and 4 bytes to push EBP above. + # So EBP at this point is X-16. + # + # So the return address for the next call in the caller is: + # EBP+8 if the caller takes 4 bytes of args + # EBP+4 if the caller takes 8 bytes of args + # EBP if the caller takes 12 bytes of args + # EBP-4 if the caller takes 16 bytes of args + # ..and so on + # That's EBP+12-nbytes. # option 1: 6 + 3 bytes -#? 2d/subtract 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # subtract from EAX -#? 8d/copy-address 0/mod/indirect 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX . . # copy EBP+EAX to EAX +#? 2d/subtract 3/mod/direct 0/rm32/EAX . . . . . 8/imm32 # subtract from EAX +#? 8d/copy-address 0/mod/indirect 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX . . # copy EBP+EAX to EAX # option 2: 2 + 4 bytes - f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EBP+EAX+12 to EAX - # copy EAX to ed->target - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX - # initialize ed->value - c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # copy to *(ECX+4) - # restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 5/base/EBP 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EBP+EAX+12 to EAX + # copy EAX to ed->target + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + 89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX + # initialize ed->value + c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 4/disp8 0/imm32 # copy to *(ECX+4) + # restore registers + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return stop: # ed : (address exit-descriptor), value : int - # no prolog; one way or another, we're going to clobber registers - # EAX = ed - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX - # exit(value) if ed->target == 0 - 81 7/subop/compare 0/mod/indirect 0/rm32/EAX . . . . . 0/imm32 # compare *EAX - 75/jump-if-not-equal $stop:fake/disp8 - # syscall(exit, value) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(ESP+8) to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + # no prolog; one way or another, we're going to clobber registers + # EAX = ed + 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX + # exit(value) if ed->target == 0 + 81 7/subop/compare 0/mod/indirect 0/rm32/EAX . . . . . 0/imm32 # compare *EAX + 75/jump-if-not-equal $stop:fake/disp8 + # syscall(exit, value) + 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(ESP+8) to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 $stop:fake: - # ed->value = value+1 - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(ESP+8) to ECX - 41/inc-ECX - 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) - # non-local jump to ed->target - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 4/r32/ESP . . # copy *EAX to ESP - c3/return # doesn't return to caller + # ed->value = value+1 + 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(ESP+8) to ECX + 41/inc-ECX + 89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy ECX to *(EAX+4) + # non-local jump to ed->target + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 4/r32/ESP . . # copy *EAX to ESP + c3/return # doesn't return to caller test-stop-skips-returns-on-exit: - # This looks like the standard prolog, but is here for different reasons. - # A function calling 'stop' can't rely on EBP persisting past the call. - # - # Use EBP here as a stable base to refer to locals and arguments from in the - # presence of push/pop/call instructions. - # *Don't* use EBP as a way to restore ESP. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # Make room for an exit descriptor on the stack. That's almost always the - # right place for it, available only as long as it's legal to use. Once this - # containing function returns we'll need a new exit descriptor. - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # Size the exit-descriptor precisely for the next call below, to _test-stop-1. - # tailor-exit-descriptor(ed, 4) + # This looks like the standard prolog, but is here for different reasons. + # A function calling 'stop' can't rely on EBP persisting past the call. + # + # Use EBP here as a stable base to refer to locals and arguments from in the + # presence of push/pop/call instructions. + # *Don't* use EBP as a way to restore ESP. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # Make room for an exit descriptor on the stack. That's almost always the + # right place for it, available only as long as it's legal to use. Once this + # containing function returns we'll need a new exit descriptor. + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # Size the exit-descriptor precisely for the next call below, to _test-stop-1. + # tailor-exit-descriptor(ed, 4) # push args - 68/push 4/imm32/nbytes-of-args-for-_test-stop-1 - 50/push-EAX + 68/push 4/imm32/nbytes-of-args-for-_test-stop-1 + 50/push-EAX # call - e8/call tailor-exit-descriptor/disp32 + e8/call tailor-exit-descriptor/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call _test-stop-1(ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call _test-stop-1(ed) # push args - 50/push-EAX + 50/push-EAX # call - e8/call _test-stop-1/disp32 - ## registers except ESP may be clobbered at this point + e8/call _test-stop-1/disp32 + ## registers except ESP may be clobbered at this point # restore args - 58/pop-to-EAX - # check that _test-stop-1 tried to call exit(1) - # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 + 58/pop-to-EAX + # check that _test-stop-1 tried to call exit(1) + # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 # push args - 68/push "F - test-stop-skips-returns-on-exit"/imm32 - 68/push 2/imm32 + 68/push "F - test-stop-skips-returns-on-exit"/imm32 + 68/push 2/imm32 # push ed->value - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # epilog - 5d/pop-to-EBP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # epilog + 5d/pop-to-EBP # don't restore ESP from EBP; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + c3/return _test-stop-1: # ed : (address exit-descriptor) - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # _test-stop-2(ed) + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # _test-stop-2(ed) # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call _test-stop-2/disp32 - ## should never get past this point + e8/call _test-stop-2/disp32 + ## should never get past this point # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # signal test failed: check-ints-equal(1, 0, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # signal test failed: check-ints-equal(1, 0, msg) # push args - 68/push "F - test-stop-skips-returns-on-exit"/imm32 - 68/push 0/imm32 - 68/push 1/imm32 + 68/push "F - test-stop-skips-returns-on-exit"/imm32 + 68/push 0/imm32 + 68/push 1/imm32 # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return _test-stop-2: # ed : (address exit-descriptor) - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # call stop(ed, 1) + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # call stop(ed, 1) # push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + 68/push 1/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call stop/disp32 - ## should never get past this point - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + e8/call stop/disp32 + ## should never get past this point + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # vim:nowrap:textwidth=0 diff --git a/subx/058read.subx b/subx/058read.subx index b6bfac64..54f83f0f 100644 --- a/subx/058read.subx +++ b/subx/058read.subx @@ -41,76 +41,76 @@ # data: (array byte) # prefixed by length as usual == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # syscall(exit, Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 read: # f : fd or (address stream), s : (address stream) -> num-bytes-read/EAX - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - ## if (f < 0x08000000) return _read(f, s) # f can't be a user-mode address, so treat it as a kernel file descriptor - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 7d/jump-if-greater-or-equal $read:fake/disp8 + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## if (f < 0x08000000) return _read(f, s) # f can't be a user-mode address, so treat it as a kernel file descriptor + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) + 7d/jump-if-greater-or-equal $read:fake/disp8 # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call _read/disp32 + e8/call _read/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # return - eb/jump $read:end/disp8 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # return + eb/jump $read:end/disp8 $read:fake: - ## otherwise, treat 'f' as a stream to scan from - # save registers - 56/push-ESI - 57/push-EDI - # ESI = f - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = s - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to ESI - # EAX = _append-4(out = &s->data[s->write], outend = &s->data[s->length], - # in = &f->data[f->read], inend = &f->data[f->write]) + ## otherwise, treat 'f' as a stream to scan from + # save registers + 56/push-ESI + 57/push-EDI + # ESI = f + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + # EDI = s + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to ESI + # EAX = _append-4(out = &s->data[s->write], outend = &s->data[s->length], + # in = &f->data[f->read], inend = &f->data[f->write]) # push &f->data[f->write] - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX + 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX + 50/push-EAX # push &f->data[f->read] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX + 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX + 50/push-EAX # push &s->data[s->length] - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 8/disp8 . # copy *(EDI+8) to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX - 50/push-EAX + 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 8/disp8 . # copy *(EDI+8) to EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX + 50/push-EAX # push &s->data[s->write] - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy *EDI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX - 50/push-EAX + 8b/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy *EDI to EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX + 50/push-EAX # call - e8/call _append-4/disp32 + e8/call _append-4/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # s->write += EAX - 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI - # f.read += EAX - 01/add 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # add EAX to *(ESI+4) - # restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # s->write += EAX + 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI + # f.read += EAX + 01/add 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # add EAX to *(ESI+4) + # restore registers + 5f/pop-to-EDI + 5e/pop-to-ESI $read:end: - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return ## helpers @@ -119,245 +119,245 @@ $read:end: # Maybe a better helper would be 'empty-stream?' _read: # fd : int, s : (address stream) -> num-bytes-read/EAX - # prolog - 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 - # ESI = s - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # EAX = s->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - # EDX = s->length - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 8/disp8 . # copy *(ESI+8) to EDX - # syscall(read, fd, &s->data[s->write], s->length - s->write) + # prolog + 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 + # ESI = s + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + # EAX = s->write + 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX + # EDX = s->length + 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 8/disp8 . # copy *(ESI+8) to EDX + # syscall(read, fd, &s->data[s->write], s->length - s->write) # fd : EBX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX # data : ECX = &s->data[s->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 1/r32/ECX 0xc/disp8 . # copy ESI+EAX+12 to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 1/r32/ECX 0xc/disp8 . # copy ESI+EAX+12 to ECX # size : EDX = s->length - s->write - 29/subtract 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # subtract EAX from EDX + 29/subtract 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # subtract EAX from EDX # syscall - b8/copy-to-EAX 3/imm32/read - cd/syscall 0x80/imm8 - # add the result EAX to s->write - 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI - # restore registers - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + b8/copy-to-EAX 3/imm32/read + cd/syscall 0x80/imm8 + # add the result EAX to s->write + 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI + # restore registers + 5e/pop-to-ESI + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return - # Two options: - # 1 (what we have above): - # ECX = s - # EAX = s->write - # EDX = s->length - # # syscall - # ECX = lea ECX+EAX+12 - # EDX = sub EDX EAX - # - # 2: - # ECX = s - # EDX = s->length - # ECX = &s->data - # # syscall - # ECX = add ECX, s->write - # EDX = sub EDX, s->write - # - # Not much to choose between the two? Option 2 performs a duplicate load to - # use one less register, but doesn't increase the amount of spilling (ECX - # and EDX must be used, and EAX must be clobbered anyway). + # Two options: + # 1 (what we have above): + # ECX = s + # EAX = s->write + # EDX = s->length + # # syscall + # ECX = lea ECX+EAX+12 + # EDX = sub EDX EAX + # + # 2: + # ECX = s + # EDX = s->length + # ECX = &s->data + # # syscall + # ECX = add ECX, s->write + # EDX = sub EDX, s->write + # + # Not much to choose between the two? Option 2 performs a duplicate load to + # use one less register, but doesn't increase the amount of spilling (ECX + # and EDX must be used, and EAX must be clobbered anyway). ## tests test-read-single: - # clear-stream(_test-stream) + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-stream-buffer) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-stream-buffer) # push args - 68/push _test-stream-buffer/imm32 + 68/push _test-stream-buffer/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream, "Ab") # push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 + 68/push "Ab"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read(_test-stream, _test-stream-buffer) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # read(_test-stream, _test-stream-buffer) # push args - 68/push _test-stream-buffer/imm32 - 68/push _test-stream/imm32 + 68/push _test-stream-buffer/imm32 + 68/push _test-stream/imm32 # call - e8/call read/disp32 + e8/call read/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 2) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(EAX, 2) # push args - 68/push "F - test-read-single: return EAX"/imm32 - 68/push 2/imm32 - 50/push-EAX + 68/push "F - test-read-single: return EAX"/imm32 + 68/push 2/imm32 + 50/push-EAX # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # check-ints-equal(*_test-stream-buffer->data, 41/A 62/b 00 00, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # check-ints-equal(*_test-stream-buffer->data, 41/A 62/b 00 00, msg) # push args - 68/push "F - test-read-single"/imm32 - 68/push 0x006241/imm32/Ab + 68/push "F - test-read-single"/imm32 + 68/push 0x006241/imm32/Ab # push *_test-stream-buffer->data - b8/copy-to-EAX _test-stream-buffer/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-stream-buffer/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return test-read-is-stateful: - ## make two consecutive reads, check that their results are appended - # clear-stream(_test-stream) + ## make two consecutive reads, check that their results are appended + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-stream-buffer) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-stream-buffer) # push args - 68/push _test-stream-buffer/imm32 + 68/push _test-stream-buffer/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "C") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream, "C") # push args - 68/push "C"/imm32 - 68/push _test-stream/imm32 + 68/push "C"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read(_test-stream, _test-stream-buffer) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # read(_test-stream, _test-stream-buffer) # push args - 68/push _test-stream-buffer/imm32 - 68/push _test-stream/imm32 + 68/push _test-stream-buffer/imm32 + 68/push _test-stream/imm32 # call - e8/call read/disp32 + e8/call read/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(_test-stream, "D") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(_test-stream, "D") # push args - 68/push "D"/imm32 - 68/push _test-stream/imm32 + 68/push "D"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read(_test-stream, _test-stream-buffer) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # read(_test-stream, _test-stream-buffer) # push args - 68/push _test-stream-buffer/imm32 - 68/push _test-stream/imm32 + 68/push _test-stream-buffer/imm32 + 68/push _test-stream/imm32 # call - e8/call read/disp32 + e8/call read/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-stream-buffer->data, 43/C 44/D 00 00, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*_test-stream-buffer->data, 43/C 44/D 00 00, msg) # push args - 68/push "F - test-read-is-stateful"/imm32 - 68/push 0x00004443/imm32/C-D + 68/push "F - test-read-is-stateful"/imm32 + 68/push 0x00004443/imm32/C-D # push *_test-stream-buffer->data - b8/copy-to-EAX _test-stream-buffer/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-stream-buffer/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return test-read-returns-0-on-end-of-file: - ## read after hitting end-of-file, check that result is 0 - # clear-stream(_test-stream) + ## read after hitting end-of-file, check that result is 0 + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-stream-buffer) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-stream-buffer) # push args - 68/push _test-stream-buffer/imm32 + 68/push _test-stream-buffer/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream, "Ab") # push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 + 68/push "Ab"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## first read gets to end-of-file - # read(_test-stream, _test-stream-buffer) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## first read gets to end-of-file + # read(_test-stream, _test-stream-buffer) # push args - 68/push _test-stream-buffer/imm32 - 68/push _test-stream/imm32 + 68/push _test-stream-buffer/imm32 + 68/push _test-stream/imm32 # call - e8/call read/disp32 + e8/call read/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## second read - # read(_test-stream, _test-stream-buffer) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## second read + # read(_test-stream, _test-stream-buffer) # push args - 68/push _test-stream-buffer/imm32 - 68/push _test-stream/imm32 + 68/push _test-stream-buffer/imm32 + 68/push _test-stream/imm32 # call - e8/call read/disp32 + e8/call read/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(EAX, 0) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(EAX, 0) # push args - 68/push "F - test-read-returns-0-on-end-of-file"/imm32 - 68/push 0/imm32 - 50/push-EAX + 68/push "F - test-read-returns-0-on-end-of-file"/imm32 + 68/push 0/imm32 + 50/push-EAX # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return == data _test-stream-buffer: - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (= 8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes # vim:nowrap:textwidth=0 diff --git a/subx/059read-byte.subx b/subx/059read-byte.subx index 80cf30fb..da99eed3 100644 --- a/subx/059read-byte.subx +++ b/subx/059read-byte.subx @@ -13,83 +13,83 @@ # buffered-file. Stdin: - # file descriptor or (address stream) - 00 00 00 00 # 0 = standard input - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # file descriptor or (address stream) + 00 00 00 00 # 0 = standard input + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. -#? e8/call test-read-byte-multiple/disp32 - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. +#? e8/call test-read-byte-multiple/disp32 + # syscall(exit, Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # return next byte value in EAX, with top 3 bytes cleared. # On EOF, return 0xffffffff. read-byte: # f : (address buffered-file) -> byte/EAX - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # save registers - 51/push-ECX - 56/push-ESI - # ESI = f - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # ECX = f->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX - ## if (f->read < f->write) read byte from stream - 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) - 7c/jump-if-lesser $read-byte:from-stream/disp8 - # clear-stream(stream = f+4) + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 51/push-ECX + 56/push-ESI + # ESI = f + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + # ECX = f->read + 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # copy *(ESI+8) to ECX + ## if (f->read < f->write) read byte from stream + 3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4) + 7c/jump-if-lesser $read-byte:from-stream/disp8 + # clear-stream(stream = f+4) # push args - 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX - 50/push-EAX + 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy ESI+4 to EAX + 50/push-EAX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # EAX = read(f->fd, stream = f+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # EAX = read(f->fd, stream = f+4) # push args - 50/push-EAX - ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI + 50/push-EAX + ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI # call - e8/call read/disp32 + e8/call read/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # if EAX = 0 return 0xffffffff - 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX - 75/jump-if-not-equal $read-byte:from-stream/disp8 - b8/copy-to-EAX 0xffffffff/imm32 - eb/jump $read-byte:end/disp8 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # if EAX = 0 return 0xffffffff + 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX + 75/jump-if-not-equal $read-byte:from-stream/disp8 + b8/copy-to-EAX 0xffffffff/imm32 + eb/jump $read-byte:end/disp8 $read-byte:from-stream: - # AL = f->data[f->read] - 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy *(ESI+ECX+16) to AL - # ++f->read - ff 0/subop/increment 1/mod/*+disp8 6/rm32/ESI . . . . 8/disp8 . # increment *(ESI+8) + # AL = f->data[f->read] + 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0x10/disp8 . # copy *(ESI+ECX+16) to AL + # ++f->read + ff 0/subop/increment 1/mod/*+disp8 6/rm32/ESI . . . . 8/disp8 . # increment *(ESI+8) $read-byte:end: - # restore registers - 5e/pop-to-ESI - 59/pop-to-ECX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 5e/pop-to-ESI + 59/pop-to-ECX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # todo: how should write-byte look? What should it do when the output has no # space remaining? Maybe return an error code. @@ -97,151 +97,151 @@ $read-byte:end: ## tests test-read-byte-single: - ## check that read-byte returns first byte of 'file' - # clear-stream(_test-stream) + ## check that read-byte returns first byte of 'file' + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream, "Ab") # push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 + 68/push "Ab"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read-byte(_test-buffered-file) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # read-byte(_test-buffered-file) # push args - 68/push _test-buffered-file/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call read-byte/disp32 + e8/call read-byte/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 'A') + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(EAX, 'A') # push args - 68/push "F - test-read-byte-single"/imm32 - 68/push 0x41/imm32 - 50/push-EAX + 68/push "F - test-read-byte-single"/imm32 + 68/push 0x41/imm32 + 50/push-EAX # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return test-read-byte-multiple: - ## call read-byte twice, check that second call returns second byte - # clear-stream(_test-stream) + ## call read-byte twice, check that second call returns second byte + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream, "Ab") # push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 + 68/push "Ab"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # read-byte(_test-buffered-file) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # read-byte(_test-buffered-file) # push args - 68/push _test-buffered-file/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call read-byte/disp32 + e8/call read-byte/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-byte(_test-buffered-file) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # read-byte(_test-buffered-file) # push args - 68/push _test-buffered-file/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call read-byte/disp32 + e8/call read-byte/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 'b') + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(EAX, 'b') # push args - 68/push "F - test-read-byte-multiple"/imm32 - 68/push 0x62/imm32 - 50/push-EAX + 68/push "F - test-read-byte-multiple"/imm32 + 68/push 0x62/imm32 + 50/push-EAX # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return test-read-byte-end-of-file: - ## call read-byte on an empty 'file', check that it returns -1 - # clear-stream(_test-stream) + ## call read-byte on an empty 'file', check that it returns -1 + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # read-byte(_test-buffered-file) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # read-byte(_test-buffered-file) # push args - 68/push _test-buffered-file/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call read-byte/disp32 + e8/call read-byte/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, -1) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(EAX, -1) # push args - 68/push "F - test-read-byte-end-of-file"/imm32 - 68/push -1/imm32 - 50/push-EAX + 68/push "F - test-read-byte-end-of-file"/imm32 + 68/push -1/imm32 + 50/push-EAX # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return == data _test-buffered-file: - # file descriptor or (address stream) - _test-stream/imm32 - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # file descriptor or (address stream) + _test-stream/imm32 + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes # vim:nowrap:textwidth=0 diff --git a/subx/060write-stream.subx b/subx/060write-stream.subx index 8b99301a..75957462 100644 --- a/subx/060write-stream.subx +++ b/subx/060write-stream.subx @@ -1,239 +1,239 @@ # write-stream: like write, but write streams rather than strings == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - # manual test -#? # write-stream(stdout, _test-stream2) -#? 68/push _test-stream2/imm32 -#? 68/push 1/imm32/stdout -#? e8/call write-stream/disp32 - # automatic test -#? e8/call test-write-stream-appends/disp32 - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # syscall(exit, Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + # manual test +#? # write-stream(stdout, _test-stream2) +#? 68/push _test-stream2/imm32 +#? 68/push 1/imm32/stdout +#? e8/call write-stream/disp32 + # automatic test +#? e8/call test-write-stream-appends/disp32 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # syscall(exit, Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 write-stream: # f : fd or (address stream), s : (address stream) -> - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # if (f < 0x08000000) _write-stream(f, s), return # f can't be a user-mode address, so treat it as a kernel file descriptor - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 7d/jump-if-greater-or-equal $write-stream:fake/disp8 + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # if (f < 0x08000000) _write-stream(f, s), return # f can't be a user-mode address, so treat it as a kernel file descriptor + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) + 7d/jump-if-greater-or-equal $write-stream:fake/disp8 # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call _write-stream/disp32 + e8/call _write-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - eb/jump $write-stream:end/disp8 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + eb/jump $write-stream:end/disp8 $write-stream:fake: - # otherwise, treat 'f' as a stream to append to - # save registers - 50/push-EAX - 56/push-ESI - 57/push-EDI - # ESI = f - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - # EDI = s - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI - # EAX = _append-4(&f->data[f->write], &f->data[f->length], &s->data[s->read], &s->data[s->write]) + # otherwise, treat 'f' as a stream to append to + # save registers + 50/push-EAX + 56/push-ESI + 57/push-EDI + # ESI = f + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + # EDI = s + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + # EAX = _append-4(&f->data[f->write], &f->data[f->length], &s->data[s->read], &s->data[s->write]) # push &s->data[s->write] - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy *EDI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX - 50/push-EAX + 8b/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy *EDI to EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX + 50/push-EAX # push &s->data[s->read] - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy *(EDI+4) to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX - 50/push-EAX + 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy *(EDI+4) to EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX + 50/push-EAX # push &f->data[f->length] - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 8/disp8 . # copy *(ESI+8) to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX + 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 8/disp8 . # copy *(ESI+8) to EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX + 50/push-EAX # push &f->data[f->write] - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX - 50/push-EAX + 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX + 50/push-EAX # call - e8/call _append-4/disp32 + e8/call _append-4/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # f->write += EAX - 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI - # s->read += EAX - 01/add 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # add EAX to *(EDI+4) - # restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 58/pop-to-EAX + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # f->write += EAX + 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI + # s->read += EAX + 01/add 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # add EAX to *(EDI+4) + # restore registers + 5f/pop-to-EDI + 5e/pop-to-ESI + 58/pop-to-EAX $write-stream:end: - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return _write-stream: # fd : int, s : (address stream) -> - # prolog - 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 - 56/push-ESI - 57/push-EDI - # ESI = s - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - # EDI = s->read - 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 7/r32/EDI 4/disp8 . # copy *(ESI+4) to EDI - # EDX = s->write - 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX - # syscall(write, fd, &s->data[s->read], s->write-s->read) + # prolog + 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 + 56/push-ESI + 57/push-EDI + # ESI = s + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + # EDI = s->read + 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 7/r32/EDI 4/disp8 . # copy *(ESI+4) to EDI + # EDX = s->write + 8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX + # syscall(write, fd, &s->data[s->read], s->write-s->read) # fd : EBX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX # data : ECX = &s->data[s->read] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 7/index/EDI . 1/r32/ECX 0xc/disp8 . # copy ESI+EDI+12 to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 7/index/EDI . 1/r32/ECX 0xc/disp8 . # copy ESI+EDI+12 to ECX # size : EDX = s->write - s->read - 29/subtract 3/mod/direct 2/rm32/EDX . . . 7/r32/EDI . . # subtract EDI from EDX + 29/subtract 3/mod/direct 2/rm32/EDX . . . 7/r32/EDI . . # subtract EDI from EDX # syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - # 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 - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + b8/copy-to-EAX 4/imm32/write + cd/syscall 0x80/imm8 + # 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 + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return test-write-stream-single: - # clear-stream(_test-stream) + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-stream2) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-stream2) # push args - 68/push _test-stream2/imm32 + 68/push _test-stream2/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream2, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream2, "Ab") # push args - 68/push "Ab"/imm32 - 68/push _test-stream2/imm32 + 68/push "Ab"/imm32 + 68/push _test-stream2/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream(_test-stream, _test-stream2) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write-stream(_test-stream, _test-stream2) # push args - 68/push _test-stream2/imm32 - 68/push _test-stream/imm32 + 68/push _test-stream2/imm32 + 68/push _test-stream/imm32 # call - e8/call write-stream/disp32 + e8/call write-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-stream.data, 41/A 62/b 00 00, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*_test-stream.data, 41/A 62/b 00 00, msg) # push args - 68/push "F - test-write-stream-single"/imm32 - 68/push 0x006241/imm32/Ab + 68/push "F - test-write-stream-single"/imm32 + 68/push 0x006241/imm32/Ab # push *_test-stream.data - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return test-write-stream-appends: - # clear-stream(_test-stream) + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-stream2) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-stream2) # push args - 68/push _test-stream2/imm32 + 68/push _test-stream2/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # write(_test-stream2, "C") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write(_test-stream2, "C") # push args - 68/push "C"/imm32 - 68/push _test-stream2/imm32 + 68/push "C"/imm32 + 68/push _test-stream2/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream(_test-stream, _test-stream2) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write-stream(_test-stream, _test-stream2) # push args - 68/push _test-stream2/imm32 - 68/push _test-stream/imm32 + 68/push _test-stream2/imm32 + 68/push _test-stream/imm32 # call - e8/call write-stream/disp32 + e8/call write-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(_test-stream2, "D") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(_test-stream2, "D") # push args - 68/push "D"/imm32 - 68/push _test-stream2/imm32 + 68/push "D"/imm32 + 68/push _test-stream2/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream(_test-stream, _test-stream2) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write-stream(_test-stream, _test-stream2) # push args - 68/push _test-stream2/imm32 - 68/push _test-stream/imm32 + 68/push _test-stream2/imm32 + 68/push _test-stream/imm32 # call - e8/call write-stream/disp32 + e8/call write-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # check-ints-equal(*_test-stream.data, 43/C 44/D 00 00, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(*_test-stream.data, 43/C 44/D 00 00, msg) # push args - 68/push "F - test-write-stream-appends"/imm32 - 68/push 0x00004443/imm32/C-D + 68/push "F - test-write-stream-appends"/imm32 + 68/push 0x00004443/imm32/C-D # push *_test-stream.data - b8/copy-to-EAX _test-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + b8/copy-to-EAX _test-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # end + c3/return == data _test-stream2: - # current write index - 04 00 00 00 - # current read index - 01 00 00 00 - # length (= 8) - 08 00 00 00 - # data - 41 42 43 44 00 00 00 00 # 8 bytes + # current write index + 04 00 00 00 + # current read index + 01 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 41 42 43 44 00 00 00 00 # 8 bytes # vim:nowrap:textwidth=0 diff --git a/subx/apps/crenshaw2-1.subx b/subx/apps/crenshaw2-1.subx index 94f8a3bc..94fd8efa 100644 --- a/subx/apps/crenshaw2-1.subx +++ b/subx/apps/crenshaw2-1.subx @@ -26,609 +26,609 @@ # naming conventions. == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: run tests if necessary, call 'compile' if not - # prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # if (argc > 1) - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # and if (argv[1] == "test") + # prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # if (argc > 1) + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP + 7e/jump-if-lesser-or-equal $run-main/disp8 + # and if (argv[1] == "test") # . push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + 68/push "test"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # . call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # . check result - 3d/compare-EAX 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # then return run-tests() - e8/call run-tests/disp32 -#? e8/call test-get-num-reads-multiple-digits/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 + 3d/compare-EAX 1/imm32 + 75/jump-if-not-equal $run-main/disp8 + # then return run-tests() + e8/call run-tests/disp32 +#? e8/call test-get-num-reads-multiple-digits/disp32 + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + eb/jump $main:end/disp8 $run-main: - # allocate space for an exit-descriptor + # allocate space for an exit-descriptor # . var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX # . clear ed->target (so we really exit) - c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # compile(Stdin, 1/stdout, 2/stderr, ed) + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # compile(Stdin, 1/stdout, 2/stderr, ed) # . push args - 50/push-EAX/ed - 68/push 2/imm32/stderr - 68/push 1/imm32/stdout - 68/push Stdin/imm32 + 50/push-EAX/ed + 68/push 2/imm32/stderr + 68/push 1/imm32/stdout + 68/push Stdin/imm32 # . call - e8/call compile/disp32 + e8/call compile/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # syscall(exit, 0) - bb/copy-to-EBX 0/imm32 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # syscall(exit, 0) + bb/copy-to-EBX 0/imm32 $main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 # the main entry point compile: # in : fd or (address stream), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> - # prolog - 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 - # Look = get-char(in) + # prolog + 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 + # Look = get-char(in) # . push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # . call - e8/call get-char/disp32 + e8/call get-char/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var num/ECX : (address stream) on the stack - # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - # We won't add more, so that we can get overflow-handling for free. - # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX - # num->length = 7 - c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) - # clear-stream(num) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # var num/ECX : (address stream) on the stack + # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. + # We won't add more, so that we can get overflow-handling for free. + # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX + # num->length = 7 + c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) + # clear-stream(num) # . push args - 51/push-ECX + 51/push-ECX # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # get-num(in, num, err, ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # get-num(in, num, err, ed) # . push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - 51/push-ECX/num - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + 51/push-ECX/num + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # . call - e8/call get-num/disp32 + e8/call get-num/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # EAX = write(_test-stream, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # EAX = write(_test-stream, "Ab") # . push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 + 68/push "Ab"/imm32 + 68/push _test-stream/imm32 # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, "bb/copy-to-EBX ") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, "bb/copy-to-EBX ") # . push args - 68/push "bb/copy-to-EBX "/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "bb/copy-to-EBX "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream(out, num) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write-stream(out, num) # . push args - 51/push-ECX/num - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 51/push-ECX/num + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write-stream/disp32 + e8/call write-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(out, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(out, Newline) # . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, "b8/copy-to-EAX 1/imm32/exit") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, "b8/copy-to-EAX 1/imm32/exit") # . push args - 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, Newline) # . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, "cd/syscall 0x80/imm8") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, "cd/syscall 0x80/imm8") # . push args - 68/push "cd/syscall 0x80/imm8"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "cd/syscall 0x80/imm8"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, Newline) # . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # restore registers + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'. # Input comes from the global variable 'Look', and we leave the next byte from # 'in' into it on exit. get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> - # pseudocode: - # if !is-digit?(Look) expected(ed, err, "integer") - # if out.write >= out.length - # write(err, "Error: too many digits in number\n") - # stop(ed, 1) - # out.data[out.write] = LSB(Look) - # ++out.write - # Look = get-char(in) - # - # within the loop we'll try to keep things in registers: - # ESI : in - # EDI : out - # EAX : temp - # ECX : out->write - # EDX : out->length - # EBX : temp2 - # We can't allocate Look to a register because it gets written implicitly in - # get-char in each iteration of the loop. (Thereby demonstrating that it's - # not the right interface for us. But we'll keep it just to follow Crenshaw.) - # - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = is-digit?(Look) + # pseudocode: + # if !is-digit?(Look) expected(ed, err, "integer") + # if out.write >= out.length + # write(err, "Error: too many digits in number\n") + # stop(ed, 1) + # out.data[out.write] = LSB(Look) + # ++out.write + # Look = get-char(in) + # + # within the loop we'll try to keep things in registers: + # ESI : in + # EDI : out + # EAX : temp + # ECX : out->write + # EDX : out->length + # EBX : temp2 + # We can't allocate Look to a register because it gets written implicitly in + # get-char in each iteration of the loop. (Thereby demonstrating that it's + # not the right interface for us. But we'll keep it just to follow Crenshaw.) + # + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # EAX = is-digit?(Look) # . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look # . call - e8/call is-digit?/disp32 + e8/call is-digit?/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # if EAX == 0 error - 3d/compare-EAX 0/imm32 - 75/jump-if-not-equal $get-num:main/disp8 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # if EAX == 0 error + 3d/compare-EAX 0/imm32 + 75/jump-if-not-equal $get-num:main/disp8 # . expected(ed, err, "integer") # push args - 68/push "integer"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + 68/push "integer"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) # call - e8/call expected/disp32 # never returns + e8/call expected/disp32 # never returns # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP $get-num:main: - # save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # read necessary variables to registers + # save registers + 50/push-EAX + 51/push-ECX + 52/push-EDX + 53/push-EBX + 56/push-ESI + 57/push-EDI + # read necessary variables to registers # ESI = in - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI # EDI = out - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI # ECX = out->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX + 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX # EDX = out->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX - # if out->write >= out->length error - 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX - 7d/jump-if-lesser $get-num:stage2/disp8 + 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX + # if out->write >= out->length error + 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX + 7d/jump-if-lesser $get-num:stage2/disp8 # error(ed, err, "get-num: too many digits in number") # TODO: show full number # . push args - 68/push "get-num: too many digits in number"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + 68/push "get-num: too many digits in number"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) # . call - e8/call error/disp32 # never returns + e8/call error/disp32 # never returns # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP $get-num:stage2: - # out->data[out->write] = LSB(Look) - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX - 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX - # ++out->write - 41/increment-ECX - # Look = get-char(in) + # out->data[out->write] = LSB(Look) + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX + 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX + # ++out->write + 41/increment-ECX + # Look = get-char(in) # . push args - 56/push-ESI + 56/push-ESI # . call - e8/call get-char/disp32 + e8/call get-char/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP $get-num:loop-end: - # persist necessary variables from registers - 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI - # 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 - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # persist necessary variables from registers + 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI + # 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 + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return test-get-num-reads-single-digit: - # - check that get-num returns first character if it's a digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - clear all streams - # clear-stream(_test-stream) + # - check that get-num returns first character if it's a digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # - clear all streams + # clear-stream(_test-stream) # . push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-output-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-output-stream) # . push args - 68/push _test-output-stream/imm32 + 68/push _test-output-stream/imm32 # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-error-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-error-stream) # . push args - 68/push _test-error-stream/imm32 + 68/push _test-error-stream/imm32 # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # - initialize 'in' - # write(_test-stream, "3") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # - initialize 'in' + # write(_test-stream, "3") # . push args - 68/push "3"/imm32 - 68/push _test-stream/imm32 + 68/push "3"/imm32 + 68/push _test-stream/imm32 # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # - initialize exit-descriptor 'ed' - # allocate on stack - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # size the exit-descriptor for the call to get-num below - # tailor-exit-descriptor(ed, 16) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # - initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) # . push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed # . call - e8/call tailor-exit-descriptor/disp32 + e8/call tailor-exit-descriptor/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # - prime the pump - # get-char(_test-buffered-file) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # - prime the pump + # get-char(_test-buffered-file) # . push args - 68/push _test-buffered-file/imm32 + 68/push _test-buffered-file/imm32 # . call - e8/call get-char/disp32 + e8/call get-char/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## get-num(in, out, err, ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## get-num(in, out, err, ed) # . push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 # . call - e8/call get-num/disp32 - ## registers except ESP may be clobbered at this point + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream.data, '3') + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # check-ints-equal(*_test-output-stream.data, '3') # . push args - 68/push "F - test-get-num-reads-single-digit"/imm32 - 68/push 0x33/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + 68/push "F - test-get-num-reads-single-digit"/imm32 + 68/push 0x33/imm32 + b8/copy-to-EAX _test-output-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # . call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return test-get-num-aborts-on-non-digit-in-Look: - # - check that get-num returns first character if it's a digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # - clear all streams - # clear-stream(_test-stream) + # - check that get-num returns first character if it's a digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # - clear all streams + # clear-stream(_test-stream) # . push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # . push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-output-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-output-stream) # . push args - 68/push _test-output-stream/imm32 + 68/push _test-output-stream/imm32 # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-error-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-error-stream) # . push args - 68/push _test-error-stream/imm32 + 68/push _test-error-stream/imm32 # . call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # - initialize 'in' - # write(_test-stream, "3") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # - initialize 'in' + # write(_test-stream, "3") # . push args - 68/push "3"/imm32 - 68/push _test-stream/imm32 + 68/push "3"/imm32 + 68/push _test-stream/imm32 # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # - initialize exit-descriptor 'ed' - # allocate on stack - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # size the exit-descriptor for the call to get-num below - # tailor-exit-descriptor(ed, 16) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # - initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) # . push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed # . call - e8/call tailor-exit-descriptor/disp32 + e8/call tailor-exit-descriptor/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # - don't initialize Look - # - get-num(in, out, err, ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # - don't initialize Look + # - get-num(in, out, err, ed) # . push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 # . call - e8/call get-num/disp32 - # registers except ESP may be clobbered at this point + e8/call get-num/disp32 + # registers except ESP may be clobbered at this point # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # - check that get-num tried to call exit(1) - # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # - check that get-num tried to call exit(1) + # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 # . push args - 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 - 68/push 2/imm32 + 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 + 68/push 2/imm32 # . push ed->value - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) # . call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return ## helpers # write(f, "Error: "+s+" expected\n") then stop(ed, 1) expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(f, "Error: ") + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # write(f, "Error: ") # . push args - 68/push "Error: "/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "Error: "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, s) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, s) # . push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, " expected") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, " expected") # . push args - 68/push " expected"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push " expected"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, Newline) # . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # stop(ed, 1) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # stop(ed, 1) # . push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + 68/push 1/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # . call - e8/call stop/disp32 - ## should never get past this point - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + e8/call stop/disp32 + ## should never get past this point + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # write(f, "Error: "+s+"\n") then stop(ed, 1) error: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(f, "Error: ") + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # write(f, "Error: ") # . push args - 68/push "Error: "/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "Error: "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, s) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, s) # . push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, Newline) # . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # . call - e8/call write/disp32 + e8/call write/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # stop(ed, 1) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # stop(ed, 1) # . push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + 68/push 1/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # . call - e8/call stop/disp32 - ## should never get past this point - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + e8/call stop/disp32 + ## should never get past this point + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # read a byte from 'f', and store it in 'Look' get-char: # f : (address buffered-file) -> - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # save registers - 50/push-EAX - # read-byte(f) + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 50/push-EAX + # read-byte(f) # . push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # . call - e8/call read-byte/disp32 + e8/call read-byte/disp32 # . discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # save EAX to Look - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look - # restore registers - 58/pop-to-EAX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # save EAX to Look + 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look + # restore registers + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return is-digit?: # c : int -> bool/EAX - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = false - b8/copy-to-EAX 0/imm32 - # if c < '0' return false - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) - 7c/jump-if-lesser $is-digit?:end/disp8 - # if c > '9' return false - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) - 7f/jump-if-greater $is-digit?:end/disp8 - # otherwise return true - b8/copy-to-EAX 1/imm32 + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # EAX = false + b8/copy-to-EAX 0/imm32 + # if c < '0' return false + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) + 7c/jump-if-lesser $is-digit?:end/disp8 + # if c > '9' return false + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) + 7f/jump-if-greater $is-digit?:end/disp8 + # otherwise return true + b8/copy-to-EAX 1/imm32 $is-digit?:end: - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return == data Look: # (char) - 00 00 00 00 # = 0 + 00 00 00 00 # = 0 _test-output-stream: - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (= 8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes _test-error-stream: - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (= 8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes # vim:nowrap:textwidth=0 diff --git a/subx/apps/crenshaw2-1b.subx b/subx/apps/crenshaw2-1b.subx index de900d7f..2df45308 100644 --- a/subx/apps/crenshaw2-1b.subx +++ b/subx/apps/crenshaw2-1b.subx @@ -26,811 +26,811 @@ # naming conventions. == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: run tests if necessary, call 'compile' if not - # prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # if (argc > 1) - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # and if (argv[1] == "test") + # prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # if (argc > 1) + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP + 7e/jump-if-lesser-or-equal $run-main/disp8 + # and if (argv[1] == "test") # push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + 68/push "test"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # check result - 3d/compare-EAX 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # then return run-tests() - e8/call run-tests/disp32 -#? e8/call test-get-num-reads-multiple-digits/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - eb/jump $main:end/disp8 + 3d/compare-EAX 1/imm32 + 75/jump-if-not-equal $run-main/disp8 + # then return run-tests() + e8/call run-tests/disp32 +#? e8/call test-get-num-reads-multiple-digits/disp32 + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + eb/jump $main:end/disp8 $run-main: - # allocate space for an exit-descriptor + # allocate space for an exit-descriptor # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX # clear ed->target (so we really exit) - c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - # compile(Stdin, 1/stdout, 2/stderr, ed) + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # compile(Stdin, 1/stdout, 2/stderr, ed) # push args - 50/push-EAX/ed - 68/push 2/imm32/stderr - 68/push 1/imm32/stdout - 68/push Stdin/imm32 + 50/push-EAX/ed + 68/push 2/imm32/stderr + 68/push 1/imm32/stdout + 68/push Stdin/imm32 # call - e8/call compile/disp32 + e8/call compile/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # syscall(exit, 0) - bb/copy-to-EBX 0/imm32 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # syscall(exit, 0) + bb/copy-to-EBX 0/imm32 $main:end: - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 # the main entry point compile: # in : fd or (address stream), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> - # prolog - 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 - # Look = get-char(in) + # prolog + 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 + # Look = get-char(in) # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call get-char/disp32 + e8/call get-char/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # var num/ECX : (address stream) on the stack - # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - # We won't add more, so that we can get overflow-handling for free. - # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX - # num->length = 7 - c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) - # clear-stream(num) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # var num/ECX : (address stream) on the stack + # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. + # We won't add more, so that we can get overflow-handling for free. + # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX + # num->length = 7 + c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) + # clear-stream(num) # push args - 51/push-ECX + 51/push-ECX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # get-num(in, num, err, ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # get-num(in, num, err, ed) # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - 51/push-ECX/num - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + 51/push-ECX/num + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call get-num/disp32 + e8/call get-num/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # EAX = write(_test-stream, "Ab") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # EAX = write(_test-stream, "Ab") # push args - 68/push "Ab"/imm32 - 68/push _test-stream/imm32 + 68/push "Ab"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, "bb/copy-to-EBX ") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, "bb/copy-to-EBX ") # push args - 68/push "bb/copy-to-EBX "/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "bb/copy-to-EBX "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write-stream(out, num) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write-stream(out, num) # push args - 51/push-ECX/num - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 51/push-ECX/num + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write-stream/disp32 + e8/call write-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(out, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(out, Newline) # push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, "b8/copy-to-EAX 1/imm32/exit") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, "b8/copy-to-EAX 1/imm32/exit") # push args - 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, Newline) # push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, "cd/syscall 0x80/imm8") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, "cd/syscall 0x80/imm8") # push args - 68/push "cd/syscall 0x80/imm8"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "cd/syscall 0x80/imm8"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # EAX = write(out, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, Newline) # push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # restore registers - 59/pop-to-ECX - 58/pop-to-EAX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # restore registers + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # Read a sequence of digits into 'out'. Abort if there are none, or if there is # no space in 'out'. # Input comes from the global variable 'Look' (first byte) and the argument # 'in' (rest). We leave the next byte from 'in' into 'Look' on exit. get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> - # pseudocode: - # if !is-digit?(Look) expected(ed, err, "integer") - # do - # if out.write >= out.length - # write(err, "Error: too many digits in number\n") - # stop(ed, 1) - # out.data[out.write] = LSB(Look) - # ++out.write - # Look = get-char(in) - # while is-digit?(Look) - # This is complicated because I don't want to hard-code the error strategy in - # a general helper like write-byte. Maybe I should just create a local helper. - # - # within the loop we'll try to keep things in registers: - # ESI : in - # EDI : out - # EAX : temp - # ECX : out->write - # EDX : out->length - # EBX : temp2 - # We can't allocate Look to a register because it gets written implicitly in - # get-char in each iteration of the loop. (Thereby demonstrating that it's - # not the right interface for us. But we'll keep it just to follow Crenshaw.) - # - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = is-digit?(Look) + # pseudocode: + # if !is-digit?(Look) expected(ed, err, "integer") + # do + # if out.write >= out.length + # write(err, "Error: too many digits in number\n") + # stop(ed, 1) + # out.data[out.write] = LSB(Look) + # ++out.write + # Look = get-char(in) + # while is-digit?(Look) + # This is complicated because I don't want to hard-code the error strategy in + # a general helper like write-byte. Maybe I should just create a local helper. + # + # within the loop we'll try to keep things in registers: + # ESI : in + # EDI : out + # EAX : temp + # ECX : out->write + # EDX : out->length + # EBX : temp2 + # We can't allocate Look to a register because it gets written implicitly in + # get-char in each iteration of the loop. (Thereby demonstrating that it's + # not the right interface for us. But we'll keep it just to follow Crenshaw.) + # + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # EAX = is-digit?(Look) # push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look # call - e8/call is-digit?/disp32 + e8/call is-digit?/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # if EAX == 0 error - 3d/compare-EAX 0/imm32 - 75/jump-if-not-equal $get-num:main/disp8 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # if EAX == 0 error + 3d/compare-EAX 0/imm32 + 75/jump-if-not-equal $get-num:main/disp8 # expected(ed, err, "integer") # push args - 68/push "integer"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + 68/push "integer"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) # call - e8/call expected/disp32 # never returns + e8/call expected/disp32 # never returns # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP $get-num:main: - # save registers - 50/push-EAX - 51/push-ECX - 52/push-EDX - 53/push-EBX - 56/push-ESI - 57/push-EDI - # read necessary variables to registers + # save registers + 50/push-EAX + 51/push-ECX + 52/push-EDX + 53/push-EBX + 56/push-ESI + 57/push-EDI + # read necessary variables to registers # ESI = in - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI # EDI = out - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI # ECX = out->write - 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX + 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX # EDX = out->length - 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX + 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX $get-num:loop: - # if out->write >= out->length error - 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX - 7d/jump-if-lesser $get-num:loop-stage2/disp8 + # if out->write >= out->length error + 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX + 7d/jump-if-lesser $get-num:loop-stage2/disp8 # error(ed, err, "get-num: too many digits in number") # TODO: show full number # push args - 68/push "get-num: too many digits in number"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + 68/push "get-num: too many digits in number"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) # call - e8/call error/disp32 # never returns + e8/call error/disp32 # never returns # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP $get-num:loop-stage2: - # out->data[out->write] = LSB(Look) - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX - 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX - # ++out->write - 41/increment-ECX - # Look = get-char(in) + # out->data[out->write] = LSB(Look) + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX + 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX + # ++out->write + 41/increment-ECX + # Look = get-char(in) # push args - 56/push-ESI + 56/push-ESI # call - e8/call get-char/disp32 + e8/call get-char/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # EAX = is-digit?(Look) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # EAX = is-digit?(Look) # push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look # call - e8/call is-digit?/disp32 + e8/call is-digit?/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # if EAX loop - 3d/compare-EAX 0/imm32 - 0f 85/jump-if-not-equal $get-num:loop/disp32 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # if EAX loop + 3d/compare-EAX 0/imm32 + 0f 85/jump-if-not-equal $get-num:loop/disp32 $get-num:loop-end: - # persist necessary variables from registers - 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI - # 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 - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # persist necessary variables from registers + 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI + # 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 + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return test-get-num-reads-single-digit: - ## check that get-num returns first character if it's a digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - ## clear all streams - # clear-stream(_test-stream) + ## check that get-num returns first character if it's a digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## clear all streams + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-output-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-output-stream) # push args - 68/push _test-output-stream/imm32 + 68/push _test-output-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-error-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-error-stream) # push args - 68/push _test-error-stream/imm32 + 68/push _test-error-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## initialize 'in' - # write(_test-stream, "3") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## initialize 'in' + # write(_test-stream, "3") # push args - 68/push "3"/imm32 - 68/push _test-stream/imm32 + 68/push "3"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## initialize exit-descriptor 'ed' - # allocate on stack - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # size the exit-descriptor for the call to get-num below - # tailor-exit-descriptor(ed, 16) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) # push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed # call - e8/call tailor-exit-descriptor/disp32 + e8/call tailor-exit-descriptor/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## prime the pump - # get-char(_test-buffered-file) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## prime the pump + # get-char(_test-buffered-file) # push args - 68/push _test-buffered-file/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call get-char/disp32 + e8/call get-char/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## get-num(in, out, err, ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## get-num(in, out, err, ed) # push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call get-num/disp32 - ## registers except ESP may be clobbered at this point + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream.data, '3') + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # check-ints-equal(*_test-output-stream.data, '3') # push args - 68/push "F - test-get-num-reads-single-digit"/imm32 - 68/push 0x33/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + 68/push "F - test-get-num-reads-single-digit"/imm32 + 68/push 0x33/imm32 + b8/copy-to-EAX _test-output-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return test-get-num-aborts-on-non-digit-in-Look: - ## check that get-num returns first character if it's a digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - ## clear all streams - # clear-stream(_test-stream) + ## check that get-num returns first character if it's a digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## clear all streams + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-output-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-output-stream) # push args - 68/push _test-output-stream/imm32 + 68/push _test-output-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-error-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-error-stream) # push args - 68/push _test-error-stream/imm32 + 68/push _test-error-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## initialize 'in' - # write(_test-stream, "3") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## initialize 'in' + # write(_test-stream, "3") # push args - 68/push "3"/imm32 - 68/push _test-stream/imm32 + 68/push "3"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## initialize exit-descriptor 'ed' - # allocate on stack - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # size the exit-descriptor for the call to get-num below - # tailor-exit-descriptor(ed, 16) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) # push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed # call - e8/call tailor-exit-descriptor/disp32 + e8/call tailor-exit-descriptor/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## don't initialize Look - ## get-num(in, out, err, ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## don't initialize Look + ## get-num(in, out, err, ed) # push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call get-num/disp32 - ## registers except ESP may be clobbered at this point + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - ## check that get-num tried to call exit(1) - # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + ## check that get-num tried to call exit(1) + # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 # push args - 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 - 68/push 2/imm32 + 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 + 68/push 2/imm32 # push ed->value - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return test-get-num-reads-multiple-digits: - ## check that get-num returns all initial digits until it encounters a non-digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - ## clear all streams - # clear-stream(_test-stream) + ## check that get-num returns all initial digits until it encounters a non-digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## clear all streams + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-output-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-output-stream) # push args - 68/push _test-output-stream/imm32 + 68/push _test-output-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-error-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-error-stream) # push args - 68/push _test-error-stream/imm32 + 68/push _test-error-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## initialize 'in' - # write(_test-stream, "3456 x") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## initialize 'in' + # write(_test-stream, "3456 x") # push args - 68/push "3456"/imm32 - 68/push _test-stream/imm32 + 68/push "3456"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## initialize exit-descriptor 'ed' - # allocate on stack - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # size the exit-descriptor for the call to get-num below - # tailor-exit-descriptor(ed, 16) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) # push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed # call - e8/call tailor-exit-descriptor/disp32 + e8/call tailor-exit-descriptor/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## prime the pump - # get-char(_test-buffered-file) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## prime the pump + # get-char(_test-buffered-file) # push args - 68/push _test-buffered-file/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call get-char/disp32 + e8/call get-char/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## get-num(in, out, err, ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## get-num(in, out, err, ed) # push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call get-num/disp32 - ## registers except ESP may be clobbered at this point + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream.data, '3456') + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # check-ints-equal(*_test-output-stream.data, '3456') # push args - 68/push "F - test-get-num-reads-multiple-digits"/imm32 - 68/push 0x36353433/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + 68/push "F - test-get-num-reads-multiple-digits"/imm32 + 68/push 0x36353433/imm32 + b8/copy-to-EAX _test-output-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return test-get-num-reads-multiple-digits-followed-by-nondigit: - ## check that get-num returns all initial digits until it encounters a non-digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - ## clear all streams - # clear-stream(_test-stream) + ## check that get-num returns all initial digits until it encounters a non-digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## clear all streams + # clear-stream(_test-stream) # push args - 68/push _test-stream/imm32 + 68/push _test-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-buffered-file+4) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-buffered-file+4) # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-output-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-output-stream) # push args - 68/push _test-output-stream/imm32 + 68/push _test-output-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # clear-stream(_test-error-stream) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # clear-stream(_test-error-stream) # push args - 68/push _test-error-stream/imm32 + 68/push _test-error-stream/imm32 # call - e8/call clear-stream/disp32 + e8/call clear-stream/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## initialize 'in' - # write(_test-stream, "3456 x") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## initialize 'in' + # write(_test-stream, "3456 x") # push args - 68/push "3456 x"/imm32 - 68/push _test-stream/imm32 + 68/push "3456 x"/imm32 + 68/push _test-stream/imm32 # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## initialize exit-descriptor 'ed' - # allocate on stack - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # size the exit-descriptor for the call to get-num below - # tailor-exit-descriptor(ed, 16) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) # push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed # call - e8/call tailor-exit-descriptor/disp32 + e8/call tailor-exit-descriptor/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## prime the pump - # get-char(_test-buffered-file) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## prime the pump + # get-char(_test-buffered-file) # push args - 68/push _test-buffered-file/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call get-char/disp32 + e8/call get-char/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## get-num(in, out, err, ed) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## get-num(in, out, err, ed) # push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 # call - e8/call get-num/disp32 - ## registers except ESP may be clobbered at this point + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream.data, '3456') + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # check-ints-equal(*_test-output-stream.data, '3456') # push args - 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 - 68/push 0x36353433/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 + 68/push 0x36353433/imm32 + b8/copy-to-EAX _test-output-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return ## helpers # write(f, "Error: "+s+" expected\n") then stop(ed, 1) expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(f, "Error: ") + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # write(f, "Error: ") # push args - 68/push "Error: "/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "Error: "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, s) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, s) # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, " expected") + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, " expected") # push args - 68/push " expected"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push " expected"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, Newline) # push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # stop(ed, 1) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # stop(ed, 1) # push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + 68/push 1/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call stop/disp32 - ## should never get past this point - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + e8/call stop/disp32 + ## should never get past this point + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # write(f, "Error: "+s+"\n") then stop(ed, 1) error: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # write(f, "Error: ") + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # write(f, "Error: ") # push args - 68/push "Error: "/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push "Error: "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, s) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, s) # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # write(f, Newline) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, Newline) # push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # call - e8/call write/disp32 + e8/call write/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # stop(ed, 1) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # stop(ed, 1) # push args - 68/push 1/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + 68/push 1/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) # call - e8/call stop/disp32 - ## should never get past this point - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + e8/call stop/disp32 + ## should never get past this point + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return # read a byte from 'f', and store it in 'Look' get-char: # f : (address buffered-file) -> - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # save registers - 50/push-EAX - # read-byte(f) + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 50/push-EAX + # read-byte(f) # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # call - e8/call read-byte/disp32 + e8/call read-byte/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # save EAX to Look - 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look - # restore registers - 58/pop-to-EAX - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # save EAX to Look + 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look + # restore registers + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return is-digit?: # c : int -> bool/EAX - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # EAX = false - b8/copy-to-EAX 0/imm32 - # if c < '0' return false - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) - 7c/jump-if-lesser $is-digit?:end/disp8 - # if c > '9' return false - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) - 7f/jump-if-greater $is-digit?:end/disp8 - # otherwise return true - b8/copy-to-EAX 1/imm32 + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # EAX = false + b8/copy-to-EAX 0/imm32 + # if c < '0' return false + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) + 7c/jump-if-lesser $is-digit?:end/disp8 + # if c > '9' return false + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) + 7f/jump-if-greater $is-digit?:end/disp8 + # otherwise return true + b8/copy-to-EAX 1/imm32 $is-digit?:end: - # epilog - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return == data Look: # (char) - 00 00 00 00 # = 0 + 00 00 00 00 # = 0 _test-output-stream: - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (= 8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes _test-error-stream: - # current write index - 00 00 00 00 - # current read index - 00 00 00 00 - # length (= 8) - 08 00 00 00 - # data - 00 00 00 00 00 00 00 00 # 8 bytes + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes # vim:nowrap:textwidth=0 diff --git a/subx/apps/factorial.subx b/subx/apps/factorial.subx index 43a61dcf..1e0191d1 100644 --- a/subx/apps/factorial.subx +++ b/subx/apps/factorial.subx @@ -15,94 +15,94 @@ # When running tests the exit status doesn't mean anything. Yet. == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - # prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # if (argc > 1) - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP - 7e/jump-if-lesser-or-equal $run-main/disp8 - # and if (argv[1] == "test") + # prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # if (argc > 1) + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP + 7e/jump-if-lesser-or-equal $run-main/disp8 + # and if (argv[1] == "test") # push args - 68/push "test"/imm32 - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + 68/push "test"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP # check result - 3d/compare-EAX 1/imm32 - 75/jump-if-not-equal $run-main/disp8 - # then return run-tests() - e8/call run-tests/disp32 - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Num-test-failures/disp32 # copy *Num-test-failures to EAX - eb/jump $main:end/disp8 # where EAX will get copied to EBX - # else EAX = factorial(5) + 3d/compare-EAX 1/imm32 + 75/jump-if-not-equal $run-main/disp8 + # then return run-tests() + e8/call run-tests/disp32 + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Num-test-failures/disp32 # copy *Num-test-failures to EAX + eb/jump $main:end/disp8 # where EAX will get copied to EBX + # else EAX = factorial(5) $run-main: # push args - 68/push 5/imm32 + 68/push 5/imm32 # call - e8/call factorial/disp32 + e8/call factorial/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP $main:end: - # exit(EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + # exit(EAX) + 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # factorial(n) factorial: - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 53/push-EBX - # initialize EAX to 1 (base case) - b8/copy-to-EAX 1/imm32 - # if (n <= 1) jump exit - 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 1/imm32 # compare *(EBP+8) - 7e/jump-if-<= $factorial:end/disp8 - # EBX = n-1 - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX - 81 5/subop/subtract 3/mod/direct 3/rm32/EBX . . . . . 1/imm32 # subtract from EBX - # EAX = factorial(n-1) + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 53/push-EBX + # initialize EAX to 1 (base case) + b8/copy-to-EAX 1/imm32 + # if (n <= 1) jump exit + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 1/imm32 # compare *(EBP+8) + 7e/jump-if-<= $factorial:end/disp8 + # EBX = n-1 + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX + 81 5/subop/subtract 3/mod/direct 3/rm32/EBX . . . . . 1/imm32 # subtract from EBX + # EAX = factorial(n-1) # push args - 53/push-EBX + 53/push-EBX # call - e8/call factorial/disp32 + e8/call factorial/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # return n * factorial(n-1) - f7 4/subop/multiply 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 8/disp8 . # multiply *(EBP+8) into EAX - # TODO: check for overflow + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # return n * factorial(n-1) + f7 4/subop/multiply 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none 8/disp8 . # multiply *(EBP+8) into EAX + # TODO: check for overflow $factorial:end: - # epilog - 5b/pop-to-EBX - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # epilog + 5b/pop-to-EBX + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return test-factorial: - # factorial(5) + # factorial(5) # push args - 68/push 5/imm32 + 68/push 5/imm32 # call - e8/call factorial/disp32 + e8/call factorial/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # check-ints-equal(EAX, 120, failure message) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(EAX, 120, failure message) # push args - 68/push "F - test-factorial"/imm32 - 68/push 0x78/imm32/expected-120 - 50/push-EAX + 68/push "F - test-factorial"/imm32 + 68/push 0x78/imm32/expected-120 + 50/push-EAX # call - e8/call check-ints-equal/disp32 + e8/call check-ints-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # end - c3/return + 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/subx/examples/ex10.subx b/subx/examples/ex10.subx index b179369f..4da713ae 100644 --- a/subx/examples/ex10.subx +++ b/subx/examples/ex10.subx @@ -8,9 +8,9 @@ # 0 # false == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: return argv-equal(argv[1], argv[2]) # At the start of a SubX program: @@ -18,55 +18,55 @@ # argv[0]: *(ESP+4) # argv[1]: *(ESP+8) # ... - # prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # call argv-equal(argv[1], argv[2]) + # prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # call argv-equal(argv[1], argv[2]) # push argv[2] - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # push argv[1] - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # call - e8/call argv-equal/disp32 - # exit(EAX) + e8/call argv-equal/disp32 + # exit(EAX) $exit: - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # compare two null-terminated ascii strings # reason for the name: the only place we should have null-terminated ascii strings is from commandline args argv-equal: # (s1, s2) : null-terminated ascii strings -> EAX : boolean - # initialize s1 (ECX) and s2 (EDX) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 4/disp8 . # copy *(ESP+4) to ECX - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 2/r32/EDX 8/disp8 . # copy *(ESP+8) to EDX - # while (true) + # initialize s1 (ECX) and s2 (EDX) + 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX 4/disp8 . # copy *(ESP+4) to ECX + 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 2/r32/EDX 8/disp8 . # copy *(ESP+8) to EDX + # while (true) $argv-equal:loop: # c1/EAX, c2/EBX = *s1, *s2 - b8/copy-to-EAX 0/imm32 - 8a/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy byte at *ECX to lower byte of EAX - bb/copy-to-EBX 0/imm32 - 8a/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy byte at *EDX to lower byte of EBX + b8/copy-to-EAX 0/imm32 + 8a/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy byte at *ECX to lower byte of EAX + bb/copy-to-EBX 0/imm32 + 8a/copy 0/mod/indirect 2/rm32/EDX . . . 3/r32/EBX . . # copy byte at *EDX to lower byte of EBX # if (c1 == 0) break - 3d/compare-EAX 0/imm32 - 74/jump-if-equal $argv-equal:break/disp8 + 3d/compare-EAX 0/imm32 + 74/jump-if-equal $argv-equal:break/disp8 # if (c1 != c2) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX with EBX - 75/jump-if-not-equal $argv-equal:false/disp8 + 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX with EBX + 75/jump-if-not-equal $argv-equal:false/disp8 # ++s1, ++s2 - 41/inc-ECX - 42/inc-EDX - # end while - eb/jump $argv-equal:loop/disp8 + 41/inc-ECX + 42/inc-EDX + # end while + eb/jump $argv-equal:loop/disp8 $argv-equal:break: - # if (c2 == 0) return true - 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX - 75/jump-if-not-equal $argv-equal:false/disp8 + # if (c2 == 0) return true + 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX + 75/jump-if-not-equal $argv-equal:false/disp8 $argv-equal:success: - b8/copy-to-EAX 1/imm32 - c3/return - # return false + b8/copy-to-EAX 1/imm32 + c3/return + # return false $argv-equal:false: - b8/copy-to-EAX 0/imm32 - c3/return + b8/copy-to-EAX 0/imm32 + c3/return # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex11.subx b/subx/examples/ex11.subx index 28eb2a8a..247a0076 100644 --- a/subx/examples/ex11.subx +++ b/subx/examples/ex11.subx @@ -15,336 +15,336 @@ # would cause tests to not run, rather than to fail as we'd like.) == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. - # syscall(exit, EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # syscall(exit, EAX) + 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # compare a null-terminated ascii string with a more idiomatic length-prefixed byte array # reason for the name: the only place we should have null-terminated ascii strings is from commandline args kernel-string-equal: # s : null-terminated ascii string, benchmark : length-prefixed ascii string -> EAX : boolean - # prolog - 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 - 57/push-EDI + # prolog + 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 + 57/push-EDI - # pseudocode: - # initialize n = b.length - # initialize s1 = s - # initialize s2 = b.data - # i = 0 - # for (i = 0; i < n; ++n) - # c1 = *s1 - # c2 = *s2 - # if c1 == 0 - # return false - # if c1 != c2 - # return false - # return *s1 == 0 - # initialize s into EDI - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI - # initialize benchmark length n into EDX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX - # initialize benchmark data into ESI - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI - # initialize loop counter i into ECX - b9/copy-to-ECX 0/imm32/exit - # while (i/ECX < n/EDX) + # pseudocode: + # initialize n = b.length + # initialize s1 = s + # initialize s2 = b.data + # i = 0 + # for (i = 0; i < n; ++n) + # c1 = *s1 + # c2 = *s2 + # if c1 == 0 + # return false + # if c1 != c2 + # return false + # return *s1 == 0 + # initialize s into EDI + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + # initialize benchmark length n into EDX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX + 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX + # initialize benchmark data into ESI + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + 81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI + # initialize loop counter i into ECX + b9/copy-to-ECX 0/imm32/exit + # while (i/ECX < n/EDX) $kernel-string-equal:loop: - 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 74/jump-if-equal $kernel-string-equal:break/disp8 + 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX + 74/jump-if-equal $kernel-string-equal:break/disp8 # c1/EAX, c2/EBX = *s, *benchmark - b8/copy-to-EAX 0/imm32 - 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX - bb/copy-to-EBX 0/imm32 - 8a/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy byte at *ESI to lower byte of EBX + b8/copy-to-EAX 0/imm32 + 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX + bb/copy-to-EBX 0/imm32 + 8a/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy byte at *ESI to lower byte of EBX # if (c1 == 0) return false - 3d/compare-EAX 0/imm32 - 74/jump-if-equal $kernel-string-equal:false/disp8 + 3d/compare-EAX 0/imm32 + 74/jump-if-equal $kernel-string-equal:false/disp8 # if (c1 != c2) return false - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX with EBX - 75/jump-if-not-equal $kernel-string-equal:false/disp8 + 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX with EBX + 75/jump-if-not-equal $kernel-string-equal:false/disp8 # ++s1, ++s2, ++i - 41/inc-ECX - 46/inc-ESI - 47/inc-EDI - # end while - eb/jump $kernel-string-equal:loop/disp8 + 41/inc-ECX + 46/inc-ESI + 47/inc-EDI + # end while + eb/jump $kernel-string-equal:loop/disp8 $kernel-string-equal:break: - # if (*s/EDI == 0) return true - b8/copy-to-EAX 0/imm32 - 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX - 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX - 75/jump-if-not-equal $kernel-string-equal:false/disp8 - b8/copy-to-EAX 1/imm32 + # if (*s/EDI == 0) return true + b8/copy-to-EAX 0/imm32 + 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX + 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX + 75/jump-if-not-equal $kernel-string-equal:false/disp8 + b8/copy-to-EAX 1/imm32 $kernel-string-equal:true: - eb/jump $kernel-string-equal:end/disp8 - # return false + eb/jump $kernel-string-equal:end/disp8 + # return false $kernel-string-equal:false: - b8/copy-to-EAX 0/imm32 + b8/copy-to-EAX 0/imm32 $kernel-string-equal:end: - # restore registers - 5f/pop-to-EDI - 5e/pop-to-ESI - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - # end - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 5f/pop-to-EDI + 5e/pop-to-ESI + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + # end + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return ## tests test-compare-null-kernel-string-with-empty-array: - # EAX = kernel-string-equal(Null-kernel-string, "") + # EAX = kernel-string-equal(Null-kernel-string, "") # push args - 68/push ""/imm32 - 68/push Null-kernel-string/imm32 + 68/push ""/imm32 + 68/push Null-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 1, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 1, msg) # push args - 68/push "F - test-compare-null-kernel-string-with-empty-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX + 68/push "F - test-compare-null-kernel-string-with-empty-array"/imm32 + 68/push 1/imm32/true + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-null-kernel-string-with-non-empty-array: - # EAX = kernel-string-equal(Null-kernel-string, "Abc") + # EAX = kernel-string-equal(Null-kernel-string, "Abc") # push args - 68/push "Abc"/imm32 - 68/push Null-kernel-string/imm32 + 68/push "Abc"/imm32 + 68/push Null-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0, msg) # push args - 68/push "F - test-compare-null-kernel-string-with-non-empty-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-null-kernel-string-with-non-empty-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-equal-array: - # EAX = kernel-string-equal(Abc-kernel-string, "Abc") + # EAX = kernel-string-equal(Abc-kernel-string, "Abc") # push args - 68/push "Abc"/imm32 - 68/push Abc-kernel-string/imm32 + 68/push "Abc"/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 1, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 1, msg) # push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 1/imm32/true - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 + 68/push 1/imm32/true + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-inequal-array: - # EAX = kernel-string-equal(Abc-kernel-string, "Adc") + # EAX = kernel-string-equal(Abc-kernel-string, "Adc") # push args - 68/push "Adc"/imm32 - 68/push Abc-kernel-string/imm32 + 68/push "Adc"/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0, msg) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0, msg) # push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-empty-array: - # EAX = kernel-string-equal(Abc-kernel-string, "") + # EAX = kernel-string-equal(Abc-kernel-string, "") # push args - 68/push ""/imm32 - 68/push Abc-kernel-string/imm32 + 68/push ""/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0) # push args - 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-equal-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-shorter-array: - # EAX = kernel-string-equal(Abc-kernel-string, "Ab") + # EAX = kernel-string-equal(Abc-kernel-string, "Ab") # push args - 68/push "Ab"/imm32 - 68/push Abc-kernel-string/imm32 + 68/push "Ab"/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0) # push args - 68/push "F - test-compare-kernel-string-with-shorter-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-shorter-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return test-compare-kernel-string-with-longer-array: - # EAX = kernel-string-equal(Abc-kernel-string, "Abcd") + # EAX = kernel-string-equal(Abc-kernel-string, "Abcd") # push args - 68/push "Abcd"/imm32 - 68/push Abc-kernel-string/imm32 + 68/push "Abcd"/imm32 + 68/push Abc-kernel-string/imm32 # call - e8/call kernel-string-equal/disp32 + e8/call kernel-string-equal/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # call check-ints-equal(EAX, 0) + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # call check-ints-equal(EAX, 0) # push args - 68/push "F - test-compare-kernel-string-with-longer-array"/imm32 - 68/push 0/imm32/false - 50/push-EAX + 68/push "F - test-compare-kernel-string-with-longer-array"/imm32 + 68/push 0/imm32/false + 50/push-EAX # call - e8/call check-ints-equal/disp32 + 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 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return ## helpers # print msg to stderr if a != b, otherwise print "." check-ints-equal: # (a : int, b : int, msg : (address array byte)) -> boolean - # prolog - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # save registers - 51/push-ECX - 53/push-EBX - # load args into EAX, EBX and ECX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 0x8/disp8 . # copy *(EBP+8) to EAX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX - # if EAX == b/EBX - 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX - 75/jump-if-unequal $check-ints-equal:else/disp8 + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 51/push-ECX + 53/push-EBX + # load args into EAX, EBX and ECX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 0x8/disp8 . # copy *(EBP+8) to EAX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX + # if EAX == b/EBX + 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX + 75/jump-if-unequal $check-ints-equal:else/disp8 # print('.') # push args - 68/push "."/imm32 + 68/push "."/imm32 # call - e8/call write-stderr/disp32 + e8/call write-stderr/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP # return - eb/jump $check-ints-equal:end/disp8 - # else: + eb/jump $check-ints-equal:end/disp8 + # else: $check-ints-equal:else: - # copy msg into ECX - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 0x10/disp8 . # copy *(EBP+16) to ECX + # copy msg into ECX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 0x10/disp8 . # copy *(EBP+16) to ECX # print(ECX) # push args - 51/push-ECX + 51/push-ECX # call - e8/call write-stderr/disp32 + e8/call write-stderr/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP # print newline # push args - 68/push Newline/imm32 + 68/push Newline/imm32 # call - e8/call write-stderr/disp32 + e8/call write-stderr/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP $check-ints-equal:end: - # restore registers - 5b/pop-to-EBX - 59/pop-to-ECX - # end - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + # restore registers + 5b/pop-to-EBX + 59/pop-to-ECX + # end + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return write-stderr: # s : (address array byte) -> - # prolog - 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 - # syscall(write, 2/stderr, (data) s+4, (size) *s) + # prolog + 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 + # syscall(write, 2/stderr, (data) s+4, (size) *s) # fd = 2 (stderr) - bb/copy-to-EBX 2/imm32 + bb/copy-to-EBX 2/imm32 # x = s+4 - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX - 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + 81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX # size = *s - 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 8/disp8 . # copy *(EBP+8) to EDX - 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 2/r32/EDX 8/disp8 . # copy *(EBP+8) to EDX + 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX # syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 - # restore registers - 5b/pop-to-EBX - 5a/pop-to-EDX - 59/pop-to-ECX - 58/pop-to-EAX - # end - 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 5d/pop-to-EBP - c3/return + b8/copy-to-EAX 4/imm32/write + cd/syscall 0x80/imm8 + # restore registers + 5b/pop-to-EBX + 5a/pop-to-EDX + 59/pop-to-ECX + 58/pop-to-EAX + # end + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return == data Newline: - # size - 01 00 00 00 - # data - 0a/newline + # size + 01 00 00 00 + # data + 0a/newline # for kernel-string-equal tests Null-kernel-string: - 00/null + 00/null Abc-kernel-string: - 41/A 62/b 63/c 00/null + 41/A 62/b 63/c 00/null # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex12.subx b/subx/examples/ex12.subx index 83943e1d..73d78d96 100644 --- a/subx/examples/ex12.subx +++ b/subx/examples/ex12.subx @@ -7,38 +7,38 @@ # You shouldn't get a segmentation fault. == code -# instruction effective address operand 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 +# instruction effective address operand 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 - # syscall(mmap, 0x1000) - bb/copy-to-EBX Mmap-new-segment/imm32 - b8/copy-to-EAX 0x5a/imm32/mmap - cd/syscall 0x80/imm8 + # syscall(mmap, 0x1000) + bb/copy-to-EBX Mmap-new-segment/imm32 + b8/copy-to-EAX 0x5a/imm32/mmap + cd/syscall 0x80/imm8 - # store to *EAX - c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0x34/imm32 # copy to *EAX + # store to *EAX + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0x34/imm32 # copy to *EAX - # syscall(exit, EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + # syscall(exit, EAX) + 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 == data # various constants used here were found in the Linux sources (search for file mman-common.h) Mmap-new-segment: # type mmap_arg_struct - # addr - 00 00 00 00 # null - # len - 00 01 00 00 # 0x1000 - # protection flags - 03 00 00 00 # PROT_READ | PROT_WRITE - # sharing flags - 22 00 00 00 # MAP_PRIVATE | MAP_ANONYMOUS - # fd - ff ff ff ff # -1 since MAP_ANONYMOUS is specified - # offset - 00 00 00 00 # 0 since MAP_ANONYMOUS is specified + # addr + 00 00 00 00 # null + # len + 00 01 00 00 # 0x1000 + # protection flags + 03 00 00 00 # PROT_READ | PROT_WRITE + # sharing flags + 22 00 00 00 # MAP_PRIVATE | MAP_ANONYMOUS + # fd + ff ff ff ff # -1 since MAP_ANONYMOUS is specified + # offset + 00 00 00 00 # 0 since MAP_ANONYMOUS is specified # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex3.subx b/subx/examples/ex3.subx index e5dad9e4..b3f7d6fe 100644 --- a/subx/examples/ex3.subx +++ b/subx/examples/ex3.subx @@ -8,29 +8,29 @@ # 55 == code -# instruction effective address operand 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 +# instruction effective address operand 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 - # result: EBX = 0 - bb/copy-to-EBX 0/imm32 - # counter: ECX = 1 - b9/copy-to-ECX 1/imm32 + # result: EBX = 0 + bb/copy-to-EBX 0/imm32 + # counter: ECX = 1 + b9/copy-to-ECX 1/imm32 $loop: - # while (counter <= 10) - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0xa/imm32 # compare ECX - 7f/jump-if-greater $exit/disp8 - # result += counter - 01/add 3/mod/direct 3/rm32/EBX . . . 1/r32/ECX . . # add ECX to EBX - # ++counter - 41/inc-ECX - # loop - eb/jump $loop/disp8 + # while (counter <= 10) + 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0xa/imm32 # compare ECX + 7f/jump-if-greater $exit/disp8 + # result += counter + 01/add 3/mod/direct 3/rm32/EBX . . . 1/r32/ECX . . # add ECX to EBX + # ++counter + 41/inc-ECX + # loop + eb/jump $loop/disp8 $exit: - # exit(EBX) - b8/copy-to-EAX 1/imm32 - cd/syscall 0x80/imm8 + # exit(EBX) + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex4.subx b/subx/examples/ex4.subx index 17dae120..445f7d1b 100644 --- a/subx/examples/ex4.subx +++ b/subx/examples/ex4.subx @@ -7,24 +7,24 @@ == code # syscall(read, stdin, X, 1) - # fd = 0 (stdin) + # fd = 0 (stdin) bb/copy-to-EBX 0/imm32 - # initialize X (location to write result to) + # initialize X (location to write result to) b9/copy-to-ECX X/imm32 - # size = 1 character + # size = 1 character ba/copy-to-EDX 1/imm32 - # syscall + # syscall b8/copy-to-EAX 3/imm32/read cd/syscall 0x80/imm8 # syscall(write, stdout, X, 1) - # fd = 1 (stdout) + # fd = 1 (stdout) bb/copy-to-EBX 1/imm32 - # initialize X (location to read from) + # initialize X (location to read from) b9/copy-to-ECX X/imm32 - # size = 1 character + # size = 1 character ba/copy-to-EDX 1/imm32 - # syscall + # syscall b8/copy-to-EAX 4/imm32/write cd/syscall 0x80/imm8 @@ -35,6 +35,6 @@ cd/syscall 0x80/imm8 == data X: - 00 00 00 00 # space for read() to write to + 00 00 00 00 # space for read() to write to # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex5.subx b/subx/examples/ex5.subx index cdc009bf..44291b35 100644 --- a/subx/examples/ex5.subx +++ b/subx/examples/ex5.subx @@ -5,38 +5,38 @@ # $ subx run examples/ex5 == code -# instruction effective address operand 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 +# instruction effective address operand 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 # main: - # allocate x on the stack - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # subtract from ESP + # allocate x on the stack + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # subtract from ESP - # syscall(read, stdin, x, 1) + # syscall(read, stdin, x, 1) # fd = 0 (stdin) - bb/copy-to-EBX 0/imm32 + bb/copy-to-EBX 0/imm32 # initialize x (location to write result to) - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 4/disp8 . # copy ESP+4 to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 4/disp8 . # copy ESP+4 to ECX # size = 1 character - ba/copy-to-EDX 1/imm32 + ba/copy-to-EDX 1/imm32 # syscall - b8/copy-to-EAX 3/imm32/read - cd/syscall 0x80/imm8 + b8/copy-to-EAX 3/imm32/read + cd/syscall 0x80/imm8 - # syscall(write, stdout, x, 1) + # syscall(write, stdout, x, 1) # fd = 1 (stdout) - bb/copy-to-EBX 1/imm32 + bb/copy-to-EBX 1/imm32 # initialize x (location to read from) - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 4/disp8 . # copy ESP+4 to ECX + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 4/disp8 . # copy ESP+4 to ECX # size = 1 character - ba/copy-to-EDX 1/imm32 + ba/copy-to-EDX 1/imm32 # syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 + b8/copy-to-EAX 4/imm32/write + cd/syscall 0x80/imm8 - # syscall(exit, EBX) - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + # syscall(exit, EBX) + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex6.subx b/subx/examples/ex6.subx index dfec4bf9..10331808 100644 --- a/subx/examples/ex6.subx +++ b/subx/examples/ex6.subx @@ -6,31 +6,31 @@ # Hello, world! == code -# instruction effective address operand 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 +# instruction effective address operand 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 - # syscall(write, stdout, X, size) + # syscall(write, stdout, X, size) # fd = 1 (stdout) - bb/copy-to-EBX 1/imm32 + bb/copy-to-EBX 1/imm32 # initialize X (location to write result to) - b9/copy-to-ECX X/imm32 + b9/copy-to-ECX X/imm32 # initialize size - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 2/r32/EDX Size/disp32 . # copy *size to EDX + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 2/r32/EDX Size/disp32 . # copy *size to EDX # syscall - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 + b8/copy-to-EAX 4/imm32/write + cd/syscall 0x80/imm8 - # syscall(exit, EBX) - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + # syscall(exit, EBX) + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 == data Size: # size of string - 0e 00 00 00 # 14 + 0e 00 00 00 # 14 X: # string to print - 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a 00 + 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a 00 # H e l l o , ␣ w o r l d ! newline null # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex7.subx b/subx/examples/ex7.subx index fc04ce2d..93b568f9 100644 --- a/subx/examples/ex7.subx +++ b/subx/examples/ex7.subx @@ -11,94 +11,94 @@ # 97 == code -# instruction effective address operand 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 +# instruction effective address operand 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 - # syscall(creat, Filename) - bb/copy-to-EBX Filename/imm32 - b9/copy-to-ECX 0x180/imm32/fixed-perms - b8/copy-to-EAX 8/imm32/creat - cd/syscall 0x80/imm8 + # syscall(creat, Filename) + bb/copy-to-EBX Filename/imm32 + b9/copy-to-ECX 0x180/imm32/fixed-perms + b8/copy-to-EAX 8/imm32/creat + cd/syscall 0x80/imm8 - # stream = syscall(open, Filename, O_WRONLY, 0) # we can't use 'fd' because it looks like a hex byte - bb/copy-to-EBX Filename/imm32 - b9/copy-to-ECX 1/imm32/wronly - ba/copy-to-EDX 0x180/imm32/fixed-perms - b8/copy-to-EAX 5/imm32/open - cd/syscall 0x80/imm8 + # stream = syscall(open, Filename, O_WRONLY, 0) # we can't use 'fd' because it looks like a hex byte + bb/copy-to-EBX Filename/imm32 + b9/copy-to-ECX 1/imm32/wronly + ba/copy-to-EDX 0x180/imm32/fixed-perms + b8/copy-to-EAX 5/imm32/open + cd/syscall 0x80/imm8 # save stream - bb/copy-to-EBX Stream/imm32 - 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX + bb/copy-to-EBX Stream/imm32 + 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX - # syscall(write, Stream, "a", 1) + # syscall(write, Stream, "a", 1) # load stream - bb/copy-to-EBX Stream/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX + bb/copy-to-EBX Stream/imm32 + 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX # - b9/copy-to-ECX A/imm32 - ba/copy-to-EDX 1/imm32/size - b8/copy-to-EAX 4/imm32/write - cd/syscall 0x80/imm8 + b9/copy-to-ECX A/imm32 + ba/copy-to-EDX 1/imm32/size + b8/copy-to-EAX 4/imm32/write + cd/syscall 0x80/imm8 - # syscall(close, Stream) + # syscall(close, Stream) # load stream - bb/copy-to-EBX Stream/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX + bb/copy-to-EBX Stream/imm32 + 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX # - b8/copy-to-EAX 6/imm32/close - cd/syscall 0x80/imm8 + b8/copy-to-EAX 6/imm32/close + cd/syscall 0x80/imm8 - # stream = syscall(open, Filename, O_RDONLY, 0) - bb/copy-to-EBX Filename/imm32 - b9/copy-to-ECX 0/imm32/rdonly - ba/copy-to-EDX 0x180/imm32/fixed-perms - b8/copy-to-EAX 5/imm32/open - cd/syscall 0x80/imm8 + # stream = syscall(open, Filename, O_RDONLY, 0) + bb/copy-to-EBX Filename/imm32 + b9/copy-to-ECX 0/imm32/rdonly + ba/copy-to-EDX 0x180/imm32/fixed-perms + b8/copy-to-EAX 5/imm32/open + cd/syscall 0x80/imm8 # save Stream - bb/copy-to-EBX Stream/imm32 - 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX + bb/copy-to-EBX Stream/imm32 + 89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX - # syscall(read, Stream, B, 1) + # syscall(read, Stream, B, 1) # load stream - bb/copy-to-EBX Stream/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX + bb/copy-to-EBX Stream/imm32 + 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX # - b9/copy-to-ECX B/imm32 - ba/copy-to-EDX 1/imm32/size - b8/copy-to-EAX 3/imm32/read - cd/syscall 0x80/imm8 + b9/copy-to-ECX B/imm32 + ba/copy-to-EDX 1/imm32/size + b8/copy-to-EAX 3/imm32/read + cd/syscall 0x80/imm8 - # syscall(close, Stream) + # syscall(close, Stream) # load stream - bb/copy-to-EBX Stream/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX + bb/copy-to-EBX Stream/imm32 + 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX # - b8/copy-to-EAX 6/imm32/close - cd/syscall 0x80/imm8 + b8/copy-to-EAX 6/imm32/close + cd/syscall 0x80/imm8 - # syscall(unlink, filename) - bb/copy-to-EBX Filename/imm32 - b8/copy-to-EAX 0xa/imm32/unlink - cd/syscall 0x80/imm8 + # syscall(unlink, filename) + bb/copy-to-EBX Filename/imm32 + b8/copy-to-EAX 0xa/imm32/unlink + cd/syscall 0x80/imm8 - # syscall(exit, b) + # syscall(exit, b) # load b - bb/copy-to-EBX B/imm32 - 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX + bb/copy-to-EBX B/imm32 + 8b/copy 0/mod/indirect 3/rm32/EBX . . . 3/r32/EBX . . # copy *EBX to EBX # - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 == data Stream: - 00 00 00 00 + 00 00 00 00 A: - 61 00 00 00 + 61 00 00 00 B: - 00 00 00 00 + 00 00 00 00 Filename: - 2e 66 6f 6f 00 00 00 00 + 2e 66 6f 6f 00 00 00 00 # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex8.subx b/subx/examples/ex8.subx index 3f5ebce5..fd7929f4 100644 --- a/subx/examples/ex8.subx +++ b/subx/examples/ex8.subx @@ -15,44 +15,44 @@ # Locals start from ESP-4 downwards. == code -# instruction effective address operand 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 +# instruction effective address operand 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 - # prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # call ascii-length(argv[1]) + # prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # call ascii-length(argv[1]) # push args - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # call - e8/call ascii-length/disp32 + e8/call ascii-length/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # exit(EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + # exit(EAX) + 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 ascii-length: # (s) - # initialize s (EDX) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 2/r32/EDX 4/disp8 . # copy *(ESP+4) to EDX - # var result = 0 (EAX) - b8/copy-to-EAX 0/imm32 + # initialize s (EDX) + 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none . 2/r32/EDX 4/disp8 . # copy *(ESP+4) to EDX + # var result = 0 (EAX) + b8/copy-to-EAX 0/imm32 $ascii-length-loop: - # var c = *s (ECX) - 8a/copy 0/mod/* 2/rm32/EDX . . . 1/r32/ECX . . # copy byte at *EDX to lower byte of ECX - # if c == '\0' break - 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX - 74/jump-if-equal $ascii-length-ret/disp8 - # ++s - 81 0/subop/add 3/mod/direct 2/rm32/EDX . . . . . 1/imm32 # add to EDX - # ++result - 40/inc-EAX - # loop - eb/jump $ascii-length-loop/disp8 + # var c = *s (ECX) + 8a/copy 0/mod/* 2/rm32/EDX . . . 1/r32/ECX . . # copy byte at *EDX to lower byte of ECX + # if c == '\0' break + 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX + 74/jump-if-equal $ascii-length-ret/disp8 + # ++s + 81 0/subop/add 3/mod/direct 2/rm32/EDX . . . . . 1/imm32 # add to EDX + # ++result + 40/inc-EAX + # loop + eb/jump $ascii-length-loop/disp8 $ascii-length-ret: - # return (result in EAX) - c3/return + # return (result in EAX) + c3/return # vim:nowrap:textwidth=0 diff --git a/subx/examples/ex9.subx b/subx/examples/ex9.subx index 6c6fc04b..00f2d661 100644 --- a/subx/examples/ex9.subx +++ b/subx/examples/ex9.subx @@ -17,35 +17,35 @@ # Locals start from ESP-4 downwards. == code -# instruction effective address operand 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 +# instruction effective address operand 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 - # prolog - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - # call ascii-difference(argv[1], argv[2]) + # prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # call ascii-difference(argv[1], argv[2]) # push argv[2] - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) # push argv[1] - ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) # call - e8/call ascii-difference/disp32 + e8/call ascii-difference/disp32 # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - # exit(EAX) - 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX - b8/copy-to-EAX 1/imm32/exit - cd/syscall 0x80/imm8 + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # exit(EAX) + 89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 ascii-difference: # (s1, s2) : null-terminated ascii strings - # a = first letter of s1 (ECX) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX - 8b/copy 0/mod/indirect 0/rm32/EAX . . . 0/r32/EAX . . # copy *EAX to EAX - # b = first letter of s2 (EDX) - 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 8/disp8 # copy *(ESP+8) to ECX - 8b/copy 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX - # a-b - 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX - c3/return + # a = first letter of s1 (ECX) + 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 0/r32/EAX 4/disp8 . # copy *(ESP+4) to EAX + 8b/copy 0/mod/indirect 0/rm32/EAX . . . 0/r32/EAX . . # copy *EAX to EAX + # b = first letter of s2 (EDX) + 8b/copy 1/mod/*+disp8 4/rm32/sib 4/base/ESP 4/index/none 1/r32/ECX 8/disp8 # copy *(ESP+8) to ECX + 8b/copy 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX + # a-b + 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX + c3/return # vim:nowrap:textwidth=0