18d5bab2b6
I've found two bugs in SubX libraries: 1. next-word had an out-of-bounds read 2. next-word was skipping comments, because that's what I need during bootstrapping. I've created a new variant called next-raw-word that doesn't skip comments. These really need better names. We're now at the point where 4b.mu has the right structure and returns identical result to 4a.mu.
363 lines
22 KiB
Plaintext
363 lines
22 KiB
Plaintext
# 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 (surrounded by whitespace,
|
|
# treating '#' comments as a single word)
|
|
# return empty string on reaching end of file
|
|
next-word: # line: (addr stream byte), out: (addr slice)
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# . save registers
|
|
50/push-eax
|
|
51/push-ecx
|
|
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-whitespace(line)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
|
# . . call
|
|
e8/call skip-chars-matching-whitespace/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/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-< $next-word:check-for-comment/disp8
|
|
# . return out
|
|
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-!= $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
|
|
8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax
|
|
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
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
test-next-word:
|
|
# . prologue
|
|
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: slice
|
|
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
|
|
# . epilogue
|
|
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:
|
|
# . prologue
|
|
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: slice
|
|
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
|
|
# . epilogue
|
|
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:
|
|
# . prologue
|
|
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: slice
|
|
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
|
|
# . epilogue
|
|
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-newline:
|
|
# . prologue
|
|
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: slice
|
|
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 some whitespace and a newline
|
|
# . . push args
|
|
68/push " \n"/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->end - slice->start, 0, msg)
|
|
# . . push args
|
|
68/push "F - test-next-word-returns-empty-string-on-newline"/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
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
# (re)compute the bounds of the next word in the line (separated by whitespace)
|
|
# return empty string on reaching end of file
|
|
next-raw-word: # line: (addr stream byte), out: (addr slice)
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# . save registers
|
|
50/push-eax
|
|
51/push-ecx
|
|
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-whitespace(line)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
|
# . . call
|
|
e8/call skip-chars-matching-whitespace/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
|
|
$next-raw-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-< $next-raw-word:word-exists/disp8
|
|
# . return out
|
|
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-raw-word:end/disp8
|
|
$next-raw-word:word-exists:
|
|
# 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
|
|
# 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-raw-word:end:
|
|
# . restore registers
|
|
5f/pop-to-edi
|
|
5e/pop-to-esi
|
|
59/pop-to-ecx
|
|
58/pop-to-eax
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|