Reindent all SubX code to make some room for the new comment style.
This commit is contained in:
Kartik Agaram 2018-11-30 10:54:42 -08:00
parent e9661581f0
commit 6030d7e2e5
24 changed files with 2963 additions and 2963 deletions

View File

@ -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) -> <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, 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) -> <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
# 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

View File

@ -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 -> <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
# 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

View File

@ -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

View File

@ -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

View File

@ -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) -> <void>
# 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) -> <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
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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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) -> <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)
# 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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