diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/Readme.md | 6 | ||||
-rwxr-xr-x | examples/ex1 | bin | 0 -> 128 bytes | |||
-rw-r--r-- | examples/ex1.subx | 21 | ||||
-rwxr-xr-x | examples/ex10 | bin | 0 -> 195 bytes | |||
-rw-r--r-- | examples/ex10.subx | 72 | ||||
-rwxr-xr-x | examples/ex11 | bin | 0 -> 1111 bytes | |||
-rw-r--r-- | examples/ex11.subx | 357 | ||||
-rwxr-xr-x | examples/ex12 | bin | 0 -> 167 bytes | |||
-rw-r--r-- | examples/ex12.subx | 45 | ||||
-rwxr-xr-x | examples/ex2 | bin | 0 -> 129 bytes | |||
-rw-r--r-- | examples/ex2.subx | 23 | ||||
-rwxr-xr-x | examples/ex3 | bin | 0 -> 146 bytes | |||
-rw-r--r-- | examples/ex3.subx | 39 | ||||
-rwxr-xr-x | examples/ex4 | bin | 0 -> 171 bytes | |||
-rw-r--r-- | examples/ex4.subx | 42 | ||||
-rwxr-xr-x | examples/ex5 | bin | 0 -> 171 bytes | |||
-rw-r--r-- | examples/ex5.subx | 45 | ||||
-rwxr-xr-x | examples/ex6 | bin | 0 -> 165 bytes | |||
-rw-r--r-- | examples/ex6.subx | 37 | ||||
-rwxr-xr-x | examples/ex7 | bin | 0 -> 313 bytes | |||
-rw-r--r-- | examples/ex7.subx | 107 | ||||
-rwxr-xr-x | examples/ex8 | bin | 0 -> 165 bytes | |||
-rw-r--r-- | examples/ex8.subx | 61 | ||||
-rwxr-xr-x | examples/ex9 | bin | 0 -> 159 bytes | |||
-rw-r--r-- | examples/ex9.subx | 55 |
25 files changed, 910 insertions, 0 deletions
diff --git a/examples/Readme.md b/examples/Readme.md new file mode 100644 index 00000000..d4bb1ff5 --- /dev/null +++ b/examples/Readme.md @@ -0,0 +1,6 @@ +Small example programs, each with a simple pedagogical goal. + +They also help to validate SubX instruction semantics against native x86 +hardware. For example, loading a single byte to a register would for some time +clear the rest of the register. This behavior was internally consistent with +unit tests. It took running an example binary natively to catch the discrepancy. diff --git a/examples/ex1 b/examples/ex1 new file mode 100755 index 00000000..aeb62302 --- /dev/null +++ b/examples/ex1 Binary files differdiff --git a/examples/ex1.subx b/examples/ex1.subx new file mode 100644 index 00000000..0aca40f8 --- /dev/null +++ b/examples/ex1.subx @@ -0,0 +1,21 @@ +# First program: same as https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html +# Just return 42. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex1.2.subx -o examples/ex1 +# $ ./subx run examples/ex1 +# Expected result: +# $ echo $? +# 42 + +== code 0x09000000 + +Entry: +# syscall(exit, 42) +bb/copy-to-EBX 2a/imm32 # 42 in hex +b8/copy-to-EAX 1/imm32/exit +cd/syscall 0x80/imm8 + +== data 0x0a000000 + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex10 b/examples/ex10 new file mode 100755 index 00000000..d8ad528e --- /dev/null +++ b/examples/ex10 Binary files differdiff --git a/examples/ex10.subx b/examples/ex10.subx new file mode 100644 index 00000000..51cc0a8c --- /dev/null +++ b/examples/ex10.subx @@ -0,0 +1,72 @@ +# String comparison: return 1 iff the two args passed in at the commandline are equal. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex10.subx -o examples/ex10 +# $ ./subx run examples/ex10 abc abd +# Expected result: +# $ echo $? +# 0 # false + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: # return argv-equal(argv[1], argv[2]) +# At the start of a SubX program: +# argc: *ESP +# argv[0]: *(ESP+4) +# argv[1]: *(ESP+8) +# ... + # . prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # argv-equal(argv[1], argv[2]) + # . . push argv[2] + ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + # . . push argv[1] + ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + # . . call + e8/call argv-equal/disp32 + # 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 + +# 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 +$argv-equal:loop: + # c1/EAX, c2/EBX = *s1, *s2 + b8/copy-to-EAX 0/imm32 + 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL + bb/copy-to-EBX 0/imm32 + 8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 3/r32/BL . . # copy byte at *EDX to BL + # if (c1 == 0) break + 3d/compare-EAX-and 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 and EBX + 75/jump-if-not-equal $argv-equal:false/disp8 + # ++s1, ++s2 + 41/increment-ECX + 42/increment-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 +$argv-equal:success: + b8/copy-to-EAX 1/imm32 + c3/return + # return false +$argv-equal:false: + b8/copy-to-EAX 0/imm32 + c3/return + +== data 0x0a000000 + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex11 b/examples/ex11 new file mode 100755 index 00000000..0ffafb6f --- /dev/null +++ b/examples/ex11 Binary files differdiff --git a/examples/ex11.subx b/examples/ex11.subx new file mode 100644 index 00000000..ba75c1d3 --- /dev/null +++ b/examples/ex11.subx @@ -0,0 +1,357 @@ +# Null-terminated vs length-prefixed ascii strings. +# +# By default we create strings with a 4-byte length prefix rather than a null suffix. +# However we still need null-prefixed strings when interacting with the Linux +# kernel in a few places. This layer implements a function for comparing +# a null-terminated 'kernel string' with a length-prefixed 'SubX string'. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex11.subx -o examples/ex11 +# $ ./subx run examples/ex11 # runs a series of tests +# ...... # all tests pass +# +# (We can't yet run the tests when given a "test" commandline argument, +# because checking for it would require the function being tested! Breakage +# would cause tests to not run, rather than to fail as we'd like.) + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: # run all tests + 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/exit + 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 + # pseudocode: + # n = benchmark->length + # s1 = s + # s2 = benchmark->data + # i = 0 + # while i < n + # c1 = *s1 + # c2 = *s2 + # if (c1 == 0) return false + # if (c1 != c2) return false + # ++s1, ++s2, ++i + # return *s1 == 0 + # + # registers: + # i: ECX + # n: EDX + # s1: EDI + # s2: ESI + # c1: EAX + # c2: EBX + # + # . 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 + # s1/EDI = s + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + # n/EDX = benchmark->length + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 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 + # s2/ESI = benchmark->data + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 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 + # i/ECX = c1/EAX = c2/EBX = 0 + b9/copy-to-ECX 0/imm32/exit + b8/copy-to-EAX 0/imm32 + bb/copy-to-EBX 0/imm32 +$kernel-string-equal?:loop: + # if (i >= n) break + 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX + 7d/jump-if-greater-or-equal $kernel-string-equal?:break/disp8 + # c1 = *s1 + 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 0/r32/AL . . # copy byte at *EDI to AL + # c2 = *s2 + 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL + # if (c1 == 0) return false + 3d/compare-EAX-and 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 and EBX + 75/jump-if-not-equal $kernel-string-equal?:false/disp8 + # ++i + 41/increment-ECX + # ++s1 + 47/increment-EDI + # ++s2 + 46/increment-ESI + eb/jump $kernel-string-equal?:loop/disp8 +$kernel-string-equal?:break: + # return *s1 == 0 + 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 0/r32/AL . . # copy byte at *EDI to AL + 3d/compare-EAX-and 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 +$kernel-string-equal?:false: + 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 + +# - tests + +test-compare-null-kernel-string-with-empty-array: + # EAX = kernel-string-equal?(Null-kernel-string, "") + # . . push args + 68/push ""/imm32 + 68/push Null-kernel-string/imm32 + # . . call + e8/call kernel-string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # 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 + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return + +test-compare-null-kernel-string-with-non-empty-array: + # EAX = kernel-string-equal?(Null-kernel-string, "Abc") + # . . push args + 68/push "Abc"/imm32 + 68/push Null-kernel-string/imm32 + # . . call + e8/call kernel-string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # 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 + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return + +test-compare-kernel-string-with-equal-array: + # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Abc") + # . . push args + 68/push "Abc"/imm32 + 68/push _test-Abc-kernel-string/imm32 + # . . call + e8/call kernel-string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # 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 + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return + +test-compare-kernel-string-with-inequal-array: + # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Adc") + # . . push args + 68/push "Adc"/imm32 + 68/push _test-Abc-kernel-string/imm32 + # . . call + e8/call kernel-string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # 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 + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return + +test-compare-kernel-string-with-empty-array: + # EAX = kernel-string-equal?(_test-Abc-kernel-string, "") + # . . push args + 68/push ""/imm32 + 68/push _test-Abc-kernel-string/imm32 + # . . call + e8/call kernel-string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # 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 + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + c3/return + +test-compare-kernel-string-with-shorter-array: + # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Ab") + # . . push args + 68/push "Ab"/imm32 + 68/push _test-Abc-kernel-string/imm32 + # . . call + e8/call kernel-string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(EAX, 0, msg) + # . . push args + 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 + # . . discard args + 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?(_test-Abc-kernel-string, "Abcd") + # . . push args + 68/push "Abcd"/imm32 + 68/push _test-Abc-kernel-string/imm32 + # . . call + e8/call kernel-string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # check-ints-equal(EAX, 0, msg) + # . . push args + 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 + # . . discard args + 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 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX + # if (EAX == b/EBX) print('.') and return + 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-stderr('.') + # . . push args + 68/push "."/imm32 + # . . call + e8/call write-stderr/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . return + eb/jump $check-ints-equal:end/disp8 + # otherwise print(msg) +$check-ints-equal:else: + # copy msg into ECX + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # copy *(EBP+16) to ECX + # print(ECX) + # . . push args + 51/push-ECX + # . . call + e8/call write-stderr/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # print newline + # . . push args + 68/push Newline/imm32 + # . . call + e8/call write-stderr/disp32 + # . . discard args + 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 + +write-stderr: # s : (address array byte) -> <void> + # . 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 + # . . x = s+4 + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 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 5/rm32/EBP . . . 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 + +== data 0x0a000000 + +Newline: + # size + 1/imm32 + # data + 0a/newline + +# for kernel-string-equal tests +Null-kernel-string: + 00/null + +_test-Abc-kernel-string: + 41/A 62/b 63/c 00/null + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex12 b/examples/ex12 new file mode 100755 index 00000000..c13a86ed --- /dev/null +++ b/examples/ex12 Binary files differdiff --git a/examples/ex12.subx b/examples/ex12.subx new file mode 100644 index 00000000..358da1d3 --- /dev/null +++ b/examples/ex12.subx @@ -0,0 +1,45 @@ +# Example showing mmap syscall. +# Create a new segment using mmap, save the address, write to it. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex12.subx -o examples/ex12 +# $ ./subx run examples/ex12 +# You shouldn't get a segmentation fault. + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: + # syscall(mmap, 0x1000) + bb/copy-to-EBX Mmap-new-segment/imm32 + b8/copy-to-EAX 0x5a/imm32/mmap + cd/syscall 0x80/imm8 + + # write to *EAX to check that we have access to the newly-allocated segment + c7 0/subop/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 + +== data 0x0a000000 + +# various constants used here were found in the Linux sources (search for file mman-common.h) +Mmap-new-segment: # type mmap_arg_struct + # addr + 0/imm32 + # len + 0x100/imm32 + # protection flags + 3/imm32 # PROT_READ | PROT_WRITE + # sharing flags + 0x22/imm32 # MAP_PRIVATE | MAP_ANONYMOUS + # fd + -1/imm32 # since MAP_ANONYMOUS is specified + # offset + 0/imm32 # since MAP_ANONYMOUS is specified + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex2 b/examples/ex2 new file mode 100755 index 00000000..55a87a3f --- /dev/null +++ b/examples/ex2 Binary files differdiff --git a/examples/ex2.subx b/examples/ex2.subx new file mode 100644 index 00000000..025eb0fa --- /dev/null +++ b/examples/ex2.subx @@ -0,0 +1,23 @@ +# Add 1 and 1, and return the result in the exit code. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex2.subx -o examples/ex2 +# $ ./subx run examples/ex2 +# Expected result: +# $ echo $? +# 2 + +== code 0x09000000 + +Entry: +# EBX = 1 +bb/copy-to-EBX 1/imm32 +# increment EBX +43/increment-EBX +# syscall(exit, EBX) +b8/copy-to-EAX 1/imm32/exit +cd/syscall 0x80/imm8 + +== data 0x0a000000 + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex3 b/examples/ex3 new file mode 100755 index 00000000..d85aba8e --- /dev/null +++ b/examples/ex3 Binary files differdiff --git a/examples/ex3.subx b/examples/ex3.subx new file mode 100644 index 00000000..1d52e87f --- /dev/null +++ b/examples/ex3.subx @@ -0,0 +1,39 @@ +# Add the first 10 numbers, and return the result in the exit code. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex3.subx -o examples/ex3 +# $ ./subx run examples/ex3 +# Expected result: +# $ echo $? +# 55 + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: + # result: EBX = 0 + bb/copy-to-EBX 0/imm32 + # counter: ECX = 1 + b9/copy-to-ECX 1/imm32 + +$loop: + # if (counter > 10) break + 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/increment-ECX + # loop + eb/jump $loop/disp8 + +$exit: + # syscall(exit, EBX) + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 + +== data 0x0a000000 + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex4 b/examples/ex4 new file mode 100755 index 00000000..66fb61e6 --- /dev/null +++ b/examples/ex4 Binary files differdiff --git a/examples/ex4.subx b/examples/ex4.subx new file mode 100644 index 00000000..31c1c10c --- /dev/null +++ b/examples/ex4.subx @@ -0,0 +1,42 @@ +# Read a character from stdin, save it to a global, write it to stdout. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex4.subx -o examples/ex4 +# $ ./subx run examples/ex4 + +== data 0x0a000000 + +# the global variable we save to +X: + 0/imm32 # space for read() to write to + +== code 0x09000000 + +Entry: +# syscall(read, stdin, X, 1) +# . fd = 0 (stdin) +bb/copy-to-EBX 0/imm32 +# . data = X (location to write result to) +b9/copy-to-ECX X/imm32 +# . size = 1 character +ba/copy-to-EDX 1/imm32 +# . syscall +b8/copy-to-EAX 3/imm32/read +cd/syscall 0x80/imm8 + +# syscall(write, stdout, X, 1) +# . fd = 1 (stdout) +bb/copy-to-EBX 1/imm32 +# . initialize X (location to read from) +b9/copy-to-ECX X/imm32 +# . size = 1 character +ba/copy-to-EDX 1/imm32 +# . syscall +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 + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex5 b/examples/ex5 new file mode 100755 index 00000000..37689c98 --- /dev/null +++ b/examples/ex5 Binary files differdiff --git a/examples/ex5.subx b/examples/ex5.subx new file mode 100644 index 00000000..6f5b1d90 --- /dev/null +++ b/examples/ex5.subx @@ -0,0 +1,45 @@ +# Read a character from stdin, save it to a local on the stack, write it to stdout. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex5.subx -o examples/ex5 +# $ ./subx run examples/ex5 + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: + + # 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) + # . fd = 0 (stdin) + bb/copy-to-EBX 0/imm32 + # . data = 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 + # . size = 1 character + ba/copy-to-EDX 1/imm32 + # . syscall + b8/copy-to-EAX 3/imm32/read + cd/syscall 0x80/imm8 + + # syscall(write, stdout, x, 1) + # . fd = 1 (stdout) + bb/copy-to-EBX 1/imm32 + # . data = 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 + # . size = 1 character + ba/copy-to-EDX 1/imm32 + # . syscall + 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 + +== data 0x0a000000 + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex6 b/examples/ex6 new file mode 100755 index 00000000..aa84591c --- /dev/null +++ b/examples/ex6 Binary files differdiff --git a/examples/ex6.subx b/examples/ex6.subx new file mode 100644 index 00000000..a90f11df --- /dev/null +++ b/examples/ex6.subx @@ -0,0 +1,37 @@ +# Print out a (global variable) string to stdout. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex6.subx -o examples/ex6 +# $ ./subx run examples/ex6 +# Hello, world! + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: + # syscall(write, stdout, X, Size) + # . fd = 1 (stdout) + bb/copy-to-EBX 1/imm32 + # . initialize X (location to write result to) + 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 + # . syscall + 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 + +== data 0x0a000000 + +Size: # size of string + 0x0e/imm32 # 14 +X: # string to print + 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/examples/ex7 b/examples/ex7 new file mode 100755 index 00000000..28c5bda9 --- /dev/null +++ b/examples/ex7 Binary files differdiff --git a/examples/ex7.subx b/examples/ex7.subx new file mode 100644 index 00000000..d3b33f23 --- /dev/null +++ b/examples/ex7.subx @@ -0,0 +1,107 @@ +# Example showing file syscalls. +# +# Create a file, open it for writing, write a character to it, close it, open +# it for reading, read a character from it, close it, delete it, and return +# the character read. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex7.subx -o examples/ex7 +# $ ./subx run examples/ex7 +# Expected result: +# $ echo $? +# 97 + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: + # 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 + # save stream + 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) + # . load stream + 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 + + # 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 + # . + 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 + # . save Stream + 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) + # . load stream + 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 + + # 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 + # + 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(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 + # + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 + +== data 0x0a000000 + +Stream: + 0/imm32 +A: + 61/imm32/A +B: + 0/imm32 +Filename: + 2e 66 6f 6f 00 00 00 00 +# . f o o null + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex8 b/examples/ex8 new file mode 100755 index 00000000..5be0e1e6 --- /dev/null +++ b/examples/ex8 Binary files differdiff --git a/examples/ex8.subx b/examples/ex8.subx new file mode 100644 index 00000000..9d7255e1 --- /dev/null +++ b/examples/ex8.subx @@ -0,0 +1,61 @@ +# Example reading commandline arguments: compute length of first arg. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex8.subx -o examples/ex8 +# $ ./subx run examples/ex8 abc de fghi +# Expected result: +# $ echo $? +# 3 # length of 'abc' +# +# At the start of a SubX program: +# argc: *ESP +# argv[0]: *(ESP+4) +# argv[1]: *(ESP+8) +# ... +# Locals start from ESP-4 downwards. + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: + # . prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # EAX = ascii-length(argv[1]) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + # . . call + e8/call ascii-length/disp32 + # . . discard args + 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 + +ascii-length: # s : (address array byte) -> n/EAX + # EDX = s + 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/EAX = 0 + b8/copy-to-EAX 0/imm32 +$ascii-length:loop: + # var c/ECX = *s + 8a/copy-byte 0/mod/* 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL + # if (c == '\0') break + 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX + 74/jump-if-equal $ascii-length:end/disp8 + # ++s + 42/increment-EDX + # ++result + 40/increment-EAX + # loop + eb/jump $ascii-length:loop/disp8 +$ascii-length:end: + # return EAX + c3/return + +== data 0x0a000000 + +# . . vim:nowrap:textwidth=0 diff --git a/examples/ex9 b/examples/ex9 new file mode 100755 index 00000000..fce7629c --- /dev/null +++ b/examples/ex9 Binary files differdiff --git a/examples/ex9.subx b/examples/ex9.subx new file mode 100644 index 00000000..e3318b05 --- /dev/null +++ b/examples/ex9.subx @@ -0,0 +1,55 @@ +# Example showing arg order on the stack. +# +# Show difference between ascii codes of first letter of first arg and first +# letter of second arg. +# +# To run (from the subx directory): +# $ ./subx translate examples/ex9.subx -o examples/ex9 +# $ ./subx run examples/ex9 z x +# Expected result: +# $ echo $? +# 2 +# +# At the start of a SubX program: +# argc: *ESP +# argv[0]: *(ESP+4) +# argv[1]: *(ESP+8) +# ... +# Locals start from ESP-4 downwards. + +== code 0x09000000 +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: + # . prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # ascii-difference(argv[1], argv[2]) + # . . push argv[2] + ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + # . . push argv[1] + ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + # . . call + e8/call ascii-difference/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # 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 + +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 + +== data 0x0a000000 + +# . . vim:nowrap:textwidth=0 |