5675 - move helpers from subx-common into layers

This undoes 5672 in favor of a new plan:

Layers 000 - 099 are for running without syntax sugar. We use them for
building syntax-sugar passes.

Layers 100 and up are for running with all syntax sugar.

The layers are arranged in approximate order so more phases rely on earlier
layers than later ones.

I plan to not use intermediate syntax sugar (just sigils without calls,
or sigils and calls without braces) anywhere except in the specific passes
implementing them.
This commit is contained in:
Kartik Agaram 2019-09-19 21:01:43 -07:00
parent 881c7f0270
commit fd91f7f61b
48 changed files with 2347 additions and 2430 deletions

View File

@ -28,6 +28,8 @@ Heap:
# a reasonable default
Heap-size:
0x200000/imm32/2MB
#? # TODO: reclaim space allocated in tests.
#? 0x2000000/imm32/32MB
== code
# instruction effective address register displacement immediate

116
074write-stream-data.subx Normal file
View File

@ -0,0 +1,116 @@
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
# write an entire stream's contents to a buffered-file
# ways to do this:
# - construct a 'maximal slice' and pass it to write-slice-buffered
# - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
# we'll go with the first way for now
write-stream-data: # f : (address buffered-file), 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
56/push-esi
# esi = s
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi
# var slice/ecx = {s->data, s->data + s->write}
# . push s->data + s->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
# . push s->data
8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 0xc/disp8 . # copy esi+12 to eax
50/push-eax
# . ecx = esp
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# write-slice-buffered(f, slice)
# . . push args
51/push-ecx
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call write-slice-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$write-stream-data:end:
# . restore locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . restore registers
5e/pop-to-esi
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-data:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
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-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# initialize input
# . write(_test-input-stream, "abcd")
# . . push args
68/push "abcd"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-stream-data(_test-output-buffered-file, _test-input-stream)
# . . push args
68/push _test-input-stream/imm32
68/push _test-output-buffered-file/imm32
# . . call
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check that the write happened as expected
# . flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . check-stream-equal(_test-output-stream, "abcd", msg)
# . . push args
68/push "F - test-write-stream-data"/imm32
68/push "abcd"/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return

252
076next-word.subx Normal file
View File

