# Experimental syntax sugar for addressing modes that expand into /rm32 and # other related arguments. # # To run: # $ ./subx translate 0*.subx apps/subx-common.subx apps/sigils.subx -o apps/sigils # # We currently support the following notations: # # 1. # $ echo '%eax' | ./subx run apps/sigils # 3/mod 0/rm32 # # 2. # $ echo '*eax' | ./subx run apps/sigils # 0/mod 0/rm32 # # 3. # $ echo '*(eax+4)' | ./subx run apps/sigils # 2/mod 0/rm32 4/disp32 # # 4. # $ echo '*(eax+ecx)' | ./subx run apps/sigils # 0/mod 4/rm32 0/base 1/index 0/scale # # 5. # $ echo '*(eax+ecx+4)' | ./subx run apps/sigils # 2/mod 4/rm32 0/base 1/index 0/scale 4/disp32 # # 6. # $ echo '*(eax+ecx<<2+4)' | ./subx run apps/sigils # 2/mod 4/rm32 0/base 1/index 2/scale 4/disp32 # # Addition isn't commutative here. Template must always be (base+index< 1 and argv[1] == "test", then return run_tests() # . argc > 1 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 # . argv[1] == "test" # . . push args 68/push "test"/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call kernel-string-equal?/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . check result 3d/compare-eax-and 1/imm32 75/jump-if-not-equal $run-main/disp8 # . run-tests() e8/call run-tests/disp32 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx eb/jump $main:end/disp8 $run-main: # - otherwise convert stdin # convert(Stdin, Stdout) # . . push args 68/push Stdout/imm32 68/push Stdin/imm32 # . . call e8/call convert/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: b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # error messages considered: # *x + 34 -> error: base+disp addressing must be within '()' convert: # in : (address buffered-file), out : (address buffered-file) -> # pseudocode: # var line = new-stream(512, 1) # while true # clear-stream(line) # read-line-buffered(in, line) # if (line->write == 0) break # end of file # while true # var word-slice = next-word-or-expression(line) # if slice-empty?(word-slice) # end of line # break # if slice-starts-with?(word-slice, "#") # comment # continue # if slice-starts-with?(word-slice, '%') # direct mode # emit-direct-mode(word-slice, out) # else if slice-starts-with?(word-slice, '*') # indirect mode # base, index, scale, disp = parse-effective-address(word-slice) # emit-indirect-mode(out, base, index, scale, disp) # else if slice-starts-with?(word-slice, '+') # abort("'+' only permitted within '*(...)'") # else # write-slice-buffered(out, word-slice) # write(out, " ") # write(out, "\n") # flush(out) # # . 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 # var line/ecx : (address stream byte) = stream(512) 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x200/imm32 # subtract from esp 68/push 0x200/imm32/length 68/push 0/imm32/read 68/push 0/imm32/write 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx # var word-slice/edx = {0, 0} 68/push 0/imm32/end 68/push 0/imm32/start 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx $convert:line-loop: # clear-stream(line) # . . push args 51/push-ecx # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # read-line-buffered(in, line) # . . push args 51/push-ecx ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call 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: # 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: # next-word-or-expression(line, word-slice) # . . push args 52/push-edx 51/push-ecx # . . call e8/call next-word-or-expression/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp $convert:check1: # if (slice-empty?(word-slice)) break # . eax = slice-empty?(word-slice) # . . push args 52/push-edx # . . 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) break 3d/compare-eax-and 0/imm32 0f 85/jump-if-not-equal $convert:next-line/disp32 $convert:check-for-comment: # if (slice-starts-with?(word-slice, "#")) continue # . start/ebx = word-slice->start 8b/copy 0/mod/indirect 2/rm32/edx . . . 3/r32/ebx . . # copy *edx to ebx # . c/eax = *start 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 8a/copy-byte 0/mod/indirect 3/rm32/ebx . . . 0/r32/AL . . # copy byte at *ebx to AL # . if (eax == '#') continue 3d/compare-eax-and 0x23/imm32/hash 74/jump-if-equal $convert:word-loop/disp8 $convert:check-for-direct-mode: # if (!slice-starts-with?(word-slice, "%")) goto next check 3d/compare-eax-and 0x25/imm32/percent 75/jump-if-not-equal $convert:check-for-indirect-mode/disp8 $convert:direct-mode: #? # dump word-slice {{{ #? # . write(2/stderr, "w: ") #? # . . push args #? 68/push "w: "/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 #? # . clear-stream(Stderr+4) #? # . . save eax #? 50/push-eax #? # . . push args #? b8/copy-to-eax Stderr/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 #? # . . restore eax #? 58/pop-to-eax #? # . write-slice-buffered(Stderr, word-slice) #? # . . push args #? 52/push-edx #? 68/push Stderr/imm32 #? # . . call #? e8/call write-slice-buffered/disp32 #? # . . discard args #? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp #? # . flush(Stderr) #? # . . push args #? 68/push Stderr/imm32 #? # . . call #? e8/call flush/disp32 #? # . . discard args #? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/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 #? # }}} # emit-direct-mode(word-slice, out) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 52/push-edx # . . call e8/call emit-direct-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # continue e9/jump $convert:next-word/disp32 $convert:check-for-indirect-mode: # if (!slice-starts-with?(word-slice, "*")) goto next check 3d/compare-eax-and 0x2a/imm32/asterisk 75/jump-if-not-equal $convert:check-for-invalid-addition/disp8 $convert:indirect-mode: # spill registers 50/push-eax 51/push-ecx 52/push-edx 53/push-ebx # base/eax, index/ecx, scale/edx, disp/ebx = parse-effective-address(word-slice) # . . push args 52/push-edx # . . call e8/call parse-effective-address/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # emit-indirect-mode(out, base, index, scale, disp) # . . push args 53/push-ebx 52/push-edx 51/push-ecx 50/push-eax ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call emit-indirect-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp # restore registers 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # continue e9/jump $convert:next-word/disp32 $convert:check-for-invalid-addition: # if (slice-starts-with?(word-slice, "+")) goto error1 3d/compare-eax-and 0x2b/imm32/plus 74/jump-if-equal $convert:error1/disp8 $convert:check-for-invalid-left-shift: # if (slice-starts-with?(word-slice, "<")) goto error1 3d/compare-eax-and 0x3c/imm32/less-than 74/jump-if-equal $convert:error1/disp8 $convert:regular-word: # write-slice-buffered(out, word-slice) # . . push args 52/push-edx ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call write-slice-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # fall through $convert:next-word: # write-buffered(out, " ") # . . push args 68/push " "/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # loop e9/jump $convert:word-loop/disp32 $convert:next-line: # write-buffered(out, "\n") # . . push args 68/push Newline/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call write-buffered/disp32 # . . 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: # flush(out) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp $convert:end: # . reclaim locals 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp # . restore registers 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx 58/pop-to-eax # . epilog 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return $convert:error1: # print(stderr, "error: '" eax "' only permitted within '*(...)' in '" line "'") # . write-buffered(Stderr, "error: '") # . . push args 68/push "error: '"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-byte-buffered(Stderr, eax) # . . push args 50/push-eax 68/push Stderr/imm32 # . . call e8/call write-byte-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(Stderr, "' only permitted within '*(...)' in '") # . . push args 68/push "' only permitted within '*(...)' in '"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-stream-data(Stderr, line) # . . push args 51/push-ecx 68/push Stderr/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 # . write-buffered(Stderr, "'") # . . push args 68/push "'"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here test-convert-passes-most-words-through: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . clear-stream(_test-input-buffered-file+4) # . . push args b8/copy-to-eax _test-input-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-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 # initialize input # . write(_test-input-stream, "== abcd 0x1") # . . push args 68/push "== abcd 0x1"/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 # convert(_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 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through # . 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 _test-output-stream {{{ #? # . 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 0x1 \n", msg) # . . push args 68/push "F - test-convert-passes-most-words-through"/imm32 68/push "== abcd 0x1 \n"/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-convert-direct-mode: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . clear-stream(_test-input-buffered-file+4) # . . push args b8/copy-to-eax _test-input-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-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 # initialize input # . write(_test-input-stream, "ab %ecx") # . . push args 68/push "ab %ecx"/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 # convert(_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 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through # . 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 _test-output-stream {{{ #? # . 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, "ab 3/mod/direct 0x00000001/rm32 \n", msg) # . . push args 68/push "F - test-convert-direct-mode"/imm32 68/push "ab 3/mod/direct 0x00000001/rm32 \n"/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-convert-register-indirect-mode: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . clear-stream(_test-input-buffered-file+4) # . . push args b8/copy-to-eax _test-input-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-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 # initialize input # . write(_test-input-stream, "ab *ecx") # . . push args 68/push "ab *ecx"/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 # convert(_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 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through # . 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 _test-output-stream {{{ #? # . 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, "ab 0/mod/indirect 0x00000001/rm32 \n", msg) # . . push args 68/push "F - test-convert-indirect-mode"/imm32 68/push "ab 0/mod/indirect 0x00000001/rm32 \n"/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-convert-register-indirect-mode-without-displacement: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . clear-stream(_test-input-buffered-file+4) # . . push args b8/copy-to-eax _test-input-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-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 # initialize input # . write(_test-input-stream, "ab *(ecx)") # . . push args 68/push "ab *(ecx)"/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 # convert(_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 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through # . 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 _test-output-stream {{{ #? # . 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, "ab 0/mod/indirect 1/rm32 \n", msg) # . . push args 68/push "F - test-convert-indirect-mode-without-displacement"/imm32 68/push "ab 0/mod/indirect 0x00000001/rm32 \n"/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-convert-register-indirect-mode-with-displacement: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . clear-stream(_test-input-buffered-file+4) # . . push args b8/copy-to-eax _test-input-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-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 # initialize input # . write(_test-input-stream, "ab *(ecx+4)") # . . push args 68/push "ab *(ecx+4)"/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 # convert(_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 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through # . 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 _test-output-stream {{{ #? # . 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, "ab 2/mod/*+disp32 1/rm32 4/disp32 \n", msg) # . . push args 68/push "F - test-convert-indirect-mode-with-displacement"/imm32 68/push "ab 2/mod/*+disp32 0x00000001/rm32 0x00000004/disp32 \n"/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 # boss level test-convert-register-indirect-mode-with-sib-byte: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . clear-stream(_test-input-buffered-file+4) # . . push args b8/copy-to-eax _test-input-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-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 # initialize input # . write(_test-input-stream, "ab *(ecx + edx<<3 + 4)") # . . push args 68/push "ab *(ecx + edx<<3 + 4)"/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 # convert(_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 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through # . 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 _test-output-stream {{{ #? # . 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, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale 4/disp32 \n", msg) # . . push args 68/push "F - test-convert-indirect-mode-with-sib-byte"/imm32 68/push "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0x00000004/disp32 \n"/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-convert-register-indirect-mode-with-sib-byte-negative-displacement: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . clear-stream(_test-input-buffered-file+4) # . . push args b8/copy-to-eax _test-input-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-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 # initialize input # . write(_test-input-stream, "ab *(ecx + edx<<3 - 4)") # . . push args 68/push "ab *(ecx + edx<<3 - 4)"/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 # convert(_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 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through # . 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 _test-output-stream {{{ #? # . 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, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale -4/disp32 \n", msg) # . . push args 68/push "F - test-convert-indirect-mode-with-sib-byte-negative-displacement"/imm32 68/push "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0xfffffffc/disp32 \n"/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 # beware: modifies 'word' emit-direct-mode: # word : (address slice), out : (address buffered-file) # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 50/push-eax # ++word->start # . eax = word 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax # . ++(*eax) ff 0/subop/increment 0/mod/indirect 0/rm32/eax . . . . . . # increment *eax # reg-num/eax = get-slice(Registers, word, row-size=8) # . . push args 68/push "Registers"/imm32 68/push 8/imm32/row-size ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 68/push Registers/imm32 # . . call e8/call get-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp # write-buffered(out, "3/mod/direct ") # . . push args 68/push "3/mod/direct "/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # print-int32-buffered(out, *eax) # . . push args ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # push *eax ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # write-buffered(out, "/rm32") # . . push args 68/push "/rm32"/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp $emit-direct-mode:end: # . restore registers 58/pop-to-eax # . epilog 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return test-emit-direct-mode: # . 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 slice/ecx = "%eax" b8/copy-to-eax "%eax"/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 # . 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-direct-mode(str, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 51/push-ecx # . . call e8/call emit-direct-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/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, "3/mod/direct 0/rm32", msg) # . . push args 68/push "F - test-emit-direct-mode/0"/imm32 68/push "3/mod/direct 0x00000000/rm32"/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-direct-mode-2: # . 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 slice/ecx = "%edi" b8/copy-to-eax "%edi"/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 # . 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-direct-mode(str/ecx, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 51/push-ecx # . . call e8/call emit-direct-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/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, "3/mod/direct 7/rm32", msg) # . . push args 68/push "F - test-emit-direct-mode/1"/imm32 68/push "3/mod/direct 0x00000007/rm32"/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 # (re)compute the bounds of the next word or parenthetical expression in the line # return empty string on reaching end of file # # error messages considered: # * ... -> error: no space after '*' # *(... -> error: *(...) expression must be all on a single line next-word-or-expression: # 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-or-expression: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-or-expression: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) e9/jump $next-word-or-expression:end/disp32 $next-word-or-expression: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] != '#') goto next check # . 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-or-expression:check-for-string-literal/disp8 $next-word-or-expression: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 # skip rest of line 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-or-expression:end/disp8 $next-word-or-expression:check-for-string-literal: # if (line->data[line->read] != '"') goto next check # . 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 0x22/imm32/dquote 75/jump-if-not-equal $next-word-or-expression:check-for-paren/disp8 $next-word-or-expression:string-literal: # skip-string(line) # . . push args 56/push-esi # . . call e8/call skip-string/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # skip rest of word eb/jump $next-word-or-expression:regular-word/disp8 $next-word-or-expression:check-for-paren: # if (line->data[line->read] != '*') goto next check # . 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 0x2a/imm32/asterisk 75/jump-if-not-equal $next-word-or-expression:regular-word/disp8 # if (line->data[line->read] == ' ') goto error1 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xd/disp8 . # copy byte at *(esi+ecx+12+1) to AL # . compare 3d/compare-eax-and 0x20/imm32/space 74/jump-if-equal $next-word-or-expression:error1/disp8 # if (line->data[line->read] != '(') goto regular word 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xd/disp8 . # copy byte at *(esi+ecx+12+1) to AL # . compare 3d/compare-eax-and 0x28/imm32/open-paren 75/jump-if-not-equal $next-word-or-expression:regular-word/disp8 $next-word-or-expression:paren: # skip-until-close-paren(line) # . . push args 56/push-esi # . . call e8/call skip-until-close-paren/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # if (line->data[line->read] != ')') goto error2 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 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 0x29/imm32/close-paren 75/jump-if-not-equal $next-word-or-expression:error2/disp8 # skip ')' ff 0/subop/increment 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # increment *(esi+4) # fall through $next-word-or-expression:regular-word: # 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-or-expression: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 $next-word-or-expression:error1: # print(stderr, "error: no space allowed after '*' in '" line "'") # . write-buffered(Stderr, "error: no space allowed after '*' in '") # . . push args 68/push "error: no space allowed after '*' in '"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-stream-data(Stderr, line) # . . push args 56/push-esi 68/push Stderr/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 # . write-buffered(Stderr, "'") # . . push args 68/push "'"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here $next-word-or-expression:error2: # print(stderr, "error: no space allowed after '*' in '" line "'") # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '") # . . push args 68/push "error: *(...) expression must be all on a single line in '"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-stream-data(Stderr, line) # . . push args 56/push-esi 68/push Stderr/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 # . write-buffered(Stderr, "'") # . . push args 68/push "'"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here test-next-word-or-expression: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # 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-input-stream, " ab") # . . push args 68/push " ab"/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 # next-word-or-expression(_test-input-stream, slice) # . . push args 51/push-ecx 68/push _test-input-stream/imm32 # . . call e8/call next-word-or-expression/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check-ints-equal(_test-input-stream->read, 4, msg) # . . push args 68/push "F - test-next-word-or-expression/updates-stream-read-correctly"/imm32 68/push 4/imm32 b8/copy-to-eax _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) # . . 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->start - _test-input-stream->data, 2, msg) # . check-ints-equal(slice->start - _test-input-stream, 14, msg) # . . push args 68/push "F - test-next-word-or-expression: start"/imm32 68/push 0xe/imm32 # . . push slice->start - _test-input-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-input-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-input-stream->data, 4, msg) # . check-ints-equal(slice->end - _test-input-stream, 16, msg) # . . push args 68/push "F - test-next-word-or-expression: end"/imm32 68/push 0x10/imm32 # . . push slice->end - _test-input-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-input-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-or-expression-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-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 # 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-input-stream, " # a") # . . push args 68/push " # a"/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 # next-word-or-expression(_test-input-stream, slice) # . . push args 51/push-ecx 68/push _test-input-stream/imm32 # . . call e8/call next-word-or-expression/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check-ints-equal(_test-input-stream->read, 5, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-whole-comment/updates-stream-read-correctly"/imm32 68/push 5/imm32 b8/copy-to-eax _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) # . . 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->start - _test-input-stream->data, 2, msg) # . check-ints-equal(slice->start - _test-input-stream, 14, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-whole-comment: start"/imm32 68/push 0xe/imm32 # . . push slice->start - _test-input-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-input-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-input-stream->data, 5, msg) # . check-ints-equal(slice->end - _test-input-stream, 17, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-whole-comment: end"/imm32 68/push 0x11/imm32 # . . push slice->end - _test-input-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-input-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-or-expression-returns-empty-slice-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-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 # 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-input-stream # next-word-or-expression(_test-input-stream, slice) # . . push args 51/push-ecx 68/push _test-input-stream/imm32 # . . call e8/call next-word-or-expression/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-or-expression-returns-empty-expression-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 test-next-word-or-expression-returns-string-literal: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # 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-input-stream, " \"a b\"/imm32 ") # . . push args 68/push " \"a b\"/imm32 "/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 # next-word-or-expression(_test-input-stream, slice) # . . push args 51/push-ecx 68/push _test-input-stream/imm32 # . . call e8/call next-word-or-expression/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) # . check-ints-equal(slice->start - _test-input-stream, 13, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-string-literal: start"/imm32 68/push 0xd/imm32 # . . push slice->start - _test-input-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-input-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-input-stream->data, 12, msg) # . check-ints-equal(slice->end - _test-input-stream, 24, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-string-literal: end"/imm32 68/push 0x18/imm32 # . . push slice->end - _test-input-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-input-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-or-expression-returns-string-with-escapes: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # 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-input-stream, " \"a\\\"b\"/x") # . . push args 68/push " \"a\\\"b\"/x"/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 # next-word-or-expression(_test-input-stream, slice) # . . push args 51/push-ecx 68/push _test-input-stream/imm32 # . . call e8/call next-word-or-expression/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) # . check-ints-equal(slice->start - _test-input-stream, 13, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-string-with-escapes: start"/imm32 68/push 0xd/imm32 # . . push slice->start - _test-input-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-input-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-input-stream->data, 9, msg) # . check-ints-equal(slice->end - _test-input-stream, 21, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-string-with-escapes: end"/imm32 68/push 0x15/imm32 # . . push slice->end - _test-input-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-input-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-or-expression-returns-whole-expression: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # 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-input-stream, " *(a b)/imm32 ") # . . push args 68/push " *(a b)/imm32 "/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 # next-word-or-expression(_test-input-stream, slice) # . . push args 51/push-ecx 68/push _test-input-stream/imm32 # . . call e8/call next-word-or-expression/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) # . check-ints-equal(slice->start - _test-input-stream, 13, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-whole-expression: start"/imm32 68/push 0xd/imm32 # . . push slice->start - _test-input-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-input-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-input-stream->data, 13, msg) # . check-ints-equal(slice->end - _test-input-stream, 25, msg) # . . push args 68/push "F - test-next-word-or-expression-returns-whole-expression: end"/imm32 68/push 0x19/imm32 # . . push slice->end - _test-input-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-input-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 # Grammar: # *reg -> 0/mod reg/rm32 # *(reg) -> 0/mod reg/rm32 # *(reg+disp) -> 2/mod reg/rm32 disp/disp32 # *(reg1+reg2< 2/mod 4/rm32 reg1/base reg2/index s/scale 0/disp32 # *(reg1+reg2< 2/mod 4/rm32 reg1/base reg2/index s/scale disp/disp32 # Intermediate structure: base, index, scale, disp # Default values: base: 0, index: 4 (none), scale: 0, disp: 0 # beware: modifies 'word' parse-effective-address: # word : (address slice) -> base/eax, index/ecx, scale/edx, disp/ebx # pseudocode: # ++word->start to skip '*' # initialize defaults: base=0, index=4, scale=0, disp=0 # if (*word->start != '(') { # base = get-slice(Registers, word, row-size=8) # return # } # # compound expressions # skip whitespace # read register into base # skip whitespace # if (*word->start == ')') goto end # if (*word->start == '-') goto displacement # if (*word->start != '+') goto error1 # ++word->start to skip '+' # skip whitespace # if next 3 characters don't make a register, goto displacement # read register into index # skip whitespace # if (*word->start == ')') goto end # if (*word->start == '<') { # ++word->start to skip '<' # if (*word->start != '<') goto error2 # ++word->start to skip '<' # skip whitespace # read integer into scale # skip whitespace # if (*word->start == ')') goto end # } # if (*word->start not in '+' '-') goto error3 # displacement: # read integer into disp # skip whitespace # if (*word->start != ')') goto error4 # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 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 # ++word->start to skip '*' ff 0/subop/increment 0/mod/indirect 6/rm32/esi . . . . . . # increment *esi # initialize defaults # base is in edi; we'll move it to eax just before we return bf/copy-to-edi 0/imm32 b9/copy-to-ecx 4/imm32/no-index ba/copy-to-edx 0/imm32/.scale bb/copy-to-ebx 0/imm32/disp $parse-effective-address:check-for-simple-register: # if (*word->start == '(') goto compound expression 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL 81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax 3d/compare-eax-and 0x28/imm32/open-paren 74/jump-if-equal $parse-effective-address:compound-expression/disp8 $parse-effective-address:simple-register: # base = get-slice(Registers, word, row-size=8) # . eax = get-slice(Registers, word, row-size=8) # . . push args 68/push "Registers"/imm32 68/push 8/imm32/row-size 56/push-esi 68/push Registers/imm32 # . . call e8/call get-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp # . base = *eax 8b/copy 0/mod/indirect 0/rm32/eax . . . 7/r32/edi . . # copy *eax to edi # return e9/jump $parse-effective-address:end/disp32 $parse-effective-address:compound-expression: # ++word->start to skip '(' ff 0/subop/increment 0/mod/indirect 6/rm32/esi . . . . . . # increment *esi # skip whitespace # . eax = skip-chars-matching-whitespace-in-slice(word->start, word->end) # . . push args 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 skip-chars-matching-whitespace-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . word->start = eax 89/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy eax to *esi # read register into base # . eax = next-register(word) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call next-register/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . edi = *eax 8b/copy 0/mod/indirect 0/rm32/eax . . . 7/r32/edi . . # copy *eax to edi # skip whitespace # . eax = skip-chars-matching-whitespace-in-slice(word->start, word->end) # . . push args 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 skip-chars-matching-whitespace-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . word->start = eax 89/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy eax to *esi # if (*word->start == ')') goto end 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL 81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax 3d/compare-eax-and 0x29/imm32/close-paren 0f 84/jump-if-equal $parse-effective-address:end/disp32 # if (*word->start == '-') goto displacement 3d/compare-eax-and 0x2d/imm32/minus 0f 84/jump-if-equal $parse-effective-address:displacement/disp32 # if (*word->start != '+') goto error1 3d/compare-eax-and 0x2b/imm32/plus 0f 85/jump-if-not-equal $parse-effective-address:error1/disp32 $parse-effective-address:check-for-index: # ++word->start to skip '+' ff 0/subop/increment 0/mod/indirect 6/rm32/esi . . . . . . # increment *esi # skip whitespace # . eax = skip-chars-matching-whitespace-in-slice(word->start, word->end) # . . push args 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 skip-chars-matching-whitespace-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . word->start = eax 89/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy eax to *esi $parse-effective-address:resolve-ambiguity: # if next 3 characters don't make a register, goto displacement # . spill ecx 51/push-ecx # . var tmp/ecx = {word->start, word->start+3} # . . ecx = word->start 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx # . . eax = word->start+3 05/add-to-eax 3/imm32 # . . push 50/push-eax 51/push-ecx # . . copy esp to ecx 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx # . eax = maybe-get-slice(Register, tmp, row-size=8) # . . push args 68/push 8/imm32/row-size 51/push-ecx 68/push Registers/imm32 # . . call e8/call maybe-get-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . reclaim tmp 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . restore ecx 59/pop-to-ecx # . if (eax == 0) goto displacement 3d/compare-eax-and 0/imm32 0f 84/jump-if-equal $parse-effective-address:displacement/disp32 $parse-effective-address:index: # read register into index # . eax = next-register(word) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call next-register/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . ecx = *eax 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx # skip whitespace # . eax = skip-chars-matching-whitespace-in-slice(word->start, word->end) # . . push args 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 skip-chars-matching-whitespace-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . word->start = eax 89/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy eax to *esi # if (*word->start == ')') goto end 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL 81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax 3d/compare-eax-and 0x29/imm32/close-paren 0f 84/jump-if-equal $parse-effective-address:end/disp32 $parse-effective-address:check-for-scale: # if (*word->start != '<') goto next check 3d/compare-eax-and 0x3c/imm32/less-than 75/jump-if-not-equal $parse-effective-address:check-for-displacement/disp8 # ++word->start to skip '<' ff 0/subop/increment 0/mod/indirect 6/rm32/esi . . . . . . # increment *esi # if (*word->start != '<') goto error2 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL 81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax 3d/compare-eax-and 0x3c/imm32/less-than 0f 85/jump-if-not-equal $parse-effective-address:error2/disp32 # ++word->start to skip '<' ff 0/subop/increment 0/mod/indirect 6/rm32/esi . . . . . . # increment *esi # skip whitespace # . eax = skip-chars-matching-whitespace-in-slice(word->start, word->end) # . . push args 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 skip-chars-matching-whitespace-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . word->start = eax 89/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy eax to *esi $parse-effective-address:scale: # read positive integer into scale # . eax = next-positive-hex-int(word) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call next-positive-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . edx = eax 89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx # skip whitespace # . eax = skip-chars-matching-whitespace-in-slice(word->start, word->end) # . . push args 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 skip-chars-matching-whitespace-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . word->start = eax 89/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy eax to *esi # if (*word->start == ')') goto end 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL 81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax 3d/compare-eax-and 0x29/imm32/close-paren 74/jump-if-equal $parse-effective-address:end/disp8 $parse-effective-address:check-for-displacement: # if (*word->start not in '+' '-') goto error3 3d/compare-eax-and 0x2b/imm32/plus 74/jump-if-equal $parse-effective-address:displacement/disp8 3d/compare-eax-and 0x2d/imm32/minus 74/jump-if-equal $parse-effective-address:displacement/disp8 e9/jump $parse-effective-address:error3/disp32 $parse-effective-address:displacement: # read integer into disp # . eax = next-hex-int(word) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call next-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . ebx = eax 89/copy 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # copy eax to ebx # skip whitespace # . eax = skip-chars-matching-whitespace-in-slice(word->start, word->end) # . . push args 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 skip-chars-matching-whitespace-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . word->start = eax 89/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy eax to *esi # if (*word->start != ')') goto error4 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL 81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax 3d/compare-eax-and 0x29/imm32/close-paren 0f 85/jump-if-not-equal $parse-effective-address:error4/disp32 $parse-effective-address:end: # return base in eax 89/copy 3/mod/direct 0/rm32/eax . . . 7/r32/edi . . # copy edi to eax # . restore registers 5f/pop-to-edi 5e/pop-to-esi # . epilog 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return $parse-effective-address:error1: # print(stderr, "error: unexpected character: " eax "\n") # . write-buffered(Stderr, "error: unexpected character: ") # . . push args 68/push "error: unexpected character: "/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . print-int32-buffered(out, eax) # . . push args 50/push-eax 68/push Stderr/imm32 # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(Stderr, "\n") # . . push args 68/push "\n"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here $parse-effective-address:error2: # print(stderr, "error: '<' can only be followed by '<' but got: " eax "\n") # . write-buffered(Stderr, "error: '<' can only be followed by '<' but got: ") # . . push args 68/push "error: '<' can only be followed by '<' but got: "/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . print-int32-buffered(out, eax) # . . push args 50/push-eax 68/push Stderr/imm32 # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(Stderr, "\n") # . . push args 68/push "\n"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here $parse-effective-address:error3: # print(stderr, "error: unexpected character before displacement: " eax "\n") # . write-buffered(Stderr, "error: unexpected character before displacement: ") # . . push args 68/push "error: unexpected character before displacement: "/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . print-int32-buffered(out, eax) # . . push args 50/push-eax 68/push Stderr/imm32 # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(Stderr, "\n") # . . push args 68/push "\n"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here $parse-effective-address:error4: # print(stderr, "error: unexpected character after displacement: " eax "; expected ')' to wrap up\n") # . write-buffered(Stderr, "error: unexpected character after displacement: ") # . . push args 68/push "error: unexpected character after displacement: "/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . print-int32-buffered(out, eax) # . . push args 50/push-eax 68/push Stderr/imm32 # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(Stderr, "; expected ')' to wrap up\n") # . . push args 68/push "; expected ')' to wrap up\n"/imm32 68/push Stderr/imm32 # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here # assumes 'in' starts with a register name, and returns pointer to its code # side-effect: modifies 'in' to scan past the initial register name next-register: # in : (address slice) -> reg/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 56/push-esi # esi = in 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi # var reg-slice/ecx : (address slice) = {in->start, in->start + 3} 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax 05/add-to-eax 3/imm32 50/push-eax ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx # in->start += 3 81 0/subop/add 0/mod/indirect 6/rm32/esi . . . . . 3/imm32 # add to *esi # eax = get-slice(Registers, word, row-size=8) # . . push args 68/push "next-register"/imm32 68/push 8/imm32/row-size 51/push-ecx 68/push Registers/imm32 # . . call e8/call get-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp $next-register:end: # reclaim 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 # . epilog 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return test-parse-effective-address-simple: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # var slice/ecx = "*esi" b8/copy-to-eax "*esi"/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 # . 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, ecx, edx, ebx = parse-effective-address(slice) # . . push args 51/push-ecx # . . call e8/call parse-effective-address/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # slice clobbered beyond this point # check-ints-equal(eax, 6, msg) # . . push args 68/push "F - test-parse-effective-address-simple/base"/imm32 68/push 6/imm32/esi 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(ecx, 4, msg) # . . push args 68/push "F - test-parse-effective-address-simple/index"/imm32 68/push 4/imm32/none 51/push-ecx # . . 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(edx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-simple/scale"/imm32 68/push 0/imm32/none 52/push-edx # . . 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(ebx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-simple/displacement"/imm32 68/push 0/imm32/none 53/push-ebx # . . 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-parse-effective-address-base: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # var slice/ecx = "*(esi )" b8/copy-to-eax "*(esi )"/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 # . 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, ecx, edx, ebx = parse-effective-address(slice) # . . push args 51/push-ecx # . . call e8/call parse-effective-address/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # slice clobbered beyond this point # check-ints-equal(eax, 6, msg) # . . push args 68/push "F - test-parse-effective-address-base/base"/imm32 68/push 6/imm32/esi 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(ecx, 4, msg) # . . push args 68/push "F - test-parse-effective-address-base/index"/imm32 68/push 4/imm32/none 51/push-ecx # . . 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(edx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-base/scale"/imm32 68/push 0/imm32/none 52/push-edx # . . 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(ebx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-base/displacement"/imm32 68/push 0/imm32/none 53/push-ebx # . . 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-parse-effective-address-base-displacement: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # var slice/ecx = "*(esi+3)" b8/copy-to-eax "*(esi+3)"/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 # . 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, ecx, edx, ebx = parse-effective-address(slice) # . . push args 51/push-ecx # . . call e8/call parse-effective-address/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # slice clobbered beyond this point # check-ints-equal(eax, 6, msg) # . . push args 68/push "F - test-parse-effective-address-base-displacement/base"/imm32 68/push 6/imm32/esi 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(ecx, 4, msg) # . . push args 68/push "F - test-parse-effective-address-base-displacement/index"/imm32 68/push 4/imm32/none 51/push-ecx # . . 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(edx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-base-displacement/scale"/imm32 68/push 0/imm32/none 52/push-edx # . . 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(ebx, 3, msg) # . . push args 68/push "F - test-parse-effective-address-base-displacement/displacement"/imm32 68/push 3/imm32 53/push-ebx # . . 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-parse-effective-address-base-negative-displacement: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # var slice/ecx = "*(esi-3)" b8/copy-to-eax "*(esi-3)"/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 # . 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, ecx, edx, ebx = parse-effective-address(slice) # . . push args 51/push-ecx # . . call e8/call parse-effective-address/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # slice clobbered beyond this point # check-ints-equal(eax, 6, msg) # . . push args 68/push "F - test-parse-effective-address-base-negative-displacement/base"/imm32 68/push 6/imm32/esi 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(ecx, 4, msg) # . . push args 68/push "F - test-parse-effective-address-base-negative-displacement/index"/imm32 68/push 4/imm32/none 51/push-ecx # . . 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(edx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-base-negative-displacement/scale"/imm32 68/push 0/imm32/none 52/push-edx # . . 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(ebx, -3, msg) # . . push args 68/push "F - test-parse-effective-address-base-negative-displacement/displacement"/imm32 68/push -3/imm32 53/push-ebx # . . 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-parse-effective-address-base-index: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # var slice/ecx = "*(esi+ecx)" b8/copy-to-eax "*(esi+ecx)"/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 # . 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, ecx, edx, ebx = parse-effective-address(slice) # . . push args 51/push-ecx # . . call e8/call parse-effective-address/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # slice clobbered beyond this point # check-ints-equal(eax, 6, msg) # . . push args 68/push "F - test-parse-effective-address-base-index/base"/imm32 68/push 6/imm32/esi 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(ecx, 1, msg) # . . push args 68/push "F - test-parse-effective-address-base-index/index"/imm32 68/push 1/imm32/none 51/push-ecx # . . 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(edx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-base-index/scale"/imm32 68/push 0/imm32/none 52/push-edx # . . 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(ebx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-base-index/displacement"/imm32 68/push 0/imm32 53/push-ebx # . . 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-parse-effective-address-base-index-scale: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # var slice/ecx = "*(esi+ecx<<2)" b8/copy-to-eax "*(esi+ecx<<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 # . 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, ecx, edx, ebx = parse-effective-address(slice) # . . push args 51/push-ecx # . . call e8/call parse-effective-address/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # slice clobbered beyond this point # check-ints-equal(eax, 6, msg) # . . push args 68/push "F - test-parse-effective-address-base-index-scale/base"/imm32 68/push 6/imm32/esi 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(ecx, 1, msg) # . . push args 68/push "F - test-parse-effective-address-base-index-scale/index"/imm32 68/push 1/imm32/none 51/push-ecx # . . 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(edx, 2, msg) # . . push args 68/push "F - test-parse-effective-address-base-index-scale/scale"/imm32 68/push 2/imm32 52/push-edx # . . 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(ebx, 0, msg) # . . push args 68/push "F - test-parse-effective-address-base-index-scale/displacement"/imm32 68/push 0/imm32 53/push-ebx # . . 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-parse-effective-address-base-index-scale-displacement: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # var slice/ecx = "*(esi + ecx<<2 - 0x34)" b8/copy-to-eax "*(esi + ecx<<2 - 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 # . 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, ecx, edx, ebx = parse-effective-address(slice) # . . push args 51/push-ecx # . . call e8/call parse-effective-address/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # slice clobbered beyond this point # check-ints-equal(eax, 6, msg) # . . push args 68/push "F - test-parse-effective-address-base-index-scale/base"/imm32 68/push 6/imm32/esi 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(ecx, 1, msg) # . . push args 68/push "F - test-parse-effective-address-base-index-scale/index"/imm32 68/push 1/imm32/none 51/push-ecx # . . 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(edx, 2, msg) # . . push args 68/push "F - test-parse-effective-address-base-index-scale/scale"/imm32 68/push 2/imm32 52/push-edx # . . 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(ebx, -0x34, msg) # . . push args 68/push "F - test-parse-effective-address-base-index-scale/displacement"/imm32 68/push -0x34/imm32 53/push-ebx # . . 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 # Code generation: # if index is none and disp is 0, then mod = 0 and rm32 = base # if index is none, then mod = 2 and rm32 = base and disp32 = disp # if index is not none, then mod = 2 and rm32 = 4 and base = base and index = index and disp32 = disp emit-indirect-mode: # out : (address buffered-file), base : int, index : int, scale : int, disp : int # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp $emit-indirect-mode:check-for-sib: # if (index == 4/none) goto next check 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 4/imm32 # compare *(ebp+16) 0f 84/jump-if-equal $emit-indirect-mode:check-for-disp/disp32 $emit-indirect-mode:emit-sib: # emit(out, "2/mod/indirect 4/rm32/sib " base "/base " index "/index " scale "/scale " disp "/disp32") # . write-buffered(out, "2/mod/*+disp32 4/rm32/sib ") # . . push args 68/push "2/mod/*+disp32 4/rm32/sib "/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 # . print-int32-buffered(out, base) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(out, "/base ") # . . push args 68/push "/base "/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 # . print-int32-buffered(out, index) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(out, "/index ") # . . push args 68/push "/index "/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 # . print-int32-buffered(out, scale) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(out, "/scale ") # . . push args 68/push "/scale "/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 # . print-int32-buffered(out, disp) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(out, "/disp32") # . . push args 68/push "/disp32"/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 e9/jump $emit-indirect-mode:end/disp32 $emit-indirect-mode:check-for-disp: # if (disp == 0) goto next check 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 0/imm32 # compare *(ebp+24) 74/jump-if-equal $emit-indirect-mode:emit-indirect/disp8 $emit-indirect-mode:emit-disp: # emit(out, "2/mod/*+disp32 " base "/rm32 " disp "/disp32") # . write-buffered(out, "2/mod/*+disp32 ") # . . push args 68/push "2/mod/*+disp32 "/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 # . print-int32-buffered(out, base) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(out, "/rm32 ") # . . push args 68/push "/rm32 "/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 # . print-int32-buffered(out, disp) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(out, "/disp32") # . . push args 68/push "/disp32"/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 eb/jump $emit-indirect-mode:end/disp8 $emit-indirect-mode:emit-indirect: # emit(out, "0/mod/indirect " base "/rm32") # . write-buffered(out, "0/mod/indirect ") # . . push args 68/push "0/mod/indirect "/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 # . print-int32-buffered(out, base) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . write-buffered(out, "/rm32") # . . push args 68/push "/rm32"/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 $emit-indirect-mode:end: # . epilog 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return test-emit-indirect-mode: # . 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 # emit-indirect-mode(_test-output-buffered-file, 0, 4/none, 0, 0) # . . write args 68/push 0/imm32/.disp 68/push 0/imm32/.scale 68/push 4/imm32/.index/none 68/push 0/imm32/.base 68/push _test-output-buffered-file/imm32 # . . call e8/call emit-indirect-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/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, "0/mod/indirect 0/rm32", msg) # . . push args 68/push "F - test-emit-indirect-mode"/imm32 68/push "0/mod/indirect 0x00000000/rm32"/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-indirect-mode-2: # . 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 # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 0) # . . write args 68/push 0/imm32/.disp 68/push 0/imm32/.scale 68/push 4/imm32/.index/none 68/push 7/imm32/.base 68/push _test-output-buffered-file/imm32 # . . call e8/call emit-indirect-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/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, "0/mod/indirect 7/rm32", msg) # . . push args 68/push "F - test-emit-indirect-mode-2"/imm32 68/push "0/mod/indirect 0x00000007/rm32"/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-indirect-mode-with-disp: # . 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 # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 4) # . . write args 68/push 4/imm32/.disp 68/push 0/imm32/.scale 68/push 4/imm32/.index/none 68/push 6/imm32/.base 68/push _test-output-buffered-file/imm32 # . . call e8/call emit-indirect-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/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, "2/mod/*+disp32 6/rm32 4/disp32", msg) # . . push args 68/push "F - test-emit-indirect-mode-with-disp"/imm32 68/push "2/mod/*+disp32 0x00000006/rm32 0x00000004/disp32"/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-indirect-mode-with-disp-negative: # . 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 # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, -4) # . . write args 68/push -4/imm32/.disp 68/push 0/imm32/.scale 68/push 4/imm32/.index/none 68/push 6/imm32/.base 68/push _test-output-buffered-file/imm32 # . . call e8/call emit-indirect-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/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, "2/mod/*+disp32 6/rm32 -4/disp32", msg) # . . push args 68/push "F - test-emit-indirect-mode-with-disp"/imm32 68/push "2/mod/*+disp32 0x00000006/rm32 0xfffffffc/disp32"/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-indirect-mode-with-sib: # . 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 # emit-indirect-mode(_test-output-buffered-file, 6/base, 1/index, 2/scale, 4/disp) # . . write args 68/push 4/imm32/.disp 68/push 2/imm32/.scale 68/push 1/imm32/.index 68/push 6/imm32/.base 68/push _test-output-buffered-file/imm32 # . . call e8/call emit-indirect-mode/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/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, "2/mod/indirect 4/rm32/sib 6/base 1/index 2/scale 4/disp", msg) # . . push args 68/push "F - test-emit-indirect-mode-with-sib"/imm32 68/push "2/mod/*+disp32 4/rm32/sib 0x00000006/base 0x00000001/index 0x00000002/scale 0x00000004/disp32"/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 # update line->read to ')' # line->read ends at ')' skip-until-close-paren: # line : (address stream) # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 50/push-eax 51/push-ecx 52/push-edx # ecx = line 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx # eax = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write]) # . . push &line->data[line->write] 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 8/disp8 . # copy *(ecx+8) to edx 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx 52/push-edx # . . push &line->data[line->read] 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx 52/push-edx # . . call e8/call skip-until-close-paren-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # line->read = eax - line->data 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax 2d/subtract-from-eax 0xc/imm32 89/copy 1/mod/*+disp8 1/rm32/ecx . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4) $skip-until-close-paren:end: # . restore registers 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-skip-until-close-paren: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . write(_test-input-stream, "*(abc) def") # . indices: 0123 45 # . . push args 68/push "*(abc) def"/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 # precondition: line->read == 0 # . . push args 68/push "F - test-skip-until-close-paren/precondition"/imm32 68/push 0/imm32 b8/copy-to-eax _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) # . . call e8/call check-ints-equal/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # skip-until-close-paren(_test-input-stream) # . . push args 68/push _test-input-stream/imm32 # . . call e8/call skip-until-close-paren/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(line->read, 5, msg) # . . push args 68/push "F - test-skip-until-close-paren"/imm32 68/push 5/imm32 b8/copy-to-eax _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) # . . 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-skip-until-close-paren-ignores-spaces: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . write(_test-input-stream, "*(a b)/yz") # . . push args 68/push "*(a b)/yz"/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 # precondition: line->read == 0 # . . push args 68/push "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32 68/push 0/imm32 b8/copy-to-eax _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) # . . call e8/call check-ints-equal/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # skip-until-close-paren(_test-input-stream) # . . push args 68/push _test-input-stream/imm32 # . . call e8/call skip-until-close-paren/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(line->read, 5, msg) # . . push args 68/push "F - test-skip-until-close-paren-ignores-spaces"/imm32 68/push 5/imm32 b8/copy-to-eax _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) # . . 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-skip-until-close-paren-works-from-mid-stream: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup # . 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 # . write(_test-input-stream, "0 *(a b)/yz") # . . push args 68/push "0 *(a b)/yz"/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 # precondition: _test-input-stream->read == 2 c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 2/imm32 # copy to *(eax+4) # skip-until-close-paren(_test-input-stream) # . . push args 68/push _test-input-stream/imm32 # . . call e8/call skip-until-close-paren/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(_test-input-stream->read, 7, msg) # . . push args 68/push "F - test-skip-until-close-paren-works-from-mid-stream"/imm32 68/push 7/imm32 b8/copy-to-eax _test-input-stream/imm32 ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 . # push *(eax+4) # . . 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 skip-until-close-paren-in-slice: # curr : (address byte), end : (address byte) -> new_curr/eax # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 51/push-ecx 52/push-edx # ecx = curr 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx # edx = end 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx # eax = 0 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax # skip initial dquote 41/increment-ecx $skip-until-close-paren-in-slice:loop: # if (curr >= end) break 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx 73/jump-if-greater-unsigned-or-equal $skip-until-close-paren-in-slice:break/disp8 # AL = *curr 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL $skip-until-close-paren-in-slice:check-close: # if (eax == ')') break 3d/compare-eax-and 0x29/imm32/close-paren 74/jump-if-equal $skip-until-close-paren-in-slice:break/disp8 # ++curr 41/increment-ecx eb/jump $skip-until-close-paren-in-slice:loop/disp8 $skip-until-close-paren-in-slice:break: # return curr 89/copy 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to eax $skip-until-close-paren-in-slice:end: # . restore registers 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-skip-until-close-paren-in-slice: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup: (eax..ecx) = "*(abc) def" b8/copy-to-eax "*(abc) def"/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 # eax = skip-until-close-paren-in-slice(eax, ecx) # . . push args 51/push-ecx 50/push-eax # . . call e8/call skip-until-close-paren-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check-ints-equal(ecx-eax, 5, msg) # eax is at the ')' # . . push args 68/push "F - test-skip-until-close-paren-in-slice"/imm32 68/push 5/imm32 # . . push ecx-eax 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx 51/push-ecx # . . 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-skip-until-close-paren-in-slice-ignores-spaces: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup: (eax..ecx) = "*(a b)/yz" b8/copy-to-eax "*(a b)/yz"/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 # eax = skip-until-close-paren-in-slice(eax, ecx) # . . push args 51/push-ecx 50/push-eax # . . call e8/call skip-until-close-paren-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check-ints-equal(ecx-eax, 4, msg) # eax is at the ')' # . . push args 68/push "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32 68/push 4/imm32 # . . push ecx-eax 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx 51/push-ecx # . . 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-skip-until-close-paren-in-slice-stops-at-end: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # setup: (eax..ecx) = "*(abc" # unbalanced dquote b8/copy-to-eax "*(abc"/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 # eax = skip-until-close-paren-in-slice(eax, ecx) # . . push args 51/push-ecx 50/push-eax # . . call e8/call skip-until-close-paren-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check-ints-equal(ecx-eax, 0, msg) # skipped to end of slice # . . push args 68/push "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32 68/push 0/imm32 # . . push ecx-eax 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx 51/push-ecx # . . 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 # assumes 'in' starts with optional '+' or '-', optional whitespace, and an unsigned integer # returns the value of the integer # side-effect: modifies 'in' to skip past the integer next-hex-int: # in : (address slice) -> result/eax # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 51/push-ecx 52/push-edx 53/push-ebx 56/push-esi 57/push-edi # result/edi = 0 31/xor 3/mod/direct 7/rm32/edi . . . 7/r32/edi . . # clear edi # esi = in 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi # edx = in->end 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 4/disp8 . # copy *(esi+4) to edx # curr/ecx = in->start 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx # negate?/ebx = false 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx # eax = *curr 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 $next-hex-int:positive: # if (*curr == '+') ++curr 3d/compare-eax-and 0x2b/imm32/+ 75/jump-if-not-equal $next-hex-int:negative/disp8 # . ++curr 41/increment-ecx eb/jump $next-hex-int:skip-whitespace/disp8 $next-hex-int:negative: # else if (*curr == '-') ++curr, negate = true 3d/compare-eax-and 0x2d/imm32/- 75/jump-if-not-equal $next-hex-int:skip-whitespace/disp8 $next-hex-int:need-to-negate: # . ++curr 41/increment-ecx # . negate = true bb/copy-to-ebx 1/imm32/true # fall through $next-hex-int:skip-whitespace: # spill eax 50/push-eax # eax = skip-chars-matching-whitespace-in-slice(word->start, word->end) # . . push args 52/push-edx 51/push-ecx # . . call e8/call skip-chars-matching-whitespace-in-slice/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # ecx = eax 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx # restore eax 58/pop-to-eax $next-hex-int:initial-0: # skip past leading '0x' # . if (*curr != '0') jump to loop 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL 3d/compare-eax-and 0x30/imm32/0 75/jump-if-not-equal $next-hex-int:loop/disp8 # . ++curr 41/increment-ecx $next-hex-int:initial-0x: # . if (curr >= in->end) return result 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx 73/jump-if-greater-or-equal-unsigned $next-hex-int:end/disp8 # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again 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 3d/compare-eax-and 0x78/imm32/x 75/jump-if-not-equal $next-hex-int:loop/disp8 # . ++curr 41/increment-ecx $next-hex-int:loop: # if (curr >= in->end) break 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx 73/jump-if-greater-or-equal-unsigned $next-hex-int:break/disp8 # if (!is-hex-digit?(*curr)) break # . eax = *curr 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL # . eax = is-hex-digit?(*curr) # . . push args 50/push-eax # . . call e8/call is-hex-digit?/disp32 # . . discard args 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 74/jump-if-equal $next-hex-int:break/disp8 # eax = from-hex-char(*curr) # . . copy arg to eax 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL # . . call e8/call from-hex-char/disp32 # result = result * 16 + eax c1/shift 4/subop/left 3/mod/direct 7/rm32/edi . . . . . 4/imm8 # shift edi left by 4 bits 01/add 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # add eax to edi # ++curr 41/increment-ecx # loop eb/jump $next-hex-int:loop/disp8 $next-hex-int:break: 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx 74/jump-if-equal $next-hex-int:end/disp8 $next-hex-int:negate: f7 3/subop/negate 3/mod/direct 7/rm32/edi . . . . . . # negate edi $next-hex-int:end: # word->start = curr 89/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy ecx to *esi # return edi 89/copy 3/mod/direct 0/rm32/eax . . . 7/r32/edi . . # copy edi to eax # . restore registers 5f/pop-to-edi 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx # . epilog 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return $next-hex-int:abort: # . _write(2/stderr, error) # . . push args 68/push "next-hex-int: invalid hex char: "/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 # . clear-stream(Stderr+4) # . . save eax 50/push-eax # . . push args b8/copy-to-eax Stderr/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 # . . restore eax 58/pop-to-eax # . print-int32-buffered(Stderr, eax) # . . push args 50/push-eax 68/push Stderr/imm32 # . . call e8/call print-int32-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/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 # . syscall(exit, 1) bb/copy-to-ebx 1/imm32 b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 # never gets here test-next-hex-int-single-digit: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # (eax..ecx) = "+a)" b8/copy-to-eax "+a)"/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 = next-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, 0xa, msg) # . . push args 68/push "F - test-next-hex-int-single-digit"/imm32 68/push 0xa/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 test-next-hex-int-multi-digit: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # (eax..ecx) = "+ 34a)" b8/copy-to-eax "+ 34a)"/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 = next-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, 0x34a, msg) # . . push args 68/push "F - test-next-hex-int-multi-digit"/imm32 68/push 0x34a/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 test-next-hex-int-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 = next-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, 0x34, msg) # . . push args 68/push "F - test-next-hex-int-0x-prefix"/imm32 68/push 0x34/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 test-next-hex-int-zero: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # (eax..ecx) = "+0)" b8/copy-to-eax "+0)"/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 = next-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-hex-int/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-next-hex-int-zero"/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 test-next-hex-int-0-prefix: # . 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 = next-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, 3, msg) # . . push args 68/push "F - test-next-hex-int-0-prefix"/imm32 68/push 3/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 test-next-hex-int-negative: # . 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 = next-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, -3, msg) # . . push args 68/push "F - test-next-hex-int-negative"/imm32 68/push -3/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 test-next-hex-int-negative-with-space: # . 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 = next-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, -3, msg) # . . push args 68/push "F - test-next-hex-int-negative-with-space"/imm32 68/push -3/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 # assumes 'in' starts a positive unsigned integer # returns the value of the integer # side-effect: modifies 'in' to skip past the integer next-positive-hex-int: # in : (address slice) -> result/eax # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # . save registers 51/push-ecx 52/push-edx 53/push-ebx 56/push-esi 57/push-edi # result/edi = 0 31/xor 3/mod/direct 7/rm32/edi . . . 7/r32/edi . . # clear edi # esi = in 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi # edx = in->end 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 4/disp8 . # copy *(esi+4) to edx # curr/ecx = in->start 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx # negate?/ebx = false 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx # eax = *curr 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 $next-positive-hex-int:initial-0: # skip past leading '0x' # . if (*curr != '0') jump to loop 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL 3d/compare-eax-and 0x30/imm32/0 75/jump-if-not-equal $next-positive-hex-int:loop/disp8 # . ++curr 41/increment-ecx $next-positive-hex-int:initial-0x: # . if (curr >= in->end) return result 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx 73/jump-if-greater-or-equal-unsigned $next-positive-hex-int:end/disp8 # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again 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 3d/compare-eax-and 0x78/imm32/x 75/jump-if-not-equal $next-positive-hex-int:loop/disp8 # . ++curr 41/increment-ecx $next-positive-hex-int:loop: # if (curr >= in->end) break 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx 73/jump-if-greater-or-equal-unsigned $next-positive-hex-int:end/disp8 # if (!is-hex-digit?(*curr)) break # . eax = *curr 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL # . eax = is-hex-digit?(*curr) # . . push args 50/push-eax # . . call e8/call is-hex-digit?/disp32 # . . discard args 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 74/jump-if-equal $next-positive-hex-int:end/disp8 # eax = from-hex-char(*curr) # . . copy arg to eax 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL # . . call e8/call from-hex-char/disp32 # result = result * 16 + eax c1/shift 4/subop/left 3/mod/direct 7/rm32/edi . . . . . 4/imm8 # shift edi left by 4 bits 01/add 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # add eax to edi # ++curr 41/increment-ecx # loop eb/jump $next-positive-hex-int:loop/disp8 $next-positive-hex-int:end: # word->start = curr 89/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy ecx to *esi # return edi 89/copy 3/mod/direct 0/rm32/eax . . . 7/r32/edi . . # copy edi to eax # . restore registers 5f/pop-to-edi 5e/pop-to-esi 5b/pop-to-ebx 5a/pop-to-edx 59/pop-to-ecx # . epilog 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 5d/pop-to-ebp c3/return test-next-positive-hex-int-single-digit: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # (eax..ecx) = "a)" b8/copy-to-eax "a)"/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 = next-positive-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-positive-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, 0xa, msg) # . . push args 68/push "F - test-next-positive-hex-int-single-digit"/imm32 68/push 0xa/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 test-next-positive-hex-int-multi-digit: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # (eax..ecx) = "34a)" b8/copy-to-eax "34a)"/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 = next-positive-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-positive-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, 0x34a, msg) # . . push args 68/push "F - test-next-positive-hex-int-multi-digit"/imm32 68/push 0x34a/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 test-next-positive-hex-int-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 = next-positive-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-positive-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, 0x34, msg) # . . push args 68/push "F - test-next-positive-hex-int-0x-prefix"/imm32 68/push 0x34/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 test-next-positive-hex-int-zero: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # (eax..ecx) = "0" b8/copy-to-eax "0"/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 = next-positive-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-positive-hex-int/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-next-positive-hex-int-zero"/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 test-next-positive-hex-int-0-prefix: # . 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 = next-positive-hex-int(slice) # . . push args 51/push-ecx # . . call e8/call next-positive-hex-int/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # check-ints-equal(eax, 3, msg) # . . push args 68/push "F - test-next-positive-hex-int-0-prefix"/imm32 68/push 3/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 == data Registers: # (table string int) # a table is a stream 0x40/imm32/write 0/imm32/read 0x40/imm32/length # data "eax"/imm32 0/imm32 "ecx"/imm32 1/imm32 "edx"/imm32 2/imm32 "ebx"/imm32 3/imm32 "esp"/imm32 4/imm32 "ebp"/imm32 5/imm32 "esi"/imm32 6/imm32 "edi"/imm32 7/imm32 # . . vim:nowrap:textwidth=0