@ -0,0 +1,252 @@
# Tokenize by whitespace.
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
# (re)compute the bounds of the next word in the line
# return empty string on reaching end of file
next-word: # line : (address stream byte), out : (address slice)
# . 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
56/push-esi
57/push-edi
# esi = line
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
# edi = out
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi
# skip-chars-matching(line, ' ')
# . . push args
68/push 0x20/imm32/space
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call skip-chars-matching/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$next-word:check0:
# if (line->read >= line->write) clear out and return
# . eax = line->read
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax
# . if (eax < line->write) goto next check
3b/compare 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # compare eax with *esi
7c/jump-if-lesser $next-word:check-for-comment/disp8
# . return out = {0, 0}
c7 0/subop/copy 0/mod/direct 7/rm32/edi . . . . . 0/imm32 # copy to *edi
c7 0/subop/copy 1/mod/*+disp8 7/rm32/edi . . . . 4/disp8 0/imm32 # copy to *(edi+4)
eb/jump $next-word:end/disp8
$next-word:check-for-comment:
# out->start = &line->data[line->read]
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax
89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi
# if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
# . eax = line->data[line->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 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL
# . compare
3d/compare-eax-and 0x23/imm32/pound
75/jump-if-not-equal $next-word:regular-word/disp8
$next-word:comment:
# . out->end = &line->data[line->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
89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4)
# . line->read = line->write
89/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy eax to *(esi+4)
# . return
eb/jump $next-word:end/disp8
$next-word:regular-word:
# otherwise skip-chars-not-matching-whitespace(line) # including trailing newline
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call skip-chars-not-matching-whitespace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# out->end = &line->data[line->read]
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax
89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4)
$next-word:end:
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
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-next-word:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-stream)
# . . push args
68/push _test-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# var slice/ecx = {0, 0}
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# write(_test-stream, " ab")
# . . push args
68/push " ab"/imm32
68/push _test-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# next-word(_test-stream, slice)
# . . push args
51/push-ecx
68/push _test-stream/imm32
# . . call
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-ints-equal(slice->start - _test-stream->data, 2, msg)
# . check-ints-equal(slice->start - _test-stream, 14, msg)
# . . push args
68/push "F - test-next-word: start"/imm32
68/push 0xe/imm32
# . . push slice->start - _test-stream
8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# check-ints-equal(slice->end - _test-stream->data, 4, msg)
# . check-ints-equal(slice->end - _test-stream, 16, msg)
# . . push args
68/push "F - test-next-word: end"/imm32
68/push 0x10/imm32
# . . push slice->end - _test-stream
8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax
81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-next-word-returns-whole-comment:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-stream)
# . . push args
68/push _test-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# var slice/ecx = {0, 0}
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# write(_test-stream, " # a")
# . . push args
68/push " # a"/imm32
68/push _test-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# next-word(_test-stream, slice)
# . . push args
51/push-ecx
68/push _test-stream/imm32
# . . call
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-ints-equal(slice->start - _test-stream->data, 2, msg)
# . check-ints-equal(slice->start - _test-stream, 14, msg)
# . . push args
68/push "F - test-next-word-returns-whole-comment: start"/imm32
68/push 0xe/imm32
# . . push slice->start - _test-stream
8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# check-ints-equal(slice->end - _test-stream->data, 5, msg)
# . check-ints-equal(slice->end - _test-stream, 17, msg)
# . . push args
68/push "F - test-next-word-returns-whole-comment: end"/imm32
68/push 0x11/imm32
# . . push slice->end - _test-stream
8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax
81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-next-word-returns-empty-string-on-eof:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-stream)
# . . push args
68/push _test-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# var slice/ecx = {0, 0}
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# write nothing to _test-stream
# next-word(_test-stream, slice)
# . . push args
51/push-ecx
68/push _test-stream/imm32
# . . call
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-ints-equal(slice->end - slice->start, 0, msg)
# . . push args
68/push "F - test-next-word-returns-empty-string-on-eof"/imm32
68/push 0/imm32
# . . push slice->end - slice->start
8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax
2b/subtract 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # subtract *ecx from eax
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return

631
077subx-words.subx Normal file
View File

@ -0,0 +1,631 @@
# Helpers for parsing SubX words, with their rules for hex, labels and metadata.
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
has-metadata?: # word : (address slice), s : (address string) -> eax : boolean
# pseudocode:
# var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name
# curr = twig->end
# while true
# twig = next-token-from-slice(curr, word->end, '/')
# if (twig.empty()) break
# if (slice-equal?(twig, s)) return true
# curr = twig->end
# 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
56/push-esi
57/push-edi
# esi = word
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
# edx = word->end
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 4/disp8 . # copy *(esi+4) to edx
# var twig/edi : (address slice) = {0, 0}
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi
# next-token-from-slice(word->start, word->end, '/', twig)
# . . push args
57/push-edi
68/push 0x2f/imm32/slash
52/push-edx
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call next-token-from-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# curr/ecx = twig->end
8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx
$has-metadata?:loop:
# next-token-from-slice(curr, word->end, '/', twig)
# . . push args
57/push-edi
68/push 0x2f/imm32/slash
52/push-edx
51/push-ecx
# . . call
e8/call next-token-from-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# if (slice-empty?(twig)) return false
# . eax = slice-empty?(twig)
# . . push args
57/push-edi
# . . call
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != 0) return false
3d/compare-eax-and 0/imm32
75/jump-if-not-equal $has-metadata?:false/disp8
# if (slice-equal?(twig, s)) return true
# . eax = slice-equal?(twig, s)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
57/push-edi
# . . call
e8/call slice-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != 0) return true
3d/compare-eax-and 0/imm32
75/jump-if-not-equal $has-metadata?:true/disp8
# curr = twig->end
8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx
eb/jump $has-metadata?:loop/disp8
$has-metadata?:true:
b8/copy-to-eax 1/imm32/true
eb/jump $has-metadata?:end/disp8
$has-metadata?:false:
b8/copy-to-eax 0/imm32/false
$has-metadata?:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
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
test-has-metadata-true:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "ab/imm32"
b8/copy-to-eax "ab/imm32"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var in/esi : (address slice) = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
# eax = has-metadata?(esi, "imm32")
# . . push args
68/push "imm32"/imm32
56/push-esi
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-has-metadata-true"/imm32
68/push 1/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-has-metadata-false:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "ab/c"
b8/copy-to-eax "ab/c"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var in/esi : (address slice) = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
# eax = has-metadata?(esi, "d")
# . . push args
68/push "d"/imm32
56/push-esi
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-has-metadata-false"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-has-metadata-ignore-name:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "a/b"
b8/copy-to-eax "a/b"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var in/esi : (address slice) = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
# eax = has-metadata?(esi, "a")
# . . push args
68/push "a"/imm32
56/push-esi
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-has-metadata-ignore-name"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-has-metadata-multiple-true:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "a/b/c"
b8/copy-to-eax "a/b/c"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var in/esi : (address slice) = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
# eax = has-metadata?(esi, "c")
# . . push args
68/push "c"/imm32
56/push-esi
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-has-metadata-multiple-true"/imm32
68/push 1/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-has-metadata-multiple-false:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "a/b/c"
b8/copy-to-eax "a/b/c"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var in/esi : (address slice) = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
# eax = has-metadata?(esi, "d")
# . . push args
68/push "d"/imm32
56/push-esi
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-has-metadata-multiple-false"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
# conditions for 'valid' names that are not at risk of looking like hex numbers
# keep in sync with the rules in labels.cc
#: - if it starts with a digit, it's treated as a number. If it can't be
#: parsed as hex it will raise an error.
#: - if it starts with '-' it's treated as a number.
#: - if it starts with '0x' it's treated as a number. (redundant)
#: - if it's two characters long, it can't be a name. Either it's a hex
#: byte, or it raises an error.
is-valid-name?: # in : (address slice) -> 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
56/push-esi
# esi = in
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
# start/ecx = in->start
8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx
# end/eax = in->end
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax
$is-valid-name?:check0:
# if (start >= end) return false
39/compare 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # compare ecx with eax
73/jump-if-greater-or-equal-unsigned $is-valid-name?:false/disp8
$is-valid-name?:check1:
# eax -= ecx
29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax
# if (eax == 2) return false
3d/compare-eax-and 2/imm32
74/jump-if-equal $is-valid-name?:false/disp8
$is-valid-name?:check2:
# c/eax = *ecx
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL
# if (c == "-") return false
3d/compare-eax-and 2d/imm32/-
74/jump-if-equal $is-valid-name?:false/disp8
$is-valid-name?:check3a:
# if (c < "0") return true
3d/compare-eax-with 30/imm32/0
7c/jump-if-lesser $is-valid-name?:true/disp8
$is-valid-name?:check3b:
# if (c > "9") return true
3d/compare-eax-with 39/imm32/9
7f/jump-if-greater $is-valid-name?:true/disp8
$is-valid-name?:false:
# return false
b8/copy-to-eax 0/imm32/false
eb/jump $is-valid-name?:end/disp8
$is-valid-name?:true:
# return true
b8/copy-to-eax 1/imm32/true
$is-valid-name?: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
test-is-valid-name-digit-prefix:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "34"
b8/copy-to-eax "34"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# eax = is-valid-name?(slice)
# . . push args
51/push-ecx
# . . call
e8/call is-valid-name?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-is-valid-name-digit-prefix"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-is-valid-name-negative-prefix:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "-0x34"
b8/copy-to-eax "-0x34"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# eax = is-valid-name?(slice)
# . . push args
51/push-ecx
# . . call
e8/call is-valid-name?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-is-valid-name-negative-prefix"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-is-valid-name-0x-prefix:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "0x34"
b8/copy-to-eax "0x34"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# eax = is-valid-name?(slice)
# . . push args
51/push-ecx
# . . call
e8/call is-valid-name?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-is-valid-name-0x-prefix"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-is-valid-name-starts-with-pre-digit:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "/03"
b8/copy-to-eax "/03"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# eax = is-valid-name?(slice)
# . . push args
51/push-ecx
# . . call
e8/call is-valid-name?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32
68/push 1/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-is-valid-name-starts-with-post-digit:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "q34"
b8/copy-to-eax "q34"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# eax = is-valid-name?(slice)
# . . push args
51/push-ecx
# . . call
e8/call is-valid-name?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-is-valid-name-starts-with-post-digit"/imm32
68/push 1/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-is-valid-name-starts-with-digit:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# (eax..ecx) = "0x34"
b8/copy-to-eax "0x34"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# eax = is-valid-name?(slice)
# . . push args
51/push-ecx
# . . call
e8/call is-valid-name?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-is-valid-name-starts-with-digit"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
is-label?: # word : (address slice) -> 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
# ecx = word
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
# ecx = word->end
8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 1/r32/ecx 4/disp8 . # copy *(ecx+4) to ecx
# return *(word->end - 1) == ':'
# . eax = 0
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
# . eax = *((char *) word->end - 1)
8a/copy-byte 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/AL -1/disp8 . # copy byte at *(ecx-1) to AL
# . return (eax == ':')
3d/compare-eax-and 0x3a/imm32/colon
b8/copy-to-eax 1/imm32/true
74/jump-if-equal $is-label?:end/disp8
b8/copy-to-eax 0/imm32/false
$is-label?:end:
# . 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
test-is-label?:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
$test-is-label?:true:
# (eax..ecx) = "AAA:"
b8/copy-to-eax "AAA:"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# is-label?(slice/ecx)
# . . push args
51/push-ecx
# . . call
e8/call is-label?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-is-label?:true"/imm32
68/push 1/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$test-is-label?:false:
# (eax..ecx) = "AAA"
b8/copy-to-eax "AAA"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# is-label?(slice/ecx)
# . . push args
51/push-ecx
# . . call
e8/call is-label?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-is-label?:false"/imm32
68/push 0/imm32
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . 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

249
078emit-hex.subx Normal file
View File

@ -0,0 +1,249 @@
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
# print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
emit-hex: # out : (address buffered-file), n : int, width : 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
52/push-edx
53/push-ebx
57/push-edi
# edi = out
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi
# ebx = n
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 3/r32/ebx 0xc/disp8 . # copy *(ebp+12) to ebx
# edx = width
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx
# var curr/ecx = 0
31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx
$emit-hex:loop:
# if (curr >= width) break
39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx
7d/jump-if-greater-or-equal $emit-hex:end/disp8
# print-byte-buffered(out, ebx)
# . . push args
53/push-ebx
57/push-edi
# . . call
e8/call print-byte-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-byte-buffered(out, ' ')
# . . push args
68/push 0x20/imm32/space
57/push-edi
# . . call
e8/call write-byte-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# ebx = ebx >> 8
c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/ebx . . . . . 8/imm8 # shift ebx right by 8 bits, while padding zeroes
$emit-hex:continue:
# ++curr
41/increment-ecx
eb/jump $emit-hex:loop/disp8
$emit-hex:end:
# . restore registers
5f/pop-to-edi
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-emit-hex-single-byte:
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# emit-hex(_test-output-buffered-file, 0xab, 1)
# . . push args
68/push 1/imm32
68/push 0xab/imm32
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(*_test-output-stream->data, 'ab ', msg)
# . . push args
68/push "F - test-emit-hex-single-byte"/imm32
68/push 0x206261/imm32
# . . push *_test-output-stream->data
b8/copy-to-eax _test-output-stream/imm32
ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 0xc/disp8 . # push *(eax+12)
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
test-emit-hex-multiple-byte:
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# emit-hex(_test-output-buffered-file, 0x1234, 2)
# . . push args
68/push 2/imm32
68/push 0x1234/imm32
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-stream-equal(_test-output-stream, "34 12 ", msg)
# . . push args
68/push "F - test-emit-hex-multiple-byte/1"/imm32
68/push "34 12 "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
test-emit-hex-zero-pad:
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# emit-hex(_test-output-buffered-file, 0xab, 2)
# . . push args
68/push 2/imm32
68/push 0xab/imm32
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check(_test-output-stream->data == 'ab 00 ')
# . . push args
68/push "F - test-emit-hex-zero-pad/1"/imm32
68/push "ab 00 "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
test-emit-hex-negative:
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# emit-hex(_test-output-buffered-file, -1, 2)
# . . push args
68/push 2/imm32
68/push -1/imm32
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-stream-equal(_test-output-stream == "ff ff ")
# . . push args
68/push "F - test-emit-hex-negative/1"/imm32
68/push "ff ff "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
# . . vim:nowrap:textwidth=0

484
079emit.subx Normal file
View File

@ -0,0 +1,484 @@
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
# If datum of 'word' is not a valid name, it must be a hex int. Parse and print
# it in 'width' bytes of hex, least significant first.
# Otherwise just print the entire word including metadata.
# Always print a trailing space.
emit: # out : (address buffered-file), word : (address slice), width : 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
56/push-esi
57/push-edi
# esi = word
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi
# var name/edi : (address slice) = {0, 0}
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi
# datum = next-token-from-slice(word->start, word->end, '/')
# . . push args
57/push-edi
68/push 0x2f/imm32/slash
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call next-token-from-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# if (is-valid-name?(datum)) write-slice-buffered(out, word) and return
# . eax = is-valid-name?(name)
# . . push args
57/push-edi
# . . call
e8/call is-valid-name?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != 0)
3d/compare-eax-and 0/imm32
74/jump-if-equal $emit:hex-int/disp8
$emit:name:
# . write-slice-buffered(out, word)
# . . push args
56/push-esi
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call write-slice-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write-buffered(out, " ")
# . . push args
68/push Space/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . return
eb/jump $emit:end/disp8
# otherwise emit-hex(out, parse-hex-int(datum), width)
# (Weird shit can happen here if the datum of 'word' isn't either a valid
# name or a hex number, but we're only going to be passing in real legal
# programs. We just want to make sure that valid names aren't treated as
# (valid) hex numbers.)
$emit:hex-int:
# . value/eax = parse-hex-int(datum)
# . . push args
57/push-edi
# . . call
e8/call parse-hex-int/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . emit-hex(out, value, width)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
50/push-eax
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$emit:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
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-emit-number:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# (eax..ecx) = "30"
b8/copy-to-eax "30"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# emit(_test-output-buffered-file, slice, 1)
# . . push args
68/push 1/imm32
51/push-ecx
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-stream-equal(_test-output-stream, "30 ", msg)
# . . push args
68/push "F - test-emit-number/1"/imm32
68/push "30 "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-emit-negative-number:
# test support for sign-extending negative numbers
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# (eax..ecx) = "-2"
b8/copy-to-eax "-2"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# emit(_test-output-buffered-file, slice, 2)
# . . push args
68/push 2/imm32
51/push-ecx
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-stream-equal(_test-output-stream, "fe ff ", msg)
# . . push args
68/push "F - test-emit-number/1"/imm32
68/push "fe ff "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-emit-number-with-metadata:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# (eax..ecx) = "-2/foo"
b8/copy-to-eax "-2/foo"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# emit(_test-output-buffered-file, slice, 2)
# . . push args
68/push 2/imm32
51/push-ecx
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# the '/foo' will have no impact on the output
# check-stream-equal(_test-output-stream, "fe ff ", msg)
# . . push args
68/push "F - test-emit-number-with-metadata"/imm32
68/push "fe ff "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-emit-non-number:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# (eax..ecx) = "xyz"
b8/copy-to-eax "xyz"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# emit(_test-output-buffered-file, slice, 2)
# . . push args
68/push 2/imm32
51/push-ecx
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-stream-equal(_test-output-stream, "xyz", msg)
# . . push args
68/push "F - test-emit-non-number"/imm32
68/push "xyz "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-emit-non-number-with-metadata:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# (eax..ecx) = "xyz/"
b8/copy-to-eax "xyz/"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# emit(_test-output-buffered-file, slice, 2)
# . . push args
68/push 2/imm32
51/push-ecx
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-stream-equal(_test-output-stream, "xyz/", msg)
# . . push args
68/push "F - test-emit-non-number-with-metadata"/imm32
68/push "xyz/ "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-emit-non-number-with-all-hex-digits-and-metadata:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# (eax..ecx) = "abcd/xyz"
b8/copy-to-eax "abcd/xyz"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
05/add-to-eax 4/imm32
# var slice/ecx = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# emit(_test-output-buffered-file, slice, 2)
# . . push args
68/push 2/imm32
51/push-ecx
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # dump output {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, _test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# check-stream-equal(_test-output-stream, "abcd/xyz")
# . . push args
68/push "F - test-emit-non-number-with-all-hex-digits"/imm32
68/push "abcd/xyz "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilog
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return

238
083subx-widths.subx Normal file
View File

@ -0,0 +1,238 @@
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
compute-width: # word : (address array byte) -> eax : int
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
51/push-ecx
# eax = word
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to ecx
# ecx = word + word->length
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx
# eax = word->data
05/add-to-eax 4/imm32
# var in/ecx : (address slice) = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# return compute-width-of-slice(ecx)
# . . push args
51/push-ecx
# . . call
e8/call compute-width-of-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$compute-width:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/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
compute-width-of-slice: # s : (address slice) -> eax : int
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
51/push-ecx
# ecx = s
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
# if (has-metadata?(word, "imm32")) return 4
# . eax = has-metadata?(word, "imm32")
# . . push args
68/push "imm32"/imm32
51/push-ecx
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != 0) return 4
3d/compare-eax-and 0/imm32
b8/copy-to-eax 4/imm32 # ZF is set, so we can overwrite eax now
75/jump-if-not-equal $compute-width-of-slice:end/disp8
# if (has-metadata?(word, "disp32")) return 4
# . eax = has-metadata?(word, "disp32")
# . . push args
68/push "disp32"/imm32
51/push-ecx
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != 0) return 4
3d/compare-eax-and 0/imm32
b8/copy-to-eax 4/imm32 # ZF is set, so we can overwrite eax now
75/jump-if-not-equal $compute-width-of-slice:end/disp8
# if (has-metadata?(word, "imm16")) return 2
# . eax = has-metadata?(word, "imm16")
# . . push args
68/push "imm16"/imm32
51/push-ecx
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != 0) return 2
3d/compare-eax-and 0/imm32
b8/copy-to-eax 2/imm32 # ZF is set, so we can overwrite eax now
75/jump-if-not-equal $compute-width-of-slice:end/disp8
# if (has-metadata?(word, "disp16")) return 2
# . eax = has-metadata?(word, "disp16")
# . . push args
68/push "disp16"/imm32
51/push-ecx
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != 0) return 2
3d/compare-eax-and 0/imm32
b8/copy-to-eax 2/imm32 # ZF is set, so we can overwrite eax now
75/jump-if-not-equal $compute-width-of-slice:end/disp8
# otherwise return 1
b8/copy-to-eax 1/imm32
$compute-width-of-slice:end:
# . 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
test-compute-width:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
$test-compute-width:imm8:
# eax = compute-width("0x2/imm8")
# . . push args
68/push "0x2/imm8"/imm32
# . . call
e8/call compute-width/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-compute-width: 0x2/imm8"/imm32
50/push-eax
68/push 1/imm32
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$test-compute-width:imm16:
# eax = compute-width("4/imm16")
# . . push args
68/push "4/imm16"/imm32
# . . call
e8/call compute-width/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 2, msg)
# . . push args
68/push "F - test-compute-width: 4/imm16"/imm32
50/push-eax
68/push 2/imm32
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$test-compute-width:imm32:
# eax = compute-width("4/imm32")
# . . push args
68/push "4/imm32"/imm32
# . . call
e8/call compute-width/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 4, msg)
# . . push args
68/push "F - test-compute-width: 4/imm32"/imm32
50/push-eax
68/push 4/imm32
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$test-compute-width:disp8:
# eax = compute-width("foo/disp8")
# . . push args
68/push "foo/disp8"/imm32
# . . call
e8/call compute-width/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-compute-width: foo/disp8"/imm32
50/push-eax
68/push 1/imm32
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$test-compute-width:disp16:
# eax = compute-width("foo/disp16")
# . . push args
68/push "foo/disp16"/imm32
# . . call
e8/call compute-width/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 2, msg)
# . . push args
68/push "F - test-compute-width: foo/disp16"/imm32
50/push-eax
68/push 2/imm32
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$test-compute-width:disp32:
# eax = compute-width("foo/disp32")
# . . push args
68/push "foo/disp32"/imm32
# . . call
e8/call compute-width/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 4, msg)
# . . push args
68/push "F - test-compute-width: foo/disp32"/imm32
50/push-eax
68/push 4/imm32
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$test-compute-width:no-metadata:
# eax = compute-width("45")
# . . push args
68/push "45"/imm32
# . . call
e8/call compute-width/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-compute-width: 45 (no metadata)"/imm32
50/push-eax
68/push 1/imm32
# . . call
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
# . . vim:nowrap:textwidth=0

142
084emit-hex-array.subx Normal file
View File

@ -0,0 +1,142 @@
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
# print 'arr' in hex with a space after every byte
emit-hex-array: # out : (address buffered-file), arr : (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
57/push-edi
# edi = out
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi
# edx = arr # <== 0xbdffffe4
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx
# curr/ecx = arr->data
8d/copy-address 1/mod/*+disp8 2/rm32/edx . . . 1/r32/ecx 4/disp8 . # copy edx+4 to ecx
# max/edx = arr->data + arr->length
8b/copy 0/mod/indirect 2/rm32/edx . . . 2/r32/edx . . # copy *edx to edx
01/add 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # add ecx to edx
# eax = 0
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
$emit-hex-array:loop:
# if (curr >= width) break
39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx
73/jump-if-greater-or-equal-unsigned $emit-hex-array:end/disp8
# emit-hex(out, *curr, width=1)
# . . push args
68/push 1/imm32/width
8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL
50/push-eax
57/push-edi
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# ++curr
41/increment-ecx
eb/jump $emit-hex-array:loop/disp8
$emit-hex-array:end:
# . restore registers
5f/pop-to-edi
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-emit-hex-array:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-buffered-file+4)
# . . push args
b8/copy-to-eax _test-output-buffered-file/imm32
05/add-to-eax 4/imm32
50/push-eax
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# var arr/ecx (address array byte) = [01, 02, 03]
68/push 0x00030201/imm32 # bytes 01 02 03
68/push 3/imm32/length
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# emit-hex-array(_test-output-buffered-file, arr)
# . . push args
51/push-ecx
68/push _test-output-buffered-file/imm32
# . . call
e8/call emit-hex-array/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # dump output {{{
#? # . write(2/stderr, "result: ^")
#? # . . push args
#? 68/push "result: ^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, _test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . rewind-stream(_test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? # . . call
#? e8/call rewind-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # }}}
# check-next-stream-line-equal(_test-output-stream, "01 02 03 ", msg)
# . . push args
68/push "F - test-emit-hex-array"/imm32
68/push "01 02 03 "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-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
# . . vim:nowrap:textwidth=0

View File

@ -104,12 +104,12 @@ You can use SubX to translate itself. For example, running natively on Linux:
```sh
# generate translator phases using the C++ translator
$ ./subx translate init.linux 0[0-6]*.subx 070---hex.subx -o hex
$ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/survey.subx -o survey
$ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/pack.subx -o pack
$ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/assort.subx -o assort
$ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/dquotes.subx -o dquotes
$ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/tests.subx -o tests
$ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/hex.subx -o hex
$ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/survey.subx -o survey
$ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/pack.subx -o pack
$ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/assort.subx -o assort
$ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/dquotes.subx -o dquotes
$ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/tests.subx -o tests
$ chmod +x hex survey pack assort dquotes tests
# use the generated translator phases to translate SubX programs

Binary file not shown.

View File

@ -7,7 +7,7 @@
# because we don't know if they refer to the line above or the line below.
#
# To run:
# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/assort.subx -o apps/assort
# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/assort.subx -o apps/assort
# $ cat x
# == code
# abc
@ -38,10 +38,10 @@ Entry: # run tests if necessary, convert stdin if not
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# - if argc > 1 and argv[1] == "test", then return run_tests()
# if (argc <= 1) goto run-main
# if (argc <= 1) goto interactive
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp
7e/jump-if-lesser-or-equal $run-main/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto run-main
7e/jump-if-lesser-or-equal $subx-assort-main:interactive/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto interactive
# . eax = kernel-string-equal?(argv[1], "test")
# . . push args
68/push "test"/imm32
@ -50,15 +50,15 @@ Entry: # run tests if necessary, convert stdin if not
e8/call kernel-string-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == 0) goto run-main
# . if (eax == 0) goto interactive
3d/compare-eax-and 0/imm32
74/jump-if-equal $run-main/disp8
74/jump-if-equal $subx-assort-main:interactive/disp8
# run-tests()
e8/call run-tests/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
eb/jump $main:end/disp8
$run-main:
eb/jump $subx-assort-main:end/disp8
$subx-assort-main:interactive:
# - otherwise convert stdin
# var ed/eax : exit-descriptor
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp
@ -66,19 +66,19 @@ $run-main:
# configure ed to really exit()
# . ed->target = 0
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax
# convert(Stdin, Stdout, Stderr, ed)
# subx-assort(Stdin, Stdout, Stderr, ed)
# . . push args
50/push-eax/ed
68/push Stderr/imm32
68/push Stdout/imm32
68/push Stdin/imm32
# . . call
e8/call convert/disp32
e8/call subx-assort/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$main:end:
$subx-assort-main:end:
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
@ -86,7 +86,7 @@ $main:end:
# table: (address stream {string, (address stream byte)}) (8 bytes per row)
# inefficient; uses sequential search for looking up segments by name
convert: # in : (address buffered-file), out : (address buffered-file) -> <void>
subx-assort: # in : (address buffered-file), out : (address buffered-file) -> <void>
# pseudocode:
# var table : (address stream) = new-stream(10 rows, 8 bytes each)
# read-segments(in, table)
@ -110,7 +110,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> <void
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$convert:read:
$subx-assort:read:
#? # print("read\n") {{{
#? # . . push args
#? 68/push "read\n"/imm32
@ -128,7 +128,7 @@ $convert:read:
e8/call read-segments/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:write:
$subx-assort:write:
#? # print("write\n") {{{
#? # . . push args
#? 68/push "write\n"/imm32
@ -146,7 +146,7 @@ $convert:write:
e8/call write-segments/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:end:
$subx-assort:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x5c/imm32 # add to esp
# . restore registers
@ -156,7 +156,7 @@ $convert:end:
5d/pop-to-ebp
c3/return
test-convert:
test-subx-assort:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
@ -320,12 +320,12 @@ test-convert:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-assort(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-assort/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . flush(_test-output-buffered-file)
@ -379,7 +379,7 @@ test-convert:
#? # }}}
# . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
# . . push args
68/push "F - test-convert/0"/imm32
68/push "F - test-subx-assort/0"/imm32
68/push "== code 0x09000000"/imm32
68/push _test-output-stream/imm32
# . . call
@ -388,7 +388,7 @@ test-convert:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "1", msg)
# . . push args
68/push "F - test-convert/1"/imm32
68/push "F - test-subx-assort/1"/imm32
68/push "1"/imm32
68/push _test-output-stream/imm32
# . . call
@ -397,7 +397,7 @@ test-convert:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
# . . push args
68/push "F - test-convert/2"/imm32
68/push "F - test-subx-assort/2"/imm32
68/push "2 3 # comment 4 inline with other contents"/imm32
68/push _test-output-stream/imm32
# . . call
@ -406,7 +406,7 @@ test-convert:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
# . . push args
68/push "F - test-convert/3"/imm32
68/push "F - test-subx-assort/3"/imm32
68/push "6 7"/imm32
68/push _test-output-stream/imm32
# . . call
@ -415,7 +415,7 @@ test-convert:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
# . . push args
68/push "F - test-convert/4"/imm32
68/push "F - test-subx-assort/4"/imm32
68/push "8 9"/imm32
68/push _test-output-stream/imm32
# . . call
@ -424,7 +424,7 @@ test-convert:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
# . . push args
68/push "F - test-convert/5"/imm32
68/push "F - test-subx-assort/5"/imm32
68/push "10 11"/imm32
68/push _test-output-stream/imm32
# . . call
@ -433,7 +433,7 @@ test-convert:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
# . . push args
68/push "F - test-convert/6"/imm32
68/push "F - test-subx-assort/6"/imm32
68/push "== data 0x0a000000"/imm32
68/push _test-output-stream/imm32
# . . call
@ -442,7 +442,7 @@ test-convert:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
# . . push args
68/push "F - test-convert/7"/imm32
68/push "F - test-subx-assort/7"/imm32
68/push "4 5/imm32"/imm32
68/push _test-output-stream/imm32
# . . call

Binary file not shown.

View File

@ -1,7 +1,7 @@
# Structured control flow using break/loop rather than jump.
#
# To run (on Linux):
# $ ./ntranslate init.linux 0*.subx apps/subx-common.subx apps/calls.subx
# $ ./ntranslate init.linux 0*.subx apps/subx-params.subx apps/calls.subx
# $ mv a.elf apps/calls
#
# Example 1:

Binary file not shown.

View File

@ -1,7 +1,7 @@
# Function calls in a single line.
#
# To run (on Linux):
# $ ./ntranslate init.linux 0*.subx apps/subx-common.subx apps/calls.subx
# $ ./ntranslate init.linux 0*.subx apps/subx-params.subx apps/calls.subx
# $ mv a.elf apps/calls
#
# Example 1:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,7 +2,7 @@
# Replace them with references to new variables in the data segment.
#
# To run:
# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/dquotes.subx -o apps/dquotes
# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/dquotes.subx -o apps/dquotes
# $ cat x
# == code
# ab "cd ef"/imm32
@ -33,11 +33,11 @@ Entry: # run tests if necessary, convert stdin if not
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# - if argc > 1 and argv[1] == "test", then return run_tests()
# if (argc <= 1) goto run-main
# - if argc > 1 and argv[1] == "test", then return run-tests()
# if (argc <= 1) goto interactive
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp
7e/jump-if-lesser-or-equal $run-main/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto run-main
7e/jump-if-lesser-or-equal $subx-dquotes-main:interactive/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto interactive
# . eax = kernel-string-equal?(argv[1], "test")
# . . push args
68/push "test"/imm32
@ -46,15 +46,15 @@ Entry: # run tests if necessary, convert stdin if not
e8/call kernel-string-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == 0) goto run-main
# . if (eax == 0) goto interactive
3d/compare-eax-and 0/imm32
74/jump-if-equal $run-main/disp8
74/jump-if-equal $subx-dquotes-main:interactive/disp8
# run-tests()
e8/call run-tests/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
eb/jump $main:end/disp8
$run-main:
eb/jump $subx-dquotes-main:end/disp8
$subx-dquotes-main:interactive:
# - otherwise convert stdin
# var ed/eax : exit-descriptor
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp
@ -62,19 +62,19 @@ $run-main:
# configure ed to really exit()
# . ed->target = 0
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax
# convert(Stdin, 1/stdout, 2/stderr, ed)
# subx-dquotes(Stdin, 1/stdout, 2/stderr, ed)
# . . push args
50/push-eax/ed
68/push Stderr/imm32
68/push Stdout/imm32
68/push Stdin/imm32
# . . call
e8/call convert/disp32
e8/call subx-dquotes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$main:end:
$subx-dquotes-main:end:
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
@ -82,7 +82,7 @@ $main:end:
# line = words separated by ' ', maybe followed by comment starting with '#'
# word = datum until '/', then 0 or more metadata separated by '/'
convert: # in : (address buffered-file), out : (address buffered-file) -> <void>
subx-dquotes: # in : (address buffered-file), out : (address buffered-file) -> <void>
# pseudocode:
# var line = new-stream(512, 1)
# var new-data-segment = new-stream(Heap, Segment-size, 1)
@ -146,7 +146,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> <void
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:line-loop:
$subx-dquotes:line-loop:
# clear-stream(line)
# . . push args
51/push-ecx
@ -162,11 +162,11 @@ $convert:line-loop:
e8/call read-line-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:check0:
$subx-dquotes:check0:
# if (line->write == 0) break
81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx
0f 84/jump-if-equal $convert:break/disp32
$convert:word-loop:
0f 84/jump-if-equal $subx-dquotes:break/disp32
$subx-dquotes:word-loop:
# next-word-or-string(line, word-slice)
# . . push args
52/push-edx
@ -175,7 +175,7 @@ $convert:word-loop:
e8/call next-word-or-string/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:check1:
$subx-dquotes:check1:
# if (slice-empty?(word-slice)) break
# . eax = slice-empty?(word-slice)
# . . push args
@ -186,8 +186,8 @@ $convert:check1:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != 0) break
3d/compare-eax-and 0/imm32
0f 85/jump-if-not-equal $convert:next-line/disp32
$convert:check-for-comment:
0f 85/jump-if-not-equal $subx-dquotes:next-line/disp32
$subx-dquotes:check-for-comment:
# if (slice-starts-with?(word-slice, "#")) continue
# . start/esi = word-slice->start
8b/copy 0/mod/indirect 2/rm32/edx . . . 6/r32/esi . . # copy *edx to esi
@ -196,12 +196,12 @@ $convert:check-for-comment:
8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL
# . if (eax == '#') continue
3d/compare-eax-and 0x23/imm32/hash
74/jump-if-equal $convert:word-loop/disp8
$convert:check-for-string-literal:
74/jump-if-equal $subx-dquotes:word-loop/disp8
$subx-dquotes:check-for-string-literal:
# if (slice-starts-with?(word-slice, '"')) continue
3d/compare-eax-and 0x22/imm32/dquote
75/jump-if-not-equal $convert:regular-word/disp8
$convert:string-literal:
75/jump-if-not-equal $subx-dquotes:regular-word/disp8
$subx-dquotes:string-literal:
# process-string-literal(word-slice, out, new-data-segment)
# . . push args
57/push-edi
@ -212,8 +212,8 @@ $convert:string-literal:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# continue
eb/jump $convert:next-word/disp8
$convert:regular-word:
eb/jump $subx-dquotes:next-word/disp8
$subx-dquotes:regular-word:
# write-slice-buffered(out, word-slice)
# . . push args
52/push-edx
@ -223,7 +223,7 @@ $convert:regular-word:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# fall through
$convert:next-word:
$subx-dquotes:next-word:
# write-buffered(out, " ")
# . . push args
68/push Space/imm32
@ -233,8 +233,8 @@ $convert:next-word:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# loop
eb/jump $convert:word-loop/disp8
$convert:next-line:
eb/jump $subx-dquotes:word-loop/disp8
$subx-dquotes:next-line:
# write-buffered(out, "\n")
# . . push args
68/push Newline/imm32
@ -244,8 +244,8 @@ $convert:next-line:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# loop
e9/jump $convert:line-loop/disp32
$convert:break:
e9/jump $subx-dquotes:line-loop/disp32
$subx-dquotes:break:
# write-stream-data(out, new-data-segment)
# . . push args
57/push-edi
@ -261,7 +261,7 @@ $convert:break:
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$convert:end:
$subx-dquotes:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp
# . restore registers
@ -383,7 +383,7 @@ $process-string-literal:end:
5d/pop-to-ebp
c3/return
test-convert-is-idempotent-by-default:
test-subx-dquotes-is-idempotent-by-default:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
@ -502,12 +502,12 @@ test-convert-is-idempotent-by-default:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-dquotes(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-dquotes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . flush(_test-output-buffered-file)
@ -556,7 +556,7 @@ test-convert-is-idempotent-by-default:
#? # }}}
# . check-next-stream-line-equal(_test-output-stream, "", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/0"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/0"/imm32
68/push ""/imm32
68/push _test-output-stream/imm32
# . . call
@ -565,7 +565,7 @@ test-convert-is-idempotent-by-default:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/1"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/1"/imm32
68/push ""/imm32
68/push _test-output-stream/imm32
# . . call
@ -574,7 +574,7 @@ test-convert-is-idempotent-by-default:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/2"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/2"/imm32
68/push "== code 0x1 "/imm32
68/push _test-output-stream/imm32
# . . call
@ -583,7 +583,7 @@ test-convert-is-idempotent-by-default:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/3"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/3"/imm32
68/push ""/imm32
68/push _test-output-stream/imm32
# . . call
@ -592,7 +592,7 @@ test-convert-is-idempotent-by-default:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "1 ", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/4"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/4"/imm32
68/push "1 "/imm32
68/push _test-output-stream/imm32
# . . call
@ -601,7 +601,7 @@ test-convert-is-idempotent-by-default:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/5"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/5"/imm32
68/push ""/imm32
68/push _test-output-stream/imm32
# . . call
@ -610,7 +610,7 @@ test-convert-is-idempotent-by-default:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/6"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/6"/imm32
68/push "2 3 "/imm32
68/push _test-output-stream/imm32
# . . call
@ -619,7 +619,7 @@ test-convert-is-idempotent-by-default:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/7"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/7"/imm32
68/push "== data 0x2 "/imm32
68/push _test-output-stream/imm32
# . . call
@ -628,7 +628,7 @@ test-convert-is-idempotent-by-default:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg)
# . . push args
68/push "F - test-convert-is-idempotent-by-default/8"/imm32
68/push "F - test-subx-dquotes-is-idempotent-by-default/8"/imm32
68/push "4 5/imm32 "/imm32
68/push _test-output-stream/imm32
# . . call
@ -640,7 +640,7 @@ test-convert-is-idempotent-by-default:
5d/pop-to-ebp
c3/return
test-convert-processes-string-literals:
test-subx-dquotes-processes-string-literals:
# . prolog
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
@ -703,12 +703,12 @@ test-convert-processes-string-literals:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-dquotes(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-dquotes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . flush(_test-output-buffered-file)
@ -769,7 +769,7 @@ test-convert-processes-string-literals:
#? # }}}
# . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg)
# . . push args
68/push "F - test-convert-processes-string-literals/0"/imm32
68/push "F - test-subx-dquotes-processes-string-literals/0"/imm32
68/push "== code 0x1 "/imm32
68/push _test-output-stream/imm32
# . . call
@ -778,7 +778,7 @@ test-convert-processes-string-literals:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg)
# . . push args
68/push "F - test-convert-processes-string-literals/1"/imm32
68/push "F - test-subx-dquotes-processes-string-literals/1"/imm32
68/push "1 _string1/x "/imm32
68/push _test-output-stream/imm32
# . . call
@ -787,7 +787,7 @@ test-convert-processes-string-literals:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg)
# . . push args
68/push "F - test-convert-processes-string-literals/2"/imm32
68/push "F - test-subx-dquotes-processes-string-literals/2"/imm32
68/push "2 _string2/y "/imm32
68/push _test-output-stream/imm32
# . . call
@ -796,7 +796,7 @@ test-convert-processes-string-literals:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "== data", msg)
# . . push args
68/push "F - test-convert-processes-string-literals/3"/imm32
68/push "F - test-subx-dquotes-processes-string-literals/3"/imm32
68/push "== data"/imm32
68/push _test-output-stream/imm32
# . . call
@ -805,7 +805,7 @@ test-convert-processes-string-literals:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg)
# . . push args
68/push "F - test-convert-processes-string-literals/4"/imm32
68/push "F - test-subx-dquotes-processes-string-literals/4"/imm32
68/push "_string1:"/imm32
68/push _test-output-stream/imm32
# . . call
@ -814,7 +814,7 @@ test-convert-processes-string-literals:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg)
# . . push args
68/push "F - test-convert-processes-string-literals/5"/imm32
68/push "F - test-subx-dquotes-processes-string-literals/5"/imm32
68/push "0x00000001/imm32 61/a "/imm32
68/push _test-output-stream/imm32
# . . call
@ -823,7 +823,7 @@ test-convert-processes-string-literals:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg)
# . . push args
68/push "F - test-convert-processes-string-literals/6"/imm32
68/push "F - test-subx-dquotes-processes-string-literals/6"/imm32
68/push "_string2:"/imm32
68/push _test-output-stream/imm32
# . . call
@ -832,7 +832,7 @@ test-convert-processes-string-literals:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg)
# . . push args
68/push "F - test-convert-processes-string-literals/7"/imm32
68/push "F - test-subx-dquotes-processes-string-literals/7"/imm32
68/push "0x00000002/imm32 62/b 63/c "/imm32
68/push _test-output-stream/imm32
# . . call

Binary file not shown.

Binary file not shown.

BIN
apps/hex

Binary file not shown.

View File

@ -3,7 +3,7 @@
# comments between '#' and newline.
#
# To run:
# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/hex.subx -o apps/hex
# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/hex.subx -o apps/hex
# $ echo '80 81 82 # comment' |./subx run apps/hex |xxd -
# Expected output:
# 00000000: 8081 82
@ -32,10 +32,10 @@ Entry: # run tests if necessary, convert stdin if not
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# - if argc > 1 and argv[1] == "test", then return run_tests()
# if (argc <= 1) goto run-main
# if (argc <= 1) goto interactive
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp
7e/jump-if-lesser-or-equal $hex:run-main/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto run-main
7e/jump-if-lesser-or-equal $subx-hex-main:interactive/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto interactive
# . eax = kernel-string-equal?(argv[1], "test")
# . . push args
68/push "test"/imm32
@ -44,15 +44,15 @@ Entry: # run tests if necessary, convert stdin if not
e8/call kernel-string-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == 0) goto run-main
# . if (eax == 0) goto interactive
3d/compare-eax-and 0/imm32
74/jump-if-equal $hex:run-main/disp8
74/jump-if-equal $subx-hex-main:interactive/disp8
# run-tests()
e8/call run-tests/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
eb/jump $hex:end/disp8
$hex:run-main:
eb/jump $subx-hex-main:end/disp8
$subx-hex-main:interactive:
# - otherwise convert stdin
# var ed/eax : exit-descriptor
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp
@ -60,24 +60,24 @@ $hex:run-main:
# configure ed to really exit()
# . ed->target = 0
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax
# convert-hex(Stdin, 1/stdout, 2/stderr, ed)
# subx-hex(Stdin, 1/stdout, 2/stderr, ed)
# . . push args
50/push-eax/ed
68/push Stderr/imm32
68/push Stdout/imm32
68/push Stdin/imm32
# . . call
e8/call convert-hex/disp32
e8/call subx-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$hex:end:
$subx-hex-main:end:
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
# the main entry point
convert-hex: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void>
subx-hex: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void>
# pseudocode:
# while true
# eax = convert-next-octet(in, err, ed)
@ -90,7 +90,7 @@ convert-hex: # in : (address buffered-file), out : (address buffered-file), err
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
$convert-hex:loop:
$subx-hex:loop:
# eax = convert-next-octet(in, err, ed)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20)
@ -102,7 +102,7 @@ $convert-hex:loop:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# if (eax == Eof) break
3d/compare-eax-and 0xffffffff/imm32/Eof
74/jump-if-equal $convert-hex:loop-end/disp8
74/jump-if-equal $subx-hex:loop-end/disp8
# write-byte-buffered(out, AL)
# . . push args
50/push-eax
@ -112,8 +112,8 @@ $convert-hex:loop:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# loop
eb/jump $convert-hex:loop/disp8
$convert-hex:loop-end:
eb/jump $subx-hex:loop/disp8
$subx-hex:loop-end:
# flush(out)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
@ -121,7 +121,7 @@ $convert-hex:loop-end:
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$convert-hex:end:
$subx-hex:end:
# . restore registers
58/pop-to-eax
# . epilog

View File

@ -1,7 +1,7 @@
# Toy lisp interpreter
#
# To run:
# $ ./ntranslate init.linux 0*.subx apps/subx-common.subx apps/mulisp.subx
# $ ./ntranslate init.linux 0*.subx apps/subx-params.subx apps/mulisp.subx
# $ ./a.elf
# 42
# => 42

BIN
apps/pack

Binary file not shown.

View File

@ -3,7 +3,7 @@
# uses are left untouched.
#
# To run:
# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/pack.subx -o apps/pack
# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/pack.subx -o apps/pack
# $ echo '05/add-to-eax 0x20/imm32' |./subx run apps/pack
# Expected output:
# 05 20 00 00 00 # 05/add-to-eax 0x20/imm32
@ -33,10 +33,10 @@ Entry: # run tests if necessary, convert stdin if not
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# - if argc > 1 and argv[1] == "test", then return run_tests()
# if (argc <= 1) goto run-main
# if (argc <= 1) goto interactive
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp
7e/jump-if-lesser-or-equal $run-main/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto run-main
7e/jump-if-lesser-or-equal $subx-pack-main:interactive/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto interactive
# . eax = kernel-string-equal?(argv[1], "test")
# . . push args
68/push "test"/imm32
@ -45,15 +45,15 @@ Entry: # run tests if necessary, convert stdin if not
e8/call kernel-string-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == 0) goto run-main
# . if (eax == 0) goto interactive
3d/compare-eax-and 0/imm32
74/jump-if-equal $run-main/disp8
74/jump-if-equal $subx-pack-main:interactive/disp8
# run-tests()
e8/call run-tests/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
eb/jump $main:end/disp8
$run-main:
eb/jump $subx-pack-main:end/disp8
$subx-pack-main:interactive:
# - otherwise convert stdin
# var ed/eax : exit-descriptor
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp
@ -61,19 +61,19 @@ $run-main:
# configure ed to really exit()
# . ed->target = 0
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax
# convert(Stdin, Stdout, Stderr, ed)
# subx-pack(Stdin, Stdout, Stderr, ed)
# . . push args
50/push-eax/ed
68/push Stderr/imm32
68/push Stdout/imm32
68/push Stdin/imm32
# . . call
e8/call convert/disp32
e8/call subx-pack/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$main:end:
$subx-pack-main:end:
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
@ -97,7 +97,7 @@ $main:end:
# next-token-from-slice(start, end, delim char) -> slice
# slice-equal?(slice, string)
convert: # in : (address buffered-file), out : (address buffered-file) -> <void>
subx-pack: # in : (address buffered-file), out : (address buffered-file) -> <void>
# pseudocode:
# var line = new-stream(512, 1)
# var in-code? = false
@ -140,7 +140,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> <void
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# var in-code?/ebx = false
31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx
$convert:loop:
$subx-pack:loop:
# clear-stream(line)
# . . push args
51/push-ecx
@ -156,10 +156,10 @@ $convert:loop:
e8/call read-line-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:check0:
$subx-pack:check0:
# if (line->write == 0) break
81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx
0f 84/jump-if-equal $convert:break/disp32
0f 84/jump-if-equal $subx-pack:break/disp32
#? # dump line {{{
#? # . write(2/stderr, "LL: ")
#? # . . push args
@ -194,7 +194,7 @@ $convert:check0:
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:check1:
$subx-pack:check1:
# if (slice-empty?(word-slice)) write-stream-data(out, line)
# . eax = slice-empty?(word-slice)
# . . push args
@ -205,8 +205,8 @@ $convert:check1:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != 0) write-stream-data(out, line)
3d/compare-eax-and 0/imm32
0f 85/jump-if-not-equal $convert:pass-through/disp32
$convert:check2:
0f 85/jump-if-not-equal $subx-pack:pass-through/disp32
$subx-pack:check2:
#? # dump word-slice {{{
#? # . write(2/stderr, "AA: ")
#? # . . push args
@ -260,7 +260,7 @@ $convert:check2:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == 0) goto check3
3d/compare-eax-and 0/imm32
0f 84/jump-if-equal $convert:check3/disp32
0f 84/jump-if-equal $subx-pack:check3/disp32
# word-slice = next-word(line)
# . . push args
52/push-edx
@ -322,8 +322,8 @@ $convert:check2:
# . . in-code? = eax
89/copy 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # copy eax to ebx
# write-stream-data(out, line)
eb/jump $convert:pass-through/disp8
$convert:check3:
eb/jump $subx-pack:pass-through/disp8
$subx-pack:check3:
# else rewind-stream(line)
# . rewind-stream(line)
# . . push args
@ -334,8 +334,8 @@ $convert:check3:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# if (in-code? != 0) convert-instruction(line, out)
81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx
74/jump-if-equal $convert:data/disp8
$convert:code:
74/jump-if-equal $subx-pack:data/disp8
$subx-pack:code:
# . convert-instruction(line, out)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
@ -345,8 +345,8 @@ $convert:code:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . loop
e9/jump $convert:loop/disp32
$convert:data:
e9/jump $subx-pack:loop/disp32
$subx-pack:data:
# else convert-data(line, out)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
@ -356,8 +356,8 @@ $convert:data:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . loop
e9/jump $convert:loop/disp32
$convert:pass-through:
e9/jump $subx-pack:loop/disp32
$subx-pack:pass-through:
# write-stream-data(out, line)
# . . push args
51/push-ecx
@ -367,8 +367,8 @@ $convert:pass-through:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . loop
e9/jump $convert:loop/disp32
$convert:break:
e9/jump $subx-pack:loop/disp32
$subx-pack:break:
# flush(out)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
@ -376,7 +376,7 @@ $convert:break:
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$convert:end:
$subx-pack:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp
# . restore registers
@ -389,7 +389,7 @@ $convert:end:
5d/pop-to-ebp
c3/return
test-convert-passes-empty-lines-through:
test-subx-pack-passes-empty-lines-through:
# if a line is empty, pass it along unchanged
# . prolog
55/push-ebp
@ -428,12 +428,12 @@ test-convert-passes-empty-lines-through:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# write nothing to input
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-pack(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-pack/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check that the line just passed through
@ -446,7 +446,7 @@ test-convert-passes-empty-lines-through:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . check-stream-equal(_test-output-stream, "", msg)
# . . push args
68/push "F - test-convert-passes-empty-lines-through"/imm32
68/push "F - test-subx-pack-passes-empty-lines-through"/imm32
68/push ""/imm32
68/push _test-output-stream/imm32
# . . call
@ -458,7 +458,7 @@ test-convert-passes-empty-lines-through:
5d/pop-to-ebp
c3/return
test-convert-passes-lines-with-just-whitespace-through:
test-subx-pack-passes-lines-with-just-whitespace-through:
# if a line is empty, pass it along unchanged
# . prolog
55/push-ebp
@ -505,12 +505,12 @@ test-convert-passes-lines-with-just-whitespace-through:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-pack(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-pack/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check that the line just passed through
@ -523,7 +523,7 @@ test-convert-passes-lines-with-just-whitespace-through:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, " ", msg)
# . . push args
68/push "F - test-convert-passes-with-just-whitespace-through"/imm32
68/push "F - test-subx-pack-passes-with-just-whitespace-through"/imm32
68/push " "/imm32
68/push _test-output-stream/imm32
# . . call
@ -535,7 +535,7 @@ test-convert-passes-lines-with-just-whitespace-through:
5d/pop-to-ebp
c3/return
test-convert-passes-segment-headers-through:
test-subx-pack-passes-segment-headers-through:
# if a line starts with '==', pass it along unchanged
# . prolog
55/push-ebp
@ -582,12 +582,12 @@ test-convert-passes-segment-headers-through:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-pack(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-pack/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check that the line just passed through
@ -600,7 +600,7 @@ test-convert-passes-segment-headers-through:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . check-stream-equal(_test-output-stream, "== abcd 0x1", msg)
# . . push args
68/push "F - test-convert-passes-segment-headers-through"/imm32
68/push "F - test-subx-pack-passes-segment-headers-through"/imm32
68/push "== abcd 0x1"/imm32
68/push _test-output-stream/imm32
# . . call
@ -612,7 +612,7 @@ test-convert-passes-segment-headers-through:
5d/pop-to-ebp
c3/return
test-convert-in-data-segment:
test-subx-pack-in-data-segment:
# correctly process lines in the data segment
# . prolog
55/push-ebp
@ -678,12 +678,12 @@ test-convert-in-data-segment:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-pack(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-pack/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check output
@ -722,7 +722,7 @@ test-convert-in-data-segment:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
# . . push args
68/push "F - test-convert-in-data-segment/0"/imm32
68/push "F - test-subx-pack-in-data-segment/0"/imm32
68/push "== code 0x1"/imm32
68/push _test-output-stream/imm32
# . . call
@ -731,7 +731,7 @@ test-convert-in-data-segment:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
# . . push args
68/push "F - test-convert-in-data-segment/1"/imm32
68/push "F - test-subx-pack-in-data-segment/1"/imm32
68/push "== data 0x2"/imm32
68/push _test-output-stream/imm32
# . . call
@ -740,7 +740,7 @@ test-convert-in-data-segment:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
# . . push args
68/push "F - test-convert-in-data-segment/2"/imm32
68/push "F - test-subx-pack-in-data-segment/2"/imm32
68/push "03 04 00 00 00 "/imm32
68/push _test-output-stream/imm32
# . . call
@ -752,7 +752,7 @@ test-convert-in-data-segment:
5d/pop-to-ebp
c3/return
test-convert-code-and-data-segments:
test-subx-pack-code-and-data-segments:
# correctly process lines in both code and data segments
# . prolog
55/push-ebp
@ -836,12 +836,12 @@ test-convert-code-and-data-segments:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-pack(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-pack/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check output
@ -885,7 +885,7 @@ test-convert-code-and-data-segments:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
# . . push args
68/push "F - test-convert-code-and-data-segments/0"/imm32
68/push "F - test-subx-pack-code-and-data-segments/0"/imm32
68/push "== code 0x1"/imm32
68/push _test-output-stream/imm32
# . . call
@ -894,7 +894,7 @@ test-convert-code-and-data-segments:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00 # e8/call 20/disp32", msg)
# . . push args
68/push "F - test-convert-code-and-data-segments/1"/imm32
68/push "F - test-subx-pack-code-and-data-segments/1"/imm32
68/push "e8 20 00 00 00 # e8/call 20/disp32"/imm32
68/push _test-output-stream/imm32
# . . call
@ -903,7 +903,7 @@ test-convert-code-and-data-segments:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "68 20 # 68/push 0x20/imm8", msg)
# . . push args
68/push "F - test-convert-code-and-data-segments/2"/imm32
68/push "F - test-subx-pack-code-and-data-segments/2"/imm32
68/push "68 20 # 68/push 0x20/imm8"/imm32
68/push _test-output-stream/imm32
# . . call
@ -912,7 +912,7 @@ test-convert-code-and-data-segments:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
# . . push args
68/push "F - test-convert-code-and-data-segments/3"/imm32
68/push "F - test-subx-pack-code-and-data-segments/3"/imm32
68/push "== data 0x2"/imm32
68/push _test-output-stream/imm32
# . . call
@ -921,7 +921,7 @@ test-convert-code-and-data-segments:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
# . . push args
68/push "F - test-convert-code-and-data-segments/4"/imm32
68/push "F - test-subx-pack-code-and-data-segments/4"/imm32
68/push "03 04 00 00 00 "/imm32
68/push _test-output-stream/imm32
# . . call

Binary file not shown.

View File

@ -2,7 +2,7 @@
# other related arguments.
#
# To run:
# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/sigils.subx -o apps/sigils
# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/sigils.subx -o apps/sigils
#
# We currently support the following notations:
#

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
# Normally we introduce names in the layers that need them, but we'll make an
# exception to colocate various knobs for translating SubX programs using SubX.
# Various knobs for translating SubX programs using SubX.
== data

Binary file not shown.

View File

@ -5,7 +5,7 @@
# b) add segment headers with addresses and offsets correctly filled in
#
# To build:
# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/survey.subx -o apps/survey
# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/survey.subx -o apps/survey
#
# The expected input is a stream of bytes with segment headers, comments and
# some interspersed labels.
@ -62,10 +62,10 @@ Entry: # run tests if necessary, convert stdin if not
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# - if argc > 1 and argv[1] == "test", then return run_tests()
# if (argc <= 1) goto run-main
# if (argc <= 1) goto interactive
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp
7e/jump-if-lesser-or-equal $run-main/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto run-main
7e/jump-if-lesser-or-equal $subx-survey-main:interactive/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto interactive
# . eax = kernel-string-equal?(argv[1], "test")
# . . push args
68/push "test"/imm32
@ -74,22 +74,22 @@ Entry: # run tests if necessary, convert stdin if not
e8/call kernel-string-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == 0) goto run-main
# . if (eax == 0) goto interactive
3d/compare-eax-and 0/imm32
74/jump-if-equal $run-main/disp8
74/jump-if-equal $subx-survey-main:interactive/disp8
# run-tests()
e8/call run-tests/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
eb/jump $main:end/disp8
$run-main:
eb/jump $subx-survey-main:end/disp8
$subx-survey-main:interactive:
# - otherwise convert stdin
# convert(Stdin, Stdout)
# subx-survey(Stdin, Stdout)
# . . push args
68/push Stdout/imm32
68/push Stdin/imm32
# . . call
e8/call convert/disp32
e8/call subx-survey/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, Trace-stream)
@ -102,7 +102,7 @@ $run-main:
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$main:end:
$subx-survey-main:end:
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
@ -113,7 +113,7 @@ $main:end:
# labels: (address stream {string, label-info}) (16 bytes per row)
# these are all inefficient; use sequential scans for lookups
convert: # infile : (address buffered-file), out : (address buffered-file) -> <void>
subx-survey: # infile : (address buffered-file), out : (address buffered-file) -> <void>
# pseudocode
# var in : (address stream byte) = stream(4096)
# slurp(infile, in)
@ -440,7 +440,7 @@ convert: # infile : (address buffered-file), out : (address buffered-file) -> <
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$convert:end:
$subx-survey:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x30a0/imm32 # add to esp
# . restore registers
@ -452,7 +452,7 @@ $convert:end:
5d/pop-to-ebp
c3/return
test-convert-computes-addresses:
test-subx-survey-computes-addresses:
# input:
# == code 0x1
# Entry:
@ -552,12 +552,12 @@ test-convert-computes-addresses:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# convert(_test-input-buffered-file, _test-output-buffered-file)
# subx-survey(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call convert/disp32
e8/call subx-survey/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check trace
@ -589,7 +589,7 @@ test-convert-computes-addresses:
#? # }}}
# . check-trace-contains("label 'x' is at address 0x00001079.", msg)
# . . push args
68/push "F - test-convert-computes-addresses/0"/imm32
68/push "F - test-subx-survey-computes-addresses/0"/imm32
68/push "label 'x' is at address 0x00001079."/imm32
# . . call
e8/call check-trace-contains/disp32
@ -597,7 +597,7 @@ test-convert-computes-addresses:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'code' starts at address 0x00000074.", msg)
# . . push args
68/push "F - test-convert-computes-addresses/1"/imm32
68/push "F - test-subx-survey-computes-addresses/1"/imm32
68/push "segment 'code' starts at address 0x00000074."/imm32
# . . call
e8/call check-trace-contains/disp32
@ -605,7 +605,7 @@ test-convert-computes-addresses:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'code' has size 0x00000005.", msg)
# . . push args
68/push "F - test-convert-computes-addresses/2"/imm32
68/push "F - test-subx-survey-computes-addresses/2"/imm32
68/push "segment 'code' has size 0x00000005."/imm32
# . . call
e8/call check-trace-contains/disp32
@ -613,7 +613,7 @@ test-convert-computes-addresses:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'data' starts at address 0x00001079.", msg)
# . . push args
68/push "F - test-convert-computes-addresses/3"/imm32
68/push "F - test-subx-survey-computes-addresses/3"/imm32
68/push "segment 'data' starts at address 0x00001079."/imm32
# . . call
e8/call check-trace-contains/disp32

Binary file not shown.

View File

@ -2,7 +2,7 @@
# all functions starting with 'test-'.
#
# To build:
# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/tests.subx -o apps/tests
# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/tests.subx -o apps/tests
== code
# instruction effective address register displacement immediate
@ -33,7 +33,7 @@ Entry: # run tests if necessary, convert stdin if not
# - if argc > 1 and argv[1] == "test", then return run_tests()
# if (argc <= 1) goto run-main
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp
7e/jump-if-lesser-or-equal $run-main/disp8
7e/jump-if-lesser-or-equal $subx-tests-main:interactive/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto run-main
# . eax = kernel-string-equal?(argv[1], "test")
# . . push args
@ -45,29 +45,29 @@ Entry: # run tests if necessary, convert stdin if not
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == 0) goto run-main
3d/compare-eax-and 0/imm32
74/jump-if-equal $run-main/disp8
74/jump-if-equal $subx-tests-main:interactive/disp8
# run-tests()
e8/call run-tests/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
eb/jump $main:end/disp8
$run-main:
eb/jump $subx-tests-main:end/disp8
$subx-tests-main:interactive:
# - otherwise convert stdin
# convert(Stdin, Stdout)
# subx-gen-run-tests(Stdin, Stdout)
# . . push args
68/push Stdout/imm32
68/push Stdin/imm32
# . . call
e8/call convert/disp32
e8/call subx-gen-run-tests/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$main:end:
$subx-tests-main:end:
b8/copy-to-eax 1/imm32/exit
cd/syscall 0x80/imm8
convert: # in : (address buffered-file), out : (address buffered-file) -> <void>
subx-gen-run-tests: # in : (address buffered-file), out : (address buffered-file) -> <void>
# pseudocode
# bool tests-found = false
# var line = new-stream(512, 1)
@ -141,7 +141,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> <void
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:loop:
$subx-gen-run-tests:loop:
# clear-stream(line)
# . . push args
51/push-ecx
@ -157,10 +157,10 @@ $convert:loop:
e8/call read-line-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:check0:
$subx-gen-run-tests:check0:
# if (line->write == 0) break
81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx
0f 84/jump-if-equal $convert:break/disp32
0f 84/jump-if-equal $subx-gen-run-tests:break/disp32
# next-word(line, word-slice)
# . . push args
52/push-edx
@ -169,7 +169,7 @@ $convert:check0:
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:check-for-label:
$subx-gen-run-tests:check-for-label:
# if (!is-label?(word-slice)) continue
# . eax = is-label?(word-slice)
# . . push args
@ -180,8 +180,8 @@ $convert:check-for-label:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax == 0) continue
3d/compare-eax-and 0/imm32
74/jump-if-equal $convert:continue/disp8
$convert:check-label-prefix:
74/jump-if-equal $subx-gen-run-tests:continue/disp8
$subx-gen-run-tests:check-label-prefix:
# strip trailing ':' from word-slice
ff 1/subop/decrement 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # decrement *(edx+4)
# if !slice-starts-with?(word-slice, "test-") continue
@ -194,8 +194,8 @@ $convert:check-label-prefix:
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == 0) break
3d/compare-eax-and 0/imm32
74/jump-if-equal $convert:continue/disp8
$convert:call-test-function:
74/jump-if-equal $subx-gen-run-tests:continue/disp8
$subx-gen-run-tests:call-test-function:
# tests-found? = true
bb/copy-to-ebx 1/imm32/true
# write(new-code-segment, " e8/call ")
@ -222,7 +222,7 @@ $convert:call-test-function:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:continue:
$subx-gen-run-tests:continue:
# rewind-stream(line)
# . . push args
51/push-ecx
@ -239,11 +239,11 @@ $convert:continue:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# loop
e9/jump $convert:loop/disp32
$convert:break:
e9/jump $subx-gen-run-tests:loop/disp32
$subx-gen-run-tests:break:
# if (!tests-found?) goto end
81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx
74/jump-if-equal $convert:end/disp8
74/jump-if-equal $subx-gen-run-tests:end/disp8
# write(new-code-segment, " c3/return\n")
# . . push args
68/push " c3/return\n"/imm32
@ -260,7 +260,7 @@ $convert:break:
e8/call write-stream-data/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$convert:end:
$subx-gen-run-tests:end:
# flush(out)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)

50
build
View File

@ -106,54 +106,4 @@ older_than subx_bin subx.cc *_list && {
$CXX $CFLAGS subx.cc -o subx_bin
}
# We ought to always rebuild all apps if any .subx layers are updated.
# But during development it's too slow to update _all_ apps when we're
# repeatedly running a single one.
if [ ! $ONLY_CPP ]
then
# Assumption: SubX programs don't need to be retranslated every time we
# rebuild the C++ bootstrap.
OS=${OS:-linux}
# simple example programs
for n in `seq 1 12`
do
older_than examples/ex$n init.$OS examples/ex$n.subx && {
./subx_bin translate init.$OS examples/ex$n.subx -o examples/ex$n
}
done
# simple apps that use the standard library
for app in factorial crenshaw2-1 crenshaw2-1b handle
do
older_than apps/$app init.$OS [0-9]*.subx apps/$app.subx && {
./subx_bin translate init.$OS [0-9]*.subx apps/$app.subx -o apps/$app
}
done
# self-hosting translator
older_than apps/hex init.$OS 0[0-6]*.subx 070---hex.subx && {
./subx_bin translate init.$OS 0[0-6]*.subx 070---hex.subx -o apps/hex
}
for phase in hex survey pack assort dquotes tests
do
older_than apps/$phase init.$OS [0-9]*.subx apps/subx-common.subx apps/$phase.subx && {
./subx_bin translate init.$OS [0-9]*.subx apps/subx-common.subx apps/$phase.subx -o apps/$phase
}
done
# higher-level syntax
for phase in sigils
do
older_than apps/$phase init.$OS [0-9]*.subx apps/subx-common.subx apps/$phase.subx && {
./subx_bin translate init.$OS [0-9]*.subx apps/subx-common.subx apps/$phase.subx -o apps/$phase
}
done
fi
exit 0

View File

@ -7,7 +7,7 @@
if [[ $2 == 'test-'* ]]
then
TEST_NAME=$2 envsubst '$TEST_NAME' < run_one_test.subx > /tmp/run_one_test.subx
FILES=$(ls [0-9]*.subx apps/subx-common.subx $1 |sort |uniq)
FILES=$(ls [0-9]*.subx apps/subx-params.subx $1 |sort |uniq)
echo $FILES > /tmp/last_run_files
elif [[ -e /tmp/last_run_files ]]
then

108
test_apps
View File

@ -225,82 +225,28 @@ test $NATIVE && {
# Phases of the self-hosted SubX translator.
echo hex
./subx translate init.$OS $(enumerate/enumerate --until 070---hex.subx |grep '\.subx$') -o apps/hex
test "$1" = 'record' || git diff --exit-code apps/hex
test $EMULATED && {
./subx run apps/hex test
echo
}
test $NATIVE && {
apps/hex test
echo
}
echo survey
./subx translate init.$OS 0*.subx apps/subx-common.subx apps/survey.subx -o apps/survey
test "$1" = 'record' || git diff --exit-code apps/survey
test $EMULATED && {
./subx run apps/survey test
echo
}
test $NATIVE && {
apps/survey test
echo
}
echo pack
./subx translate init.$OS 0*.subx apps/subx-common.subx apps/pack.subx -o apps/pack
test "$1" = 'record' || git diff --exit-code apps/pack
test $EMULATED && {
./subx run apps/pack test
echo
}
test $NATIVE && {
apps/pack test
echo
}
echo assort
./subx translate init.$OS 0*.subx apps/subx-common.subx apps/assort.subx -o apps/assort
test "$1" = 'record' || git diff --exit-code apps/assort
test $EMULATED && {
./subx run apps/assort test
echo
}
test $NATIVE && {
apps/assort test
echo
}
echo dquotes
./subx translate init.$OS 0*.subx apps/subx-common.subx apps/dquotes.subx -o apps/dquotes
test "$1" = 'record' || git diff --exit-code apps/dquotes
test $EMULATED && {
./subx run apps/dquotes test
echo
}
test $NATIVE && {
apps/dquotes test
echo
}
echo tests
./subx translate init.$OS 0*.subx apps/subx-common.subx apps/tests.subx -o apps/tests
test "$1" = 'record' || git diff --exit-code apps/tests
test $EMULATED && {
./subx run apps/tests test
echo
}
test $NATIVE && {
apps/tests test
echo
}
for phase in hex survey pack assort dquotes tests
do
echo $phase
./subx translate init.$OS 0*.subx apps/subx-params.subx apps/$phase.subx -o apps/$phase
test "$1" = 'record' || git diff --exit-code apps/hex
test $EMULATED && {
./subx run apps/$phase test
echo
}
test $NATIVE && {
apps/$phase test
echo
}
done
# Higher-level syntax.
# Certain phases of translation run native beyond this point. We're starting
# to go beyond functionality of the C++ bootstrap.
echo sigils
./subx translate init.$OS 0*.subx apps/subx-common.subx apps/sigils.subx -o apps/sigils
./subx translate init.$OS 0*.subx apps/subx-params.subx apps/sigils.subx -o apps/sigils
[ "$1" != record ] && git diff --exit-code apps/sigils
./subx run apps/sigils test
echo
@ -310,7 +256,7 @@ test `uname` = 'Linux' && {
}
echo calls
cat init.$OS 0*.subx apps/subx-common.subx apps/calls.subx | apps/sigils > a.sigils
cat init.$OS 0*.subx apps/subx-params.subx apps/calls.subx | apps/sigils > a.sigils
./subx translate a.sigils -o apps/calls
[ "$1" != record ] && git diff --exit-code apps/calls
./subx run apps/calls test
@ -321,7 +267,7 @@ test `uname` = 'Linux' && {
}
echo braces
cat init.$OS 0*.subx apps/subx-common.subx apps/braces.subx | apps/calls | apps/sigils > a.sigils
cat init.$OS 0*.subx apps/subx-params.subx apps/braces.subx | apps/calls | apps/sigils > a.sigils
./subx translate a.sigils -o apps/braces
[ "$1" != record ] && git diff --exit-code apps/braces
./subx run apps/braces test
@ -331,12 +277,8 @@ test `uname` = 'Linux' && {
echo
}
# Only native runs beyond this point. We start using syntax that the emulator
# doesn't support.
test $EMULATED && echo "skipping remaining runs in emulated mode"
test $NATIVE || exit 0
echo "== translating using SubX"
echo "== translating using SubX (native only)"
# example programs
@ -358,14 +300,10 @@ done
# Phases of the self-hosted SubX translator.
echo hex
./ntranslate init.$OS $(enumerate/enumerate --until 070---hex.subx |grep '\.subx$')
diff apps/hex a.elf
for app in survey pack assort dquotes tests sigils calls braces
for app in hex survey pack assort dquotes tests sigils calls braces
do
echo $app
./ntranslate init.$OS 0*.subx apps/subx-common.subx apps/$app.subx
./ntranslate init.$OS 0*.subx apps/subx-params.subx apps/$app.subx
diff apps/$app a.elf
done

View File

@ -22,11 +22,11 @@ for f in [0-9]*.subx
do
echo "=== $f"
./subx translate init.linux $(enumerate/enumerate --until $f |grep '\.subx$') -o a.elf
./subx run a.elf
./subx run a.elf test
echo
test `uname` = 'Linux' && {
chmod +x a.elf
./a.elf
./a.elf test
echo
} || true
done