diff --git a/html/subx/apps/crenshaw2-1.subx.html b/html/subx/apps/crenshaw2-1.subx.html index b4e300ad..db1f08b6 100644 --- a/html/subx/apps/crenshaw2-1.subx.html +++ b/html/subx/apps/crenshaw2-1.subx.html @@ -93,606 +93,611 @@ if ('onhashchange' in window) { 30 # . op subop mod rm32 base index scale r32 31 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes 32 - 33 # main: run tests if necessary, call 'compile' if not - 34 # . prolog - 35 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 36 # - if argc > 1 and argv[1] == "test" then return run_tests() - 37 # . argc > 1 - 38 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP - 39 7e/jump-if-lesser-or-equal $run-main/disp8 - 40 # . argv[1] == "test" - 41 # . . push args - 42 68/push "test"/imm32 - 43 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) - 44 # . . call - 45 e8/call kernel-string-equal/disp32 - 46 # . . discard args - 47 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 48 # . check result - 49 3d/compare-EAX 1/imm32 - 50 75/jump-if-not-equal $run-main/disp8 - 51 # . run-tests() - 52 e8/call run-tests/disp32 - 53 #? e8/call test-get-num-reads-multiple-digits/disp32 - 54 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 55 eb/jump $main:end/disp8 - 56 $run-main: - 57 # - otherwise read a program from stdin and emit its translation to stdout - 58 # var ed/EAX : exit-descriptor - 59 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 60 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - 61 # configure ed to really exit() - 62 # . ed->target = 0 - 63 c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 64 # return compile(Stdin, 1/stdout, 2/stderr, ed) - 65 # . . push args - 66 50/push-EAX/ed - 67 68/push 2/imm32/stderr - 68 68/push 1/imm32/stdout - 69 68/push Stdin/imm32 - 70 # . . call - 71 e8/call compile/disp32 - 72 # . . discard args - 73 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 74 # . syscall(exit, 0) - 75 bb/copy-to-EBX 0/imm32 - 76 $main:end: - 77 b8/copy-to-EAX 1/imm32/exit - 78 cd/syscall 0x80/imm8 - 79 - 80 # the main entry point - 81 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - 82 # . prolog - 83 55/push-EBP - 84 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 85 # . save registers - 86 50/push-EAX - 87 51/push-ECX - 88 # . Look = get-char(in) - 89 # . . push args - 90 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) - 91 # . . call - 92 e8/call get-char/disp32 - 93 # . . discard args - 94 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 95 # var num/ECX : (address stream) on the stack - 96 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - 97 # We won't add more, so that we can get overflow-handling for free. - 98 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. - 99 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP -100 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX -101 # num->length = 7 -102 c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) -103 # read a digit from 'in' into 'num' -104 # . clear-stream(num) -105 # . . push args -106 51/push-ECX -107 # . . call -108 e8/call clear-stream/disp32 -109 # . . discard args -110 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -111 # . get-num(in, num, err, ed) -112 # . . push args -113 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) -114 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -115 51/push-ECX/num -116 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) -117 # . . call -118 e8/call get-num/disp32 -119 # . . discard args -120 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -121 # render 'num' into the following template on 'out': -122 # bb/copy-to-EBX _num_ -123 # b8/copy-to-EAX 1/imm32/exit -124 # cd/syscall 0x80/imm8 -125 # -126 # . EAX = write(out, "bb/copy-to-EBX ") -127 # . . push args -128 68/push "bb/copy-to-EBX "/imm32 -129 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -130 # . . call -131 e8/call write/disp32 -132 # . . discard args -133 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -134 # . write-stream(out, num) -135 # . . push args -136 51/push-ECX/num -137 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -138 # . . call -139 e8/call write-stream/disp32 -140 # . . discard args -141 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -142 # . write(out, Newline) -143 # . . push args -144 68/push Newline/imm32 -145 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -146 # . . call -147 e8/call write/disp32 -148 # . . discard args -149 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -150 # . write(out, "b8/copy-to-EAX 1/imm32/exit") -151 # . . push args -152 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 -153 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -154 # . . call -155 e8/call write/disp32 -156 # . . discard args -157 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -158 # . write(out, Newline) -159 # . . push args -160 68/push Newline/imm32 -161 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -162 # . . call -163 e8/call write/disp32 -164 # . . discard args -165 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -166 # . write(out, "cd/syscall 0x80/imm8") -167 # . . push args -168 68/push "cd/syscall 0x80/imm8"/imm32 -169 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -170 # . . call -171 e8/call write/disp32 -172 # . . discard args -173 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -174 # . write(out, Newline) -175 # . . push args -176 68/push Newline/imm32 -177 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -178 # . . call -179 e8/call write/disp32 -180 # . . discard args -181 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -182 # . restore registers -183 59/pop-to-ECX -184 58/pop-to-EAX -185 # . epilog -186 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -187 5d/pop-to-EBP -188 c3/return -189 -190 # Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'. -191 # Input comes from the global variable 'Look', and we leave the next byte from -192 # 'in' into it on exit. -193 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> -194 # pseudocode: -195 # if !is-digit?(Look) expected(ed, err, "integer") -196 # if out.write >= out.length -197 # write(err, "Error: too many digits in number\n") -198 # stop(ed, 1) -199 # out.data[out.write] = LSB(Look) -200 # ++out.write -201 # Look = get-char(in) -202 # -203 # registers: -204 # ESI : in -205 # EDI : out -206 # EAX : temp -207 # ECX : out->write -208 # EDX : out->length -209 # EBX : temp2 -210 # We can't allocate Look to a register because it gets written implicitly in -211 # get-char in each iteration of the loop. (Thereby demonstrating that it's -212 # not the right interface for us. But we'll keep it just to follow Crenshaw.) -213 # -214 # . prolog -215 55/push-EBP -216 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -217 # - if is-digit?(Look) expected(ed, err, "integer") -218 # . EAX = is-digit?(Look) -219 # . . push args -220 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look -221 # . . call -222 e8/call is-digit?/disp32 -223 # . . discard args -224 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -225 # . if EAX == 0 -226 3d/compare-EAX 0/imm32 -227 75/jump-if-not-equal $get-num:main/disp8 -228 # . expected(ed, err, "integer") -229 # . . push args -230 68/push "integer"/imm32 -231 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -232 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) -233 # . . call -234 e8/call expected/disp32 # never returns -235 # . . discard args -236 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -237 $get-num:main: -238 # - otherwise read a digit -239 # . save registers -240 50/push-EAX -241 51/push-ECX -242 52/push-EDX -243 53/push-EBX -244 56/push-ESI -245 57/push-EDI -246 # read necessary variables to registers -247 # ESI = in -248 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -249 # EDI = out -250 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -251 # ECX = out->write -252 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX -253 # EDX = out->length -254 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX -255 # if out->write >= out->length error -256 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX -257 7d/jump-if-lesser $get-num:stage2/disp8 -258 # . error(ed, err, msg) # TODO: show full number -259 # . . push args -260 68/push "get-num: too many digits in number"/imm32 -261 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -262 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) -263 # . . call -264 e8/call error/disp32 # never returns -265 # . . discard args -266 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -267 $get-num:stage2: -268 # out->data[out->write] = LSB(Look) -269 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX -270 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX -271 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX -272 # ++out->write -273 41/increment-ECX -274 # Look = get-char(in) -275 # . . push args -276 56/push-ESI -277 # . . call -278 e8/call get-char/disp32 -279 # . . discard args -280 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -281 $get-num:loop-end: -282 # persist necessary variables from registers -283 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI -284 # . restore registers -285 5f/pop-to-EDI -286 5e/pop-to-ESI -287 5b/pop-to-EBX -288 5a/pop-to-EDX -289 59/pop-to-ECX -290 58/pop-to-EAX -291 # . epilog -292 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -293 5d/pop-to-EBP -294 c3/return -295 -296 test-get-num-reads-single-digit: -297 # - check that get-num returns first character if it's a digit -298 # This test uses exit-descriptors. Use EBP for setting up local variables. -299 55/push-EBP -300 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -301 # setup -302 # . clear-stream(_test-stream) -303 # . . push args -304 68/push _test-stream/imm32 -305 # . . call -306 e8/call clear-stream/disp32 -307 # . . discard args -308 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -309 # . clear-stream(_test-buffered-file+4) -310 # . . push args -311 b8/copy-to-EAX _test-buffered-file/imm32 -312 05/add-to-EAX 4/imm32 -313 50/push-EAX -314 # . . call -315 e8/call clear-stream/disp32 -316 # . . discard args -317 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -318 # . clear-stream(_test-output-stream) -319 # . . push args -320 68/push _test-output-stream/imm32 -321 # . . call -322 e8/call clear-stream/disp32 -323 # . . discard args -324 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -325 # . clear-stream(_test-error-stream) -326 # . . push args -327 68/push _test-error-stream/imm32 -328 # . . call -329 e8/call clear-stream/disp32 -330 # . . discard args -331 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -332 # initialize 'in' -333 # . write(_test-stream, "3") -334 # . . push args -335 68/push "3"/imm32 -336 68/push _test-stream/imm32 -337 # . . call -338 e8/call write/disp32 -339 # . . discard args -340 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -341 # initialize exit-descriptor 'ed' for the call to 'get-num' below -342 # . var ed/EAX : exit-descriptor -343 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -344 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX -345 # . tailor-exit-descriptor(ed, 16) -346 # . . push args -347 68/push 0x10/imm32/nbytes-of-args-for-get-num -348 50/push-EAX/ed -349 # . . call -350 e8/call tailor-exit-descriptor/disp32 -351 # . . discard args -352 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -353 # prime the pump -354 # . get-char(_test-buffered-file) -355 # . . push args -356 68/push _test-buffered-file/imm32 -357 # . . call -358 e8/call get-char/disp32 -359 # . . discard args -360 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -361 # get-num(in, out, err, ed) -362 # . . push args -363 50/push-EAX/ed -364 68/push _test-error-stream/imm32 -365 68/push _test-output-stream/imm32 -366 68/push _test-buffered-file/imm32 -367 # . . call -368 e8/call get-num/disp32 -369 # registers except ESP may be clobbered at this point -370 # . . discard args -371 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -372 # check-ints-equal(*_test-output-stream.data, '3') -373 # . . push args -374 68/push "F - test-get-num-reads-single-digit"/imm32 -375 68/push 0x33/imm32 -376 b8/copy-to-EAX _test-output-stream/imm32 -377 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -378 # . . call -379 e8/call check-ints-equal/disp32 -380 # . . discard args -381 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -382 # . reclaim locals -383 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -384 5d/pop-to-EBP -385 c3/return -386 -387 test-get-num-aborts-on-non-digit-in-Look: -388 # - check that get-num returns first character if it's a digit -389 # This test uses exit-descriptors. Use EBP for setting up local variables. -390 55/push-EBP -391 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -392 # setup -393 # . clear-stream(_test-stream) -394 # . . push args -395 68/push _test-stream/imm32 -396 # . . call -397 e8/call clear-stream/disp32 -398 # . . discard args -399 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -400 # . clear-stream(_test-buffered-file+4) -401 # . . push args -402 b8/copy-to-EAX _test-buffered-file/imm32 -403 05/add-to-EAX 4/imm32 -404 50/push-EAX -405 # . . call -406 e8/call clear-stream/disp32 -407 # . . discard args -408 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -409 # . clear-stream(_test-output-stream) -410 # . . push args -411 68/push _test-output-stream/imm32 -412 # . . call -413 e8/call clear-stream/disp32 -414 # . . discard args -415 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -416 # . clear-stream(_test-error-stream) -417 # . . push args -418 68/push _test-error-stream/imm32 -419 # . . call -420 e8/call clear-stream/disp32 -421 # . . discard args -422 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -423 # initialize 'in' -424 # . write(_test-stream, "3") -425 # . . push args -426 68/push "3"/imm32 -427 68/push _test-stream/imm32 -428 # . . call -429 e8/call write/disp32 -430 # . . discard args -431 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -432 # initialize exit-descriptor 'ed' for the call to 'get-num' below -433 # . var ed/EAX : (address exit-descriptor) -434 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -435 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX -436 # . tailor-exit-descriptor(ed, 16) -437 # . . push args -438 68/push 0x10/imm32/nbytes-of-args-for-get-num -439 50/push-EAX/ed -440 # . . call -441 e8/call tailor-exit-descriptor/disp32 -442 # . . discard args -443 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -444 # *don't* prime the pump -445 # get-num(in, out, err, ed) -446 # . . push args -447 50/push-EAX/ed -448 68/push _test-error-stream/imm32 -449 68/push _test-output-stream/imm32 -450 68/push _test-buffered-file/imm32 -451 # . . call -452 e8/call get-num/disp32 -453 # registers except ESP may be clobbered at this point -454 # . . discard args -455 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -456 # check that get-num tried to call exit(1) -457 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 -458 # . . push args -459 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 -460 68/push 2/imm32 -461 # . . push ed->value -462 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -463 # . . call -464 e8/call check-ints-equal/disp32 -465 # . . discard args -466 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -467 # . reclaim locals -468 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -469 5d/pop-to-EBP -470 c3/return -471 -472 ## helpers -473 -474 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) -475 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> -476 # . prolog -477 55/push-EBP -478 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -479 # write(f, "Error: ") -480 # . . push args -481 68/push "Error: "/imm32 -482 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -483 # . . call -484 e8/call write/disp32 -485 # . . discard args -486 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -487 # write(f, s) -488 # . . push args -489 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -490 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -491 # . . call -492 e8/call write/disp32 -493 # . . discard args -494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -495 # write(f, " expected") -496 # . . push args -497 68/push " expected"/imm32 -498 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -499 # . . call -500 e8/call write/disp32 -501 # . . discard args -502 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -503 # write(f, Newline) -504 # . . push args -505 68/push Newline/imm32 -506 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -507 # . . call -508 e8/call write/disp32 -509 # . . discard args -510 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -511 # stop(ed, 1) -512 # . . push args -513 68/push 1/imm32 -514 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) -515 # . . call -516 e8/call stop/disp32 -517 # should never get past this point -518 # . epilog -519 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -520 5d/pop-to-EBP -521 c3/return -522 -523 # write(f, "Error: "+s+"\n") then stop(ed, 1) -524 error: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> -525 # . prolog -526 55/push-EBP -527 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -528 # write(f, "Error: ") -529 # . . push args -530 68/push "Error: "/imm32 -531 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -532 # . . call -533 e8/call write/disp32 -534 # . . discard args -535 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -536 # write(f, s) -537 # . . push args -538 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -539 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -540 # . . call -541 e8/call write/disp32 -542 # . . discard args -543 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -544 # write(f, Newline) -545 # . . push args -546 68/push Newline/imm32 -547 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -548 # . . call -549 e8/call write/disp32 -550 # . . discard args -551 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -552 # stop(ed, 1) -553 # . . push args -554 68/push 1/imm32 -555 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) -556 # . . call -557 e8/call stop/disp32 -558 # should never get past this point -559 # . epilog -560 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -561 5d/pop-to-EBP -562 c3/return -563 -564 # read a byte from 'f', and save it in 'Look' -565 get-char: # f : (address buffered-file) -> <void> -566 # . prolog -567 55/push-EBP -568 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -569 # . save registers -570 50/push-EAX -571 # read-byte(f) -572 # . . push args -573 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) -574 # . . call -575 e8/call read-byte/disp32 -576 # . . discard args -577 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -578 # save EAX to Look -579 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look -580 # . restore registers -581 58/pop-to-EAX -582 # . epilog -583 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -584 5d/pop-to-EBP -585 c3/return -586 -587 is-digit?: # c : int -> bool/EAX -588 # . prolog -589 55/push-EBP -590 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -591 # EAX = false -592 b8/copy-to-EAX 0/imm32 -593 # if c < '0' return false -594 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) -595 7c/jump-if-lesser $is-digit?:end/disp8 -596 # if c > '9' return false -597 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) -598 7f/jump-if-greater $is-digit?:end/disp8 -599 # otherwise return true -600 b8/copy-to-EAX 1/imm32 -601 $is-digit?:end: -602 # . epilog -603 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -604 5d/pop-to-EBP -605 c3/return -606 -607 == data -608 -609 Look: # (char) -610 00 00 00 00 # = 0 + 33 #? # for debugging: run a single test; don't bother setting status code + 34 #? e8/call test-get-num-reads-single-digit/disp32 + 35 #? eb/jump $main:end/disp8 + 36 + 37 # main: run tests if necessary, call 'compile' if not + 38 # . prolog + 39 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 40 # - if argc > 1 and argv[1] == "test" then return run_tests() + 41 # . argc > 1 + 42 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP + 43 7e/jump-if-lesser-or-equal $run-main/disp8 + 44 # . argv[1] == "test" + 45 # . . push args + 46 68/push "test"/imm32 + 47 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + 48 # . . call + 49 e8/call kernel-string-equal/disp32 + 50 # . . discard args + 51 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 52 # . check result + 53 3d/compare-EAX 1/imm32 + 54 75/jump-if-not-equal $run-main/disp8 + 55 # . run-tests() + 56 e8/call run-tests/disp32 + 57 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 58 eb/jump $main:end/disp8 + 59 $run-main: + 60 # - otherwise read a program from stdin and emit its translation to stdout + 61 # var ed/EAX : exit-descriptor + 62 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 63 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + 64 # configure ed to really exit() + 65 # . ed->target = 0 + 66 c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + 67 # return compile(Stdin, 1/stdout, 2/stderr, ed) + 68 # . . push args + 69 50/push-EAX/ed + 70 68/push 2/imm32/stderr + 71 68/push 1/imm32/stdout + 72 68/push Stdin/imm32 + 73 # . . call + 74 e8/call compile/disp32 + 75 # . . discard args + 76 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 77 # . syscall(exit, 0) + 78 bb/copy-to-EBX 0/imm32 + 79 $main:end: + 80 b8/copy-to-EAX 1/imm32/exit + 81 cd/syscall 0x80/imm8 + 82 + 83 # the main entry point + 84 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> + 85 # . prolog + 86 55/push-EBP + 87 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 88 # . save registers + 89 50/push-EAX + 90 51/push-ECX + 91 # . Look = get-char(in) + 92 # . . push args + 93 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + 94 # . . call + 95 e8/call get-char/disp32 + 96 # . . discard args + 97 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 98 # var num/ECX : (address stream) on the stack + 99 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. +100 # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. +101 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. +102 # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. +103 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP +104 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX +105 # initialize the stream +106 # . num->length = 7 +107 c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) +108 # . clear-stream(num) +109 # . . push args +110 51/push-ECX +111 # . . call +112 e8/call clear-stream/disp32 +113 # . . discard args +114 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +115 # read a digit from 'in' into 'num' +116 # . get-num(in, num, err, ed) +117 # . . push args +118 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) +119 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +120 51/push-ECX/num +121 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) +122 # . . call +123 e8/call get-num/disp32 +124 # . . discard args +125 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +126 # render 'num' into the following template on 'out': +127 # bb/copy-to-EBX _num_ +128 # b8/copy-to-EAX 1/imm32/exit +129 # cd/syscall 0x80/imm8 +130 # +131 # . EAX = write(out, "bb/copy-to-EBX ") +132 # . . push args +133 68/push "bb/copy-to-EBX "/imm32 +134 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +135 # . . call +136 e8/call write/disp32 +137 # . . discard args +138 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +139 # . write-stream(out, num) +140 # . . push args +141 51/push-ECX/num +142 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +143 # . . call +144 e8/call write-stream/disp32 +145 # . . discard args +146 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +147 # . write(out, Newline) +148 # . . push args +149 68/push Newline/imm32 +150 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +151 # . . call +152 e8/call write/disp32 +153 # . . discard args +154 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +155 # . write(out, "b8/copy-to-EAX 1/imm32/exit") +156 # . . push args +157 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 +158 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +159 # . . call +160 e8/call write/disp32 +161 # . . discard args +162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +163 # . write(out, Newline) +164 # . . push args +165 68/push Newline/imm32 +166 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +167 # . . call +168 e8/call write/disp32 +169 # . . discard args +170 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +171 # . write(out, "cd/syscall 0x80/imm8") +172 # . . push args +173 68/push "cd/syscall 0x80/imm8"/imm32 +174 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +175 # . . call +176 e8/call write/disp32 +177 # . . discard args +178 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +179 # . write(out, Newline) +180 # . . push args +181 68/push Newline/imm32 +182 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +183 # . . call +184 e8/call write/disp32 +185 # . . discard args +186 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +187 # . restore registers +188 59/pop-to-ECX +189 58/pop-to-EAX +190 # . epilog +191 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +192 5d/pop-to-EBP +193 c3/return +194 +195 # Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'. +196 # Input comes from the global variable 'Look', and we leave the next byte from +197 # 'in' into it on exit. +198 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> +199 # pseudocode: +200 # if !is-digit?(Look) expected(ed, err, "integer") +201 # if out.write >= out.length +202 # write(err, "Error: too many digits in number\n") +203 # stop(ed, 1) +204 # out.data[out.write] = LSB(Look) +205 # ++out.write +206 # Look = get-char(in) +207 # +208 # registers: +209 # ESI : in +210 # EDI : out +211 # EAX : temp +212 # ECX : out->write +213 # EDX : out->length +214 # EBX : temp2 +215 # We can't allocate Look to a register because it gets written implicitly in +216 # get-char in each iteration of the loop. (Thereby demonstrating that it's +217 # not the right interface for us. But we'll keep it just to follow Crenshaw.) +218 # +219 # . prolog +220 55/push-EBP +221 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +222 # - if is-digit?(Look) expected(ed, err, "integer") +223 # . EAX = is-digit?(Look) +224 # . . push args +225 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +226 # . . call +227 e8/call is-digit?/disp32 +228 # . . discard args +229 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +230 # . if EAX == 0 +231 3d/compare-EAX 0/imm32 +232 75/jump-if-not-equal $get-num:main/disp8 +233 # . expected(ed, err, "integer") +234 # . . push args +235 68/push "integer"/imm32 +236 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +237 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) +238 # . . call +239 e8/call expected/disp32 # never returns +240 # . . discard args +241 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +242 $get-num:main: +243 # - otherwise read a digit +244 # . save registers +245 50/push-EAX +246 51/push-ECX +247 52/push-EDX +248 53/push-EBX +249 56/push-ESI +250 57/push-EDI +251 # read necessary variables to registers +252 # ESI = in +253 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +254 # EDI = out +255 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI +256 # ECX = out->write +257 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX +258 # EDX = out->length +259 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX +260 # if out->write >= out->length error +261 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX +262 7d/jump-if-lesser $get-num:stage2/disp8 +263 # . error(ed, err, msg) # TODO: show full number +264 # . . push args +265 68/push "get-num: too many digits in number"/imm32 +266 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +267 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) +268 # . . call +269 e8/call error/disp32 # never returns +270 # . . discard args +271 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +272 $get-num:stage2: +273 # out->data[out->write] = LSB(Look) +274 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX +275 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX +276 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX +277 # ++out->write +278 41/increment-ECX +279 # Look = get-char(in) +280 # . . push args +281 56/push-ESI +282 # . . call +283 e8/call get-char/disp32 +284 # . . discard args +285 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +286 $get-num:loop-end: +287 # persist necessary variables from registers +288 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI +289 # . restore registers +290 5f/pop-to-EDI +291 5e/pop-to-ESI +292 5b/pop-to-EBX +293 5a/pop-to-EDX +294 59/pop-to-ECX +295 58/pop-to-EAX +296 # . epilog +297 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +298 5d/pop-to-EBP +299 c3/return +300 +301 test-get-num-reads-single-digit: +302 # - check that get-num returns first character if it's a digit +303 # This test uses exit-descriptors. Use EBP for setting up local variables. +304 55/push-EBP +305 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +306 # setup +307 # . clear-stream(_test-stream) +308 # . . push args +309 68/push _test-stream/imm32 +310 # . . call +311 e8/call clear-stream/disp32 +312 # . . discard args +313 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +314 # . clear-stream(_test-buffered-file+4) +315 # . . push args +316 b8/copy-to-EAX _test-buffered-file/imm32 +317 05/add-to-EAX 4/imm32 +318 50/push-EAX +319 # . . call +320 e8/call clear-stream/disp32 +321 # . . discard args +322 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +323 # . clear-stream(_test-output-stream) +324 # . . push args +325 68/push _test-output-stream/imm32 +326 # . . call +327 e8/call clear-stream/disp32 +328 # . . discard args +329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +330 # . clear-stream(_test-error-stream) +331 # . . push args +332 68/push _test-error-stream/imm32 +333 # . . call +334 e8/call clear-stream/disp32 +335 # . . discard args +336 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +337 # initialize 'in' +338 # . write(_test-stream, "3") +339 # . . push args +340 68/push "3"/imm32 +341 68/push _test-stream/imm32 +342 # . . call +343 e8/call write/disp32 +344 # . . discard args +345 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +346 # initialize exit-descriptor 'ed' for the call to 'get-num' below +347 # . var ed/EAX : exit-descriptor +348 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +349 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX +350 # . tailor-exit-descriptor(ed, 16) +351 # . . push args +352 68/push 0x10/imm32/nbytes-of-args-for-get-num +353 50/push-EAX/ed +354 # . . call +355 e8/call tailor-exit-descriptor/disp32 +356 # . . discard args +357 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +358 # prime the pump +359 # . get-char(_test-buffered-file) +360 # . . push args +361 68/push _test-buffered-file/imm32 +362 # . . call +363 e8/call get-char/disp32 +364 # . . discard args +365 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +366 # get-num(in, out, err, ed) +367 # . . push args +368 50/push-EAX/ed +369 68/push _test-error-stream/imm32 +370 68/push _test-output-stream/imm32 +371 68/push _test-buffered-file/imm32 +372 # . . call +373 e8/call get-num/disp32 +374 # registers except ESP may be clobbered at this point +375 # . . discard args +376 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +377 # check-ints-equal(*_test-output-stream.data, '3') +378 # . . push args +379 68/push "F - test-get-num-reads-single-digit"/imm32 +380 68/push 0x33/imm32 +381 b8/copy-to-EAX _test-output-stream/imm32 +382 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +383 # . . call +384 e8/call check-ints-equal/disp32 +385 # . . discard args +386 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +387 # . reclaim locals +388 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +389 5d/pop-to-EBP +390 c3/return +391 +392 test-get-num-aborts-on-non-digit-in-Look: +393 # - check that get-num returns first character if it's a digit +394 # This test uses exit-descriptors. Use EBP for setting up local variables. +395 55/push-EBP +396 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +397 # setup +398 # . clear-stream(_test-stream) +399 # . . push args +400 68/push _test-stream/imm32 +401 # . . call +402 e8/call clear-stream/disp32 +403 # . . discard args +404 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +405 # . clear-stream(_test-buffered-file+4) +406 # . . push args +407 b8/copy-to-EAX _test-buffered-file/imm32 +408 05/add-to-EAX 4/imm32 +409 50/push-EAX +410 # . . call +411 e8/call clear-stream/disp32 +412 # . . discard args +413 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +414 # . clear-stream(_test-output-stream) +415 # . . push args +416 68/push _test-output-stream/imm32 +417 # . . call +418 e8/call clear-stream/disp32 +419 # . . discard args +420 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +421 # . clear-stream(_test-error-stream) +422 # . . push args +423 68/push _test-error-stream/imm32 +424 # . . call +425 e8/call clear-stream/disp32 +426 # . . discard args +427 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +428 # initialize 'in' +429 # . write(_test-stream, "3") +430 # . . push args +431 68/push "3"/imm32 +432 68/push _test-stream/imm32 +433 # . . call +434 e8/call write/disp32 +435 # . . discard args +436 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +437 # initialize exit-descriptor 'ed' for the call to 'get-num' below +438 # . var ed/EAX : (address exit-descriptor) +439 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +440 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX +441 # . tailor-exit-descriptor(ed, 16) +442 # . . push args +443 68/push 0x10/imm32/nbytes-of-args-for-get-num +444 50/push-EAX/ed +445 # . . call +446 e8/call tailor-exit-descriptor/disp32 +447 # . . discard args +448 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +449 # *don't* prime the pump +450 # get-num(in, out, err, ed) +451 # . . push args +452 50/push-EAX/ed +453 68/push _test-error-stream/imm32 +454 68/push _test-output-stream/imm32 +455 68/push _test-buffered-file/imm32 +456 # . . call +457 e8/call get-num/disp32 +458 # registers except ESP may be clobbered at this point +459 # . . discard args +460 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +461 # check that get-num tried to call exit(1) +462 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 +463 # . . push args +464 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 +465 68/push 2/imm32 +466 # . . push ed->value +467 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +468 # . . call +469 e8/call check-ints-equal/disp32 +470 # . . discard args +471 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +472 # . reclaim locals +473 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +474 5d/pop-to-EBP +475 c3/return +476 +477 ## helpers +478 +479 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) +480 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> +481 # . prolog +482 55/push-EBP +483 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +484 # write(f, "Error: ") +485 # . . push args +486 68/push "Error: "/imm32 +487 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +488 # . . call +489 e8/call write/disp32 +490 # . . discard args +491 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +492 # write(f, s) +493 # . . push args +494 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +495 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +496 # . . call +497 e8/call write/disp32 +498 # . . discard args +499 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +500 # write(f, " expected") +501 # . . push args +502 68/push " expected"/imm32 +503 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +504 # . . call +505 e8/call write/disp32 +506 # . . discard args +507 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +508 # write(f, Newline) +509 # . . push args +510 68/push Newline/imm32 +511 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +512 # . . call +513 e8/call write/disp32 +514 # . . discard args +515 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +516 # stop(ed, 1) +517 # . . push args +518 68/push 1/imm32 +519 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) +520 # . . call +521 e8/call stop/disp32 +522 # should never get past this point +523 # . epilog +524 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +525 5d/pop-to-EBP +526 c3/return +527 +528 # write(f, "Error: "+s+"\n") then stop(ed, 1) +529 error: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> +530 # . prolog +531 55/push-EBP +532 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +533 # write(f, "Error: ") +534 # . . push args +535 68/push "Error: "/imm32 +536 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +537 # . . call +538 e8/call write/disp32 +539 # . . discard args +540 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +541 # write(f, s) +542 # . . push args +543 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +544 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +545 # . . call +546 e8/call write/disp32 +547 # . . discard args +548 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +549 # write(f, Newline) +550 # . . push args +551 68/push Newline/imm32 +552 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +553 # . . call +554 e8/call write/disp32 +555 # . . discard args +556 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +557 # stop(ed, 1) +558 # . . push args +559 68/push 1/imm32 +560 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) +561 # . . call +562 e8/call stop/disp32 +563 # should never get past this point +564 # . epilog +565 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +566 5d/pop-to-EBP +567 c3/return +568 +569 # read a byte from 'f', and save it in 'Look' +570 get-char: # f : (address buffered-file) -> <void> +571 # . prolog +572 55/push-EBP +573 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +574 # . save registers +575 50/push-EAX +576 # read-byte(f) +577 # . . push args +578 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) +579 # . . call +580 e8/call read-byte/disp32 +581 # . . discard args +582 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +583 # save EAX to Look +584 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look +585 # . restore registers +586 58/pop-to-EAX +587 # . epilog +588 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +589 5d/pop-to-EBP +590 c3/return +591 +592 is-digit?: # c : int -> bool/EAX +593 # . prolog +594 55/push-EBP +595 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +596 # EAX = false +597 b8/copy-to-EAX 0/imm32 +598 # if c < '0' return false +599 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) +600 7c/jump-if-lesser $is-digit?:end/disp8 +601 # if c > '9' return false +602 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) +603 7f/jump-if-greater $is-digit?:end/disp8 +604 # otherwise return true +605 b8/copy-to-EAX 1/imm32 +606 $is-digit?:end: +607 # . epilog +608 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +609 5d/pop-to-EBP +610 c3/return 611 -612 _test-output-stream: -613 # current write index -614 00 00 00 00 -615 # current read index -616 00 00 00 00 -617 # length (= 8) -618 08 00 00 00 -619 # data -620 00 00 00 00 00 00 00 00 # 8 bytes -621 -622 _test-error-stream: -623 # current write index -624 00 00 00 00 -625 # current read index -626 00 00 00 00 -627 # length (= 8) -628 08 00 00 00 -629 # data -630 00 00 00 00 00 00 00 00 # 8 bytes -631 -632 # . . vim:nowrap:textwidth=0 +612 == data +613 +614 Look: # (char) +615 00 00 00 00 # = 0 +616 +617 _test-output-stream: +618 # current write index +619 00 00 00 00 +620 # current read index +621 00 00 00 00 +622 # length (= 8) +623 08 00 00 00 +624 # data +625 00 00 00 00 00 00 00 00 # 8 bytes +626 +627 _test-error-stream: +628 # current write index +629 00 00 00 00 +630 # current read index +631 00 00 00 00 +632 # length (= 8) +633 08 00 00 00 +634 # data +635 00 00 00 00 00 00 00 00 # 8 bytes +636 +637 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/crenshaw2-1b.subx.html b/html/subx/apps/crenshaw2-1b.subx.html index f38703f0..77d9d41a 100644 --- a/html/subx/apps/crenshaw2-1b.subx.html +++ b/html/subx/apps/crenshaw2-1b.subx.html @@ -93,806 +93,811 @@ if ('onhashchange' in window) { 30 # . op subop mod rm32 base index scale r32 31 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes 32 - 33 # main: run tests if necessary, call 'compile' if not - 34 # . prolog - 35 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 36 # - if argc > 1 and argv[1] == "test" then return run_tests() - 37 # . argc > 1 - 38 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP - 39 7e/jump-if-lesser-or-equal $run-main/disp8 - 40 # . argv[1] == "test" - 41 # . . push args - 42 68/push "test"/imm32 - 43 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) - 44 # . . call - 45 e8/call kernel-string-equal/disp32 - 46 # . . discard args - 47 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 48 # . check result - 49 3d/compare-EAX 1/imm32 - 50 75/jump-if-not-equal $run-main/disp8 - 51 # . run-tests() - 52 e8/call run-tests/disp32 - 53 #? e8/call test-get-num-reads-multiple-digits/disp32 - 54 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 55 eb/jump $main:end/disp8 - 56 $run-main: - 57 # - otherwise read a program from stdin and emit its translation to stdout - 58 # var ed/EAX : exit-descriptor - 59 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 60 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - 61 # configure ed to really exit() - 62 # . ed->target = 0 - 63 c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 64 # return compile(Stdin, 1/stdout, 2/stderr, ed) - 65 # . . push args - 66 50/push-EAX/ed - 67 68/push 2/imm32/stderr - 68 68/push 1/imm32/stdout - 69 68/push Stdin/imm32 - 70 # . . call - 71 e8/call compile/disp32 - 72 # . . discard args - 73 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 74 # . syscall(exit, 0) - 75 bb/copy-to-EBX 0/imm32 - 76 $main:end: - 77 b8/copy-to-EAX 1/imm32/exit - 78 cd/syscall 0x80/imm8 - 79 - 80 # the main entry point - 81 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - 82 # . prolog - 83 55/push-EBP - 84 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 85 # . save registers - 86 50/push-EAX - 87 51/push-ECX - 88 # prime the pump - 89 # . Look = get-char(in) - 90 # . . push args - 91 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) - 92 # . . call - 93 e8/call get-char/disp32 - 94 # . . discard args - 95 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 96 # var num/ECX : (address stream) on the stack - 97 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - 98 # We won't add more, so that we can get overflow-handling for free. - 99 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. -100 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP -101 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX -102 # num->length = 7 -103 c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) -104 # read a digit from 'in' into 'num' -105 # . clear-stream(num) -106 # . . push args -107 51/push-ECX -108 # . . call -109 e8/call clear-stream/disp32 -110 # . . discard args -111 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -112 # . get-num(in, num, err, ed) -113 # . . push args -114 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) -115 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -116 51/push-ECX/num -117 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) -118 # . . call -119 e8/call get-num/disp32 -120 # . . discard args -121 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -122 # render 'num' into the following template on 'out': -123 # bb/copy-to-EBX _num_ -124 # b8/copy-to-EAX 1/imm32/exit -125 # cd/syscall 0x80/imm8 -126 # -127 # . EAX = write(out, "bb/copy-to-EBX ") -128 # . . push args -129 68/push "bb/copy-to-EBX "/imm32 -130 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -131 # . . call -132 e8/call write/disp32 -133 # . . discard args -134 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -135 # . write-stream(out, num) -136 # . . push args -137 51/push-ECX/num -138 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -139 # . . call -140 e8/call write-stream/disp32 -141 # . . discard args -142 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -143 # . write(out, Newline) -144 # . . push args -145 68/push Newline/imm32 -146 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -147 # . . call -148 e8/call write/disp32 -149 # . . discard args -150 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -151 # . write(out, "b8/copy-to-EAX 1/imm32/exit") -152 # . . push args -153 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 -154 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -155 # . . call -156 e8/call write/disp32 -157 # . . discard args -158 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -159 # . write(out, Newline) -160 # . . push args -161 68/push Newline/imm32 -162 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -163 # . . call -164 e8/call write/disp32 -165 # . . discard args -166 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -167 # . write(out, "cd/syscall 0x80/imm8") -168 # . . push args -169 68/push "cd/syscall 0x80/imm8"/imm32 -170 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -171 # . . call -172 e8/call write/disp32 -173 # . . discard args -174 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -175 # . write(out, Newline) -176 # . . push args -177 68/push Newline/imm32 -178 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -179 # . . call -180 e8/call write/disp32 -181 # . . discard args -182 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -183 # . restore registers -184 59/pop-to-ECX -185 58/pop-to-EAX -186 # . epilog -187 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -188 5d/pop-to-EBP -189 c3/return -190 -191 # Read a sequence of digits into 'out'. Abort if there are none, or if there is -192 # no space in 'out'. -193 # Input comes from the global variable 'Look' (first byte) and the argument -194 # 'in' (rest). We leave the next byte from 'in' into 'Look' on exit. -195 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> -196 # pseudocode: -197 # if !is-digit?(Look) expected(ed, err, "integer") -198 # do -199 # if out.write >= out.length -200 # write(err, "Error: too many digits in number\n") -201 # stop(ed, 1) -202 # out.data[out.write] = LSB(Look) -203 # ++out.write -204 # Look = get-char(in) -205 # while is-digit?(Look) -206 # This is complicated because I don't want to hard-code the error strategy in -207 # a general helper like write-byte. Maybe I should just create a local helper. -208 # -209 # within the loop we'll try to keep things in registers: -210 # ESI : in -211 # EDI : out -212 # EAX : temp -213 # ECX : out->write -214 # EDX : out->length -215 # EBX : temp2 -216 # We can't allocate Look to a register because it gets written implicitly in -217 # get-char in each iteration of the loop. (Thereby demonstrating that it's -218 # not the right interface for us. But we'll keep it just to follow Crenshaw.) -219 # -220 # . prolog -221 55/push-EBP -222 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -223 # - if is-digit?(Look) expected(ed, err, "integer") -224 # . EAX = is-digit?(Look) -225 # . . push args -226 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look -227 # . . call -228 e8/call is-digit?/disp32 -229 # . . discard args -230 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -231 # . if EAX == 0 -232 3d/compare-EAX 0/imm32 -233 75/jump-if-not-equal $get-num:main/disp8 -234 # . expected(ed, err, "integer") -235 # . . push args -236 68/push "integer"/imm32 -237 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -238 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) -239 # . . call -240 e8/call expected/disp32 # never returns -241 # . . discard args -242 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -243 $get-num:main: -244 # - otherwise read a digit -245 # . save registers -246 50/push-EAX -247 51/push-ECX -248 52/push-EDX -249 53/push-EBX -250 56/push-ESI -251 57/push-EDI -252 # read necessary variables to registers -253 # ESI = in -254 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -255 # EDI = out -256 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -257 # ECX = out->write -258 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX -259 # EDX = out->length -260 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX -261 $get-num:loop: -262 # if out->write >= out->length error -263 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX -264 7d/jump-if-lesser $get-num:loop-stage2/disp8 -265 # . error(ed, err, msg) # TODO: show full number -266 # . . push args -267 68/push "get-num: too many digits in number"/imm32 -268 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -269 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) -270 # . . call -271 e8/call error/disp32 # never returns -272 # . . discard args -273 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -274 $get-num:loop-stage2: -275 # out->data[out->write] = LSB(Look) -276 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX -277 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX -278 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX -279 # ++out->write -280 41/increment-ECX -281 # Look = get-char(in) -282 # . . push args -283 56/push-ESI -284 # . . call -285 e8/call get-char/disp32 -286 # . . discard args -287 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -288 # if is-digit?(Look) loop -289 # . EAX = is-digit?(Look) -290 # . . push args -291 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look -292 # . . call -293 e8/call is-digit?/disp32 -294 # . . discard args -295 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -296 # . if EAX loop -297 3d/compare-EAX 0/imm32 -298 0f 85/jump-if-not-equal $get-num:loop/disp32 -299 $get-num:loop-end: -300 # persist necessary variables from registers -301 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI -302 # . restore registers -303 5f/pop-to-EDI -304 5e/pop-to-ESI -305 5b/pop-to-EBX -306 5a/pop-to-EDX -307 59/pop-to-ECX -308 58/pop-to-EAX -309 # . epilog -310 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -311 5d/pop-to-EBP -312 c3/return -313 -314 test-get-num-reads-single-digit: -315 # - check that get-num returns first character if it's a digit -316 # This test uses exit-descriptors. Use EBP for setting up local variables. -317 55/push-EBP -318 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -319 # clear all streams -320 # . clear-stream(_test-stream) -321 # . . push args -322 68/push _test-stream/imm32 -323 # . . call -324 e8/call clear-stream/disp32 -325 # . . discard args -326 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -327 # . clear-stream(_test-buffered-file+4) -328 # . . push args -329 b8/copy-to-EAX _test-buffered-file/imm32 -330 05/add-to-EAX 4/imm32 -331 50/push-EAX -332 # . . call -333 e8/call clear-stream/disp32 -334 # . . discard args -335 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -336 # . clear-stream(_test-output-stream) -337 # . . push args -338 68/push _test-output-stream/imm32 -339 # . . call -340 e8/call clear-stream/disp32 -341 # . . discard args -342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -343 # . clear-stream(_test-error-stream) -344 # . . push args -345 68/push _test-error-stream/imm32 -346 # . . call -347 e8/call clear-stream/disp32 -348 # . . discard args -349 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -350 # initialize 'in' -351 # . write(_test-stream, "3") -352 # . . push args -353 68/push "3"/imm32 -354 68/push _test-stream/imm32 -355 # . . call -356 e8/call write/disp32 -357 # . . discard args -358 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -359 # initialize exit-descriptor 'ed' for the call to 'get-num' below -360 # . var ed/EAX : exit-descriptor -361 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -362 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX -363 # . tailor-exit-descriptor(ed, 16) -364 # . . push args -365 68/push 0x10/imm32/nbytes-of-args-for-get-num -366 50/push-EAX/ed -367 # . . call -368 e8/call tailor-exit-descriptor/disp32 -369 # . . discard args -370 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -371 # prime the pump -372 # . get-char(_test-buffered-file) -373 # . . push args -374 68/push _test-buffered-file/imm32 -375 # . . call -376 e8/call get-char/disp32 -377 # . . discard args -378 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -379 # get-num(in, out, err, ed) -380 # . . push args -381 50/push-EAX/ed -382 68/push _test-error-stream/imm32 -383 68/push _test-output-stream/imm32 -384 68/push _test-buffered-file/imm32 -385 # . . call -386 e8/call get-num/disp32 -387 # registers except ESP may be clobbered at this point -388 # . . discard args -389 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -390 # check-ints-equal(*_test-output-stream.data, '3') -391 # . . push args -392 68/push "F - test-get-num-reads-single-digit"/imm32 -393 68/push 0x33/imm32 -394 b8/copy-to-EAX _test-output-stream/imm32 -395 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -396 # . . call -397 e8/call check-ints-equal/disp32 -398 # . . discard args -399 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -400 # . reclaim locals -401 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -402 5d/pop-to-EBP -403 c3/return -404 -405 test-get-num-aborts-on-non-digit-in-Look: -406 # - check that get-num returns first character if it's a digit -407 # This test uses exit-descriptors. Use EBP for setting up local variables. -408 55/push-EBP -409 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -410 # clear all streams -411 # . clear-stream(_test-stream) -412 # . . push args -413 68/push _test-stream/imm32 -414 # . . call -415 e8/call clear-stream/disp32 -416 # . . discard args -417 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -418 # . clear-stream(_test-buffered-file+4) -419 # . . push args -420 b8/copy-to-EAX _test-buffered-file/imm32 -421 05/add-to-EAX 4/imm32 -422 50/push-EAX -423 # . . call -424 e8/call clear-stream/disp32 -425 # . . discard args -426 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -427 # . clear-stream(_test-output-stream) -428 # . . push args -429 68/push _test-output-stream/imm32 -430 # . . call -431 e8/call clear-stream/disp32 -432 # . . discard args -433 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -434 # . clear-stream(_test-error-stream) -435 # . . push args -436 68/push _test-error-stream/imm32 -437 # . . call -438 e8/call clear-stream/disp32 -439 # . . discard args -440 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -441 # initialize 'in' -442 # . write(_test-stream, "3") -443 # . . push args -444 68/push "3"/imm32 -445 68/push _test-stream/imm32 -446 # . . call -447 e8/call write/disp32 -448 # . . discard args -449 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -450 # initialize exit-descriptor 'ed' for the call to 'get-num' below -451 # . var ed/EAX : (address exit-descriptor) -452 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -453 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX -454 # . tailor-exit-descriptor(ed, 16) -455 # . . push args -456 68/push 0x10/imm32/nbytes-of-args-for-get-num -457 50/push-EAX/ed -458 # . . call -459 e8/call tailor-exit-descriptor/disp32 -460 # . . discard args -461 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -462 # *don't* prime the pump -463 # get-num(in, out, err, ed) -464 # . . push args -465 50/push-EAX/ed -466 68/push _test-error-stream/imm32 -467 68/push _test-output-stream/imm32 -468 68/push _test-buffered-file/imm32 -469 # . . call -470 e8/call get-num/disp32 -471 # registers except ESP may be clobbered at this point -472 # . . discard args -473 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -474 # check that get-num tried to call exit(1) -475 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 -476 # . . push args -477 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 -478 68/push 2/imm32 -479 # . . push ed->value -480 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -481 # . . call -482 e8/call check-ints-equal/disp32 -483 # . . discard args -484 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -485 # . reclaim locals -486 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -487 5d/pop-to-EBP -488 c3/return -489 -490 test-get-num-reads-multiple-digits: -491 # - check that get-num returns all initial digits until it encounters a non-digit -492 # This test uses exit-descriptors. Use EBP for setting up local variables. -493 55/push-EBP -494 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -495 # clear all streams -496 # . clear-stream(_test-stream) -497 # . . push args -498 68/push _test-stream/imm32 -499 # . . call -500 e8/call clear-stream/disp32 -501 # . . discard args -502 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -503 # . clear-stream(_test-buffered-file+4) -504 # . . push args -505 b8/copy-to-EAX _test-buffered-file/imm32 -506 05/add-to-EAX 4/imm32 -507 50/push-EAX -508 # . . call -509 e8/call clear-stream/disp32 -510 # . . discard args -511 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -512 # . clear-stream(_test-output-stream) -513 # . . push args -514 68/push _test-output-stream/imm32 -515 # . . call -516 e8/call clear-stream/disp32 -517 # . . discard args -518 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -519 # . clear-stream(_test-error-stream) -520 # . . push args -521 68/push _test-error-stream/imm32 -522 # . . call -523 e8/call clear-stream/disp32 -524 # . . discard args -525 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -526 # initialize 'in' -527 # . write(_test-stream, "3456 x") -528 # . . push args -529 68/push "3456"/imm32 -530 68/push _test-stream/imm32 -531 # . . call -532 e8/call write/disp32 -533 # . . discard args -534 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -535 # initialize exit-descriptor 'ed' for the call to 'get-num' below -536 # . var ed/EAX : (address exit-descriptor) -537 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -538 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX -539 # . tailor-exit-descriptor(ed, 16) -540 # . . push args -541 68/push 0x10/imm32/nbytes-of-args-for-get-num -542 50/push-EAX/ed -543 # . . call -544 e8/call tailor-exit-descriptor/disp32 -545 # . . discard args -546 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -547 # prime the pump -548 # . get-char(_test-buffered-file) -549 # . . push args -550 68/push _test-buffered-file/imm32 -551 # . . call -552 e8/call get-char/disp32 -553 # . . discard args -554 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -555 # get-num(in, out, err, ed) -556 # . . push args -557 50/push-EAX/ed -558 68/push _test-error-stream/imm32 -559 68/push _test-output-stream/imm32 -560 68/push _test-buffered-file/imm32 -561 # . . call -562 e8/call get-num/disp32 -563 # registers except ESP may be clobbered at this point -564 # . . discard args -565 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -566 # check-ints-equal(*_test-output-stream.data, '3456') -567 # . . push args -568 68/push "F - test-get-num-reads-multiple-digits"/imm32 -569 68/push 0x36353433/imm32 -570 b8/copy-to-EAX _test-output-stream/imm32 -571 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -572 # . . call -573 e8/call check-ints-equal/disp32 -574 # . . discard args -575 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -576 # . reclaim locals -577 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -578 5d/pop-to-EBP -579 c3/return -580 -581 test-get-num-reads-multiple-digits-followed-by-nondigit: -582 # - check that get-num returns all initial digits until it encounters a non-digit -583 # This test uses exit-descriptors. Use EBP for setting up local variables. -584 55/push-EBP -585 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -586 # clear all streams -587 # . clear-stream(_test-stream) -588 # . . push args -589 68/push _test-stream/imm32 -590 # . . call -591 e8/call clear-stream/disp32 -592 # . . discard args -593 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -594 # . clear-stream(_test-buffered-file+4) -595 # . . push args -596 b8/copy-to-EAX _test-buffered-file/imm32 -597 05/add-to-EAX 4/imm32 -598 50/push-EAX -599 # . . call -600 e8/call clear-stream/disp32 -601 # . . discard args -602 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -603 # . clear-stream(_test-output-stream) -604 # . . push args -605 68/push _test-output-stream/imm32 -606 # . . call -607 e8/call clear-stream/disp32 -608 # . . discard args -609 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -610 # . clear-stream(_test-error-stream) -611 # . . push args -612 68/push _test-error-stream/imm32 -613 # . . call -614 e8/call clear-stream/disp32 -615 # . . discard args -616 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -617 # initialize 'in' -618 # . write(_test-stream, "3456 x") -619 # . . push args -620 68/push "3456 x"/imm32 -621 68/push _test-stream/imm32 -622 # . . call -623 e8/call write/disp32 -624 # . . discard args -625 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -626 # initialize exit-descriptor 'ed' for the call to 'get-num' below -627 # . var ed/EAX : (address exit-descriptor) -628 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -629 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX -630 # . tailor-exit-descriptor(ed, 16) -631 # . . push args -632 68/push 0x10/imm32/nbytes-of-args-for-get-num -633 50/push-EAX/ed -634 # . . call -635 e8/call tailor-exit-descriptor/disp32 -636 # . . discard args -637 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -638 # prime the pump -639 # . get-char(_test-buffered-file) -640 # . . push args -641 68/push _test-buffered-file/imm32 -642 # . . call -643 e8/call get-char/disp32 -644 # . . discard args -645 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -646 # get-num(in, out, err, ed) -647 # . . push args -648 50/push-EAX/ed -649 68/push _test-error-stream/imm32 -650 68/push _test-output-stream/imm32 -651 68/push _test-buffered-file/imm32 -652 # . . call -653 e8/call get-num/disp32 -654 # registers except ESP may be clobbered at this point -655 # . . discard args -656 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -657 # check-ints-equal(*_test-output-stream.data, '3456') -658 # . . push args -659 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 -660 68/push 0x36353433/imm32 -661 b8/copy-to-EAX _test-output-stream/imm32 -662 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -663 # . . call -664 e8/call check-ints-equal/disp32 -665 # . . discard args -666 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -667 # . reclaim locals -668 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -669 5d/pop-to-EBP -670 c3/return -671 -672 ## helpers -673 -674 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) -675 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> -676 # . prolog -677 55/push-EBP -678 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -679 # write(f, "Error: ") -680 # . . push args -681 68/push "Error: "/imm32 -682 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -683 # . . call -684 e8/call write/disp32 -685 # . . discard args -686 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -687 # write(f, s) -688 # . . push args -689 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -690 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -691 # . . call -692 e8/call write/disp32 -693 # . . discard args -694 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -695 # write(f, " expected") -696 # . . push args -697 68/push " expected"/imm32 -698 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -699 # . . call -700 e8/call write/disp32 -701 # . . discard args -702 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -703 # write(f, Newline) -704 # . . push args -705 68/push Newline/imm32 -706 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -707 # . . call -708 e8/call write/disp32 -709 # . . discard args -710 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -711 # stop(ed, 1) -712 # . . push args -713 68/push 1/imm32 -714 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) -715 # . . call -716 e8/call stop/disp32 -717 # should never get past this point -718 # . epilog -719 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -720 5d/pop-to-EBP -721 c3/return -722 -723 # write(f, "Error: "+s+"\n") then stop(ed, 1) -724 error: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> -725 # . prolog -726 55/push-EBP -727 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -728 # write(f, "Error: ") -729 # . . push args -730 68/push "Error: "/imm32 -731 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -732 # . . call -733 e8/call write/disp32 -734 # . . discard args -735 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -736 # write(f, s) -737 # . . push args -738 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) -739 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -740 # . . call -741 e8/call write/disp32 -742 # . . discard args -743 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -744 # write(f, Newline) -745 # . . push args -746 68/push Newline/imm32 -747 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) -748 # . . call -749 e8/call write/disp32 -750 # . . discard args -751 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -752 # stop(ed, 1) -753 # . . push args -754 68/push 1/imm32 -755 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) -756 # . . call -757 e8/call stop/disp32 -758 # should never get past this point -759 # . epilog -760 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -761 5d/pop-to-EBP -762 c3/return -763 -764 # read a byte from 'f', and save it in 'Look' -765 get-char: # f : (address buffered-file) -> <void> -766 # . prolog -767 55/push-EBP -768 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -769 # . save registers -770 50/push-EAX -771 # read-byte(f) -772 # . . push args -773 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) -774 # . . call -775 e8/call read-byte/disp32 -776 # . . discard args -777 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -778 # save EAX to Look -779 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look -780 # . restore registers -781 58/pop-to-EAX -782 # . epilog -783 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -784 5d/pop-to-EBP -785 c3/return -786 -787 is-digit?: # c : int -> bool/EAX -788 # . prolog -789 55/push-EBP -790 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -791 # EAX = false -792 b8/copy-to-EAX 0/imm32 -793 # if c < '0' return false -794 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) -795 7c/jump-if-lesser $is-digit?:end/disp8 -796 # if c > '9' return false -797 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) -798 7f/jump-if-greater $is-digit?:end/disp8 -799 # otherwise return true -800 b8/copy-to-EAX 1/imm32 -801 $is-digit?:end: -802 # . epilog -803 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -804 5d/pop-to-EBP -805 c3/return -806 -807 == data -808 -809 Look: # (char) -810 00 00 00 00 # = 0 + 33 #? # for debugging: run a single test; don't bother setting status code + 34 #? e8/call test-get-num-reads-single-digit/disp32 + 35 #? eb/jump $main:end/disp8 + 36 + 37 # main: run tests if necessary, call 'compile' if not + 38 # . prolog + 39 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 40 # - if argc > 1 and argv[1] == "test" then return run_tests() + 41 # . argc > 1 + 42 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP + 43 7e/jump-if-lesser-or-equal $run-main/disp8 + 44 # . argv[1] == "test" + 45 # . . push args + 46 68/push "test"/imm32 + 47 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + 48 # . . call + 49 e8/call kernel-string-equal/disp32 + 50 # . . discard args + 51 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 52 # . check result + 53 3d/compare-EAX 1/imm32 + 54 75/jump-if-not-equal $run-main/disp8 + 55 # . run-tests() + 56 e8/call run-tests/disp32 + 57 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 58 eb/jump $main:end/disp8 + 59 $run-main: + 60 # - otherwise read a program from stdin and emit its translation to stdout + 61 # var ed/EAX : exit-descriptor + 62 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 63 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + 64 # configure ed to really exit() + 65 # . ed->target = 0 + 66 c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + 67 # return compile(Stdin, 1/stdout, 2/stderr, ed) + 68 # . . push args + 69 50/push-EAX/ed + 70 68/push 2/imm32/stderr + 71 68/push 1/imm32/stdout + 72 68/push Stdin/imm32 + 73 # . . call + 74 e8/call compile/disp32 + 75 # . . discard args + 76 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 77 # . syscall(exit, 0) + 78 bb/copy-to-EBX 0/imm32 + 79 $main:end: + 80 b8/copy-to-EAX 1/imm32/exit + 81 cd/syscall 0x80/imm8 + 82 + 83 # the main entry point + 84 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> + 85 # . prolog + 86 55/push-EBP + 87 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 88 # . save registers + 89 50/push-EAX + 90 51/push-ECX + 91 # prime the pump + 92 # . Look = get-char(in) + 93 # . . push args + 94 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + 95 # . . call + 96 e8/call get-char/disp32 + 97 # . . discard args + 98 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 99 # var num/ECX : (address stream) on the stack +100 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. +101 # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. +102 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. +103 # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. +104 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP +105 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX +106 # initialize the stream +107 # . num->length = 7 +108 c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) +109 # . clear-stream(num) +110 # . . push args +111 51/push-ECX +112 # . . call +113 e8/call clear-stream/disp32 +114 # . . discard args +115 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +116 # read a digit from 'in' into 'num' +117 # . get-num(in, num, err, ed) +118 # . . push args +119 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) +120 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +121 51/push-ECX/num +122 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) +123 # . . call +124 e8/call get-num/disp32 +125 # . . discard args +126 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +127 # render 'num' into the following template on 'out': +128 # bb/copy-to-EBX _num_ +129 # b8/copy-to-EAX 1/imm32/exit +130 # cd/syscall 0x80/imm8 +131 # +132 # . EAX = write(out, "bb/copy-to-EBX ") +133 # . . push args +134 68/push "bb/copy-to-EBX "/imm32 +135 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +136 # . . call +137 e8/call write/disp32 +138 # . . discard args +139 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +140 # . write-stream(out, num) +141 # . . push args +142 51/push-ECX/num +143 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +144 # . . call +145 e8/call write-stream/disp32 +146 # . . discard args +147 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +148 # . write(out, Newline) +149 # . . push args +150 68/push Newline/imm32 +151 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +152 # . . call +153 e8/call write/disp32 +154 # . . discard args +155 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +156 # . write(out, "b8/copy-to-EAX 1/imm32/exit") +157 # . . push args +158 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 +159 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +160 # . . call +161 e8/call write/disp32 +162 # . . discard args +163 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +164 # . write(out, Newline) +165 # . . push args +166 68/push Newline/imm32 +167 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +168 # . . call +169 e8/call write/disp32 +170 # . . discard args +171 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +172 # . write(out, "cd/syscall 0x80/imm8") +173 # . . push args +174 68/push "cd/syscall 0x80/imm8"/imm32 +175 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +176 # . . call +177 e8/call write/disp32 +178 # . . discard args +179 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +180 # . write(out, Newline) +181 # . . push args +182 68/push Newline/imm32 +183 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +184 # . . call +185 e8/call write/disp32 +186 # . . discard args +187 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +188 # . restore registers +189 59/pop-to-ECX +190 58/pop-to-EAX +191 # . epilog +192 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +193 5d/pop-to-EBP +194 c3/return +195 +196 # Read a sequence of digits into 'out'. Abort if there are none, or if there is +197 # no space in 'out'. +198 # Input comes from the global variable 'Look' (first byte) and the argument +199 # 'in' (rest). We leave the next byte from 'in' into 'Look' on exit. +200 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> +201 # pseudocode: +202 # if !is-digit?(Look) expected(ed, err, "integer") +203 # do +204 # if out.write >= out.length +205 # write(err, "Error: too many digits in number\n") +206 # stop(ed, 1) +207 # out.data[out.write] = LSB(Look) +208 # ++out.write +209 # Look = get-char(in) +210 # while is-digit?(Look) +211 # This is complicated because I don't want to hard-code the error strategy in +212 # a general helper like write-byte. Maybe I should just create a local helper. +213 # +214 # within the loop we'll try to keep things in registers: +215 # ESI : in +216 # EDI : out +217 # EAX : temp +218 # ECX : out->write +219 # EDX : out->length +220 # EBX : temp2 +221 # We can't allocate Look to a register because it gets written implicitly in +222 # get-char in each iteration of the loop. (Thereby demonstrating that it's +223 # not the right interface for us. But we'll keep it just to follow Crenshaw.) +224 # +225 # . prolog +226 55/push-EBP +227 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +228 # - if is-digit?(Look) expected(ed, err, "integer") +229 # . EAX = is-digit?(Look) +230 # . . push args +231 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +232 # . . call +233 e8/call is-digit?/disp32 +234 # . . discard args +235 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +236 # . if EAX == 0 +237 3d/compare-EAX 0/imm32 +238 75/jump-if-not-equal $get-num:main/disp8 +239 # . expected(ed, err, "integer") +240 # . . push args +241 68/push "integer"/imm32 +242 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +243 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) +244 # . . call +245 e8/call expected/disp32 # never returns +246 # . . discard args +247 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +248 $get-num:main: +249 # - otherwise read a digit +250 # . save registers +251 50/push-EAX +252 51/push-ECX +253 52/push-EDX +254 53/push-EBX +255 56/push-ESI +256 57/push-EDI +257 # read necessary variables to registers +258 # ESI = in +259 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +260 # EDI = out +261 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI +262 # ECX = out->write +263 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX +264 # EDX = out->length +265 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX +266 $get-num:loop: +267 # if out->write >= out->length error +268 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX +269 7d/jump-if-lesser $get-num:loop-stage2/disp8 +270 # . error(ed, err, msg) # TODO: show full number +271 # . . push args +272 68/push "get-num: too many digits in number"/imm32 +273 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +274 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) +275 # . . call +276 e8/call error/disp32 # never returns +277 # . . discard args +278 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +279 $get-num:loop-stage2: +280 # out->data[out->write] = LSB(Look) +281 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX +282 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX +283 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX +284 # ++out->write +285 41/increment-ECX +286 # Look = get-char(in) +287 # . . push args +288 56/push-ESI +289 # . . call +290 e8/call get-char/disp32 +291 # . . discard args +292 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +293 # if is-digit?(Look) loop +294 # . EAX = is-digit?(Look) +295 # . . push args +296 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +297 # . . call +298 e8/call is-digit?/disp32 +299 # . . discard args +300 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +301 # . if EAX loop +302 3d/compare-EAX 0/imm32 +303 0f 85/jump-if-not-equal $get-num:loop/disp32 +304 $get-num:loop-end: +305 # persist necessary variables from registers +306 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI +307 # . restore registers +308 5f/pop-to-EDI +309 5e/pop-to-ESI +310 5b/pop-to-EBX +311 5a/pop-to-EDX +312 59/pop-to-ECX +313 58/pop-to-EAX +314 # . epilog +315 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +316 5d/pop-to-EBP +317 c3/return +318 +319 test-get-num-reads-single-digit: +320 # - check that get-num returns first character if it's a digit +321 # This test uses exit-descriptors. Use EBP for setting up local variables. +322 55/push-EBP +323 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +324 # clear all streams +325 # . clear-stream(_test-stream) +326 # . . push args +327 68/push _test-stream/imm32 +328 # . . call +329 e8/call clear-stream/disp32 +330 # . . discard args +331 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +332 # . clear-stream(_test-buffered-file+4) +333 # . . push args +334 b8/copy-to-EAX _test-buffered-file/imm32 +335 05/add-to-EAX 4/imm32 +336 50/push-EAX +337 # . . call +338 e8/call clear-stream/disp32 +339 # . . discard args +340 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +341 # . clear-stream(_test-output-stream) +342 # . . push args +343 68/push _test-output-stream/imm32 +344 # . . call +345 e8/call clear-stream/disp32 +346 # . . discard args +347 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +348 # . clear-stream(_test-error-stream) +349 # . . push args +350 68/push _test-error-stream/imm32 +351 # . . call +352 e8/call clear-stream/disp32 +353 # . . discard args +354 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +355 # initialize 'in' +356 # . write(_test-stream, "3") +357 # . . push args +358 68/push "3"/imm32 +359 68/push _test-stream/imm32 +360 # . . call +361 e8/call write/disp32 +362 # . . discard args +363 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +364 # initialize exit-descriptor 'ed' for the call to 'get-num' below +365 # . var ed/EAX : exit-descriptor +366 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +367 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX +368 # . tailor-exit-descriptor(ed, 16) +369 # . . push args +370 68/push 0x10/imm32/nbytes-of-args-for-get-num +371 50/push-EAX/ed +372 # . . call +373 e8/call tailor-exit-descriptor/disp32 +374 # . . discard args +375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +376 # prime the pump +377 # . get-char(_test-buffered-file) +378 # . . push args +379 68/push _test-buffered-file/imm32 +380 # . . call +381 e8/call get-char/disp32 +382 # . . discard args +383 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +384 # get-num(in, out, err, ed) +385 # . . push args +386 50/push-EAX/ed +387 68/push _test-error-stream/imm32 +388 68/push _test-output-stream/imm32 +389 68/push _test-buffered-file/imm32 +390 # . . call +391 e8/call get-num/disp32 +392 # registers except ESP may be clobbered at this point +393 # . . discard args +394 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +395 # check-ints-equal(*_test-output-stream.data, '3') +396 # . . push args +397 68/push "F - test-get-num-reads-single-digit"/imm32 +398 68/push 0x33/imm32 +399 b8/copy-to-EAX _test-output-stream/imm32 +400 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +401 # . . call +402 e8/call check-ints-equal/disp32 +403 # . . discard args +404 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +405 # . reclaim locals +406 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +407 5d/pop-to-EBP +408 c3/return +409 +410 test-get-num-aborts-on-non-digit-in-Look: +411 # - check that get-num returns first character if it's a digit +412 # This test uses exit-descriptors. Use EBP for setting up local variables. +413 55/push-EBP +414 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +415 # clear all streams +416 # . clear-stream(_test-stream) +417 # . . push args +418 68/push _test-stream/imm32 +419 # . . call +420 e8/call clear-stream/disp32 +421 # . . discard args +422 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +423 # . clear-stream(_test-buffered-file+4) +424 # . . push args +425 b8/copy-to-EAX _test-buffered-file/imm32 +426 05/add-to-EAX 4/imm32 +427 50/push-EAX +428 # . . call +429 e8/call clear-stream/disp32 +430 # . . discard args +431 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +432 # . clear-stream(_test-output-stream) +433 # . . push args +434 68/push _test-output-stream/imm32 +435 # . . call +436 e8/call clear-stream/disp32 +437 # . . discard args +438 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +439 # . clear-stream(_test-error-stream) +440 # . . push args +441 68/push _test-error-stream/imm32 +442 # . . call +443 e8/call clear-stream/disp32 +444 # . . discard args +445 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +446 # initialize 'in' +447 # . write(_test-stream, "3") +448 # . . push args +449 68/push "3"/imm32 +450 68/push _test-stream/imm32 +451 # . . call +452 e8/call write/disp32 +453 # . . discard args +454 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +455 # initialize exit-descriptor 'ed' for the call to 'get-num' below +456 # . var ed/EAX : (address exit-descriptor) +457 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +458 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX +459 # . tailor-exit-descriptor(ed, 16) +460 # . . push args +461 68/push 0x10/imm32/nbytes-of-args-for-get-num +462 50/push-EAX/ed +463 # . . call +464 e8/call tailor-exit-descriptor/disp32 +465 # . . discard args +466 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +467 # *don't* prime the pump +468 # get-num(in, out, err, ed) +469 # . . push args +470 50/push-EAX/ed +471 68/push _test-error-stream/imm32 +472 68/push _test-output-stream/imm32 +473 68/push _test-buffered-file/imm32 +474 # . . call +475 e8/call get-num/disp32 +476 # registers except ESP may be clobbered at this point +477 # . . discard args +478 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +479 # check that get-num tried to call exit(1) +480 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 +481 # . . push args +482 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 +483 68/push 2/imm32 +484 # . . push ed->value +485 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +486 # . . call +487 e8/call check-ints-equal/disp32 +488 # . . discard args +489 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +490 # . reclaim locals +491 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +492 5d/pop-to-EBP +493 c3/return +494 +495 test-get-num-reads-multiple-digits: +496 # - check that get-num returns all initial digits until it encounters a non-digit +497 # This test uses exit-descriptors. Use EBP for setting up local variables. +498 55/push-EBP +499 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +500 # clear all streams +501 # . clear-stream(_test-stream) +502 # . . push args +503 68/push _test-stream/imm32 +504 # . . call +505 e8/call clear-stream/disp32 +506 # . . discard args +507 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +508 # . clear-stream(_test-buffered-file+4) +509 # . . push args +510 b8/copy-to-EAX _test-buffered-file/imm32 +511 05/add-to-EAX 4/imm32 +512 50/push-EAX +513 # . . call +514 e8/call clear-stream/disp32 +515 # . . discard args +516 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +517 # . clear-stream(_test-output-stream) +518 # . . push args +519 68/push _test-output-stream/imm32 +520 # . . call +521 e8/call clear-stream/disp32 +522 # . . discard args +523 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +524 # . clear-stream(_test-error-stream) +525 # . . push args +526 68/push _test-error-stream/imm32 +527 # . . call +528 e8/call clear-stream/disp32 +529 # . . discard args +530 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +531 # initialize 'in' +532 # . write(_test-stream, "3456 x") +533 # . . push args +534 68/push "3456"/imm32 +535 68/push _test-stream/imm32 +536 # . . call +537 e8/call write/disp32 +538 # . . discard args +539 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +540 # initialize exit-descriptor 'ed' for the call to 'get-num' below +541 # . var ed/EAX : (address exit-descriptor) +542 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +543 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX +544 # . tailor-exit-descriptor(ed, 16) +545 # . . push args +546 68/push 0x10/imm32/nbytes-of-args-for-get-num +547 50/push-EAX/ed +548 # . . call +549 e8/call tailor-exit-descriptor/disp32 +550 # . . discard args +551 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +552 # prime the pump +553 # . get-char(_test-buffered-file) +554 # . . push args +555 68/push _test-buffered-file/imm32 +556 # . . call +557 e8/call get-char/disp32 +558 # . . discard args +559 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +560 # get-num(in, out, err, ed) +561 # . . push args +562 50/push-EAX/ed +563 68/push _test-error-stream/imm32 +564 68/push _test-output-stream/imm32 +565 68/push _test-buffered-file/imm32 +566 # . . call +567 e8/call get-num/disp32 +568 # registers except ESP may be clobbered at this point +569 # . . discard args +570 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +571 # check-ints-equal(*_test-output-stream.data, '3456') +572 # . . push args +573 68/push "F - test-get-num-reads-multiple-digits"/imm32 +574 68/push 0x36353433/imm32 +575 b8/copy-to-EAX _test-output-stream/imm32 +576 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +577 # . . call +578 e8/call check-ints-equal/disp32 +579 # . . discard args +580 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +581 # . reclaim locals +582 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +583 5d/pop-to-EBP +584 c3/return +585 +586 test-get-num-reads-multiple-digits-followed-by-nondigit: +587 # - check that get-num returns all initial digits until it encounters a non-digit +588 # This test uses exit-descriptors. Use EBP for setting up local variables. +589 55/push-EBP +590 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +591 # clear all streams +592 # . clear-stream(_test-stream) +593 # . . push args +594 68/push _test-stream/imm32 +595 # . . call +596 e8/call clear-stream/disp32 +597 # . . discard args +598 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +599 # . clear-stream(_test-buffered-file+4) +600 # . . push args +601 b8/copy-to-EAX _test-buffered-file/imm32 +602 05/add-to-EAX 4/imm32 +603 50/push-EAX +604 # . . call +605 e8/call clear-stream/disp32 +606 # . . discard args +607 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +608 # . clear-stream(_test-output-stream) +609 # . . push args +610 68/push _test-output-stream/imm32 +611 # . . call +612 e8/call clear-stream/disp32 +613 # . . discard args +614 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +615 # . clear-stream(_test-error-stream) +616 # . . push args +617 68/push _test-error-stream/imm32 +618 # . . call +619 e8/call clear-stream/disp32 +620 # . . discard args +621 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +622 # initialize 'in' +623 # . write(_test-stream, "3456 x") +624 # . . push args +625 68/push "3456 x"/imm32 +626 68/push _test-stream/imm32 +627 # . . call +628 e8/call write/disp32 +629 # . . discard args +630 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +631 # initialize exit-descriptor 'ed' for the call to 'get-num' below +632 # . var ed/EAX : (address exit-descriptor) +633 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +634 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX +635 # . tailor-exit-descriptor(ed, 16) +636 # . . push args +637 68/push 0x10/imm32/nbytes-of-args-for-get-num +638 50/push-EAX/ed +639 # . . call +640 e8/call tailor-exit-descriptor/disp32 +641 # . . discard args +642 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +643 # prime the pump +644 # . get-char(_test-buffered-file) +645 # . . push args +646 68/push _test-buffered-file/imm32 +647 # . . call +648 e8/call get-char/disp32 +649 # . . discard args +650 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +651 # get-num(in, out, err, ed) +652 # . . push args +653 50/push-EAX/ed +654 68/push _test-error-stream/imm32 +655 68/push _test-output-stream/imm32 +656 68/push _test-buffered-file/imm32 +657 # . . call +658 e8/call get-num/disp32 +659 # registers except ESP may be clobbered at this point +660 # . . discard args +661 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +662 # check-ints-equal(*_test-output-stream.data, '3456') +663 # . . push args +664 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 +665 68/push 0x36353433/imm32 +666 b8/copy-to-EAX _test-output-stream/imm32 +667 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +668 # . . call +669 e8/call check-ints-equal/disp32 +670 # . . discard args +671 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +672 # . reclaim locals +673 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +674 5d/pop-to-EBP +675 c3/return +676 +677 ## helpers +678 +679 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) +680 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> +681 # . prolog +682 55/push-EBP +683 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +684 # write(f, "Error: ") +685 # . . push args +686 68/push "Error: "/imm32 +687 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +688 # . . call +689 e8/call write/disp32 +690 # . . discard args +691 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +692 # write(f, s) +693 # . . push args +694 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +695 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +696 # . . call +697 e8/call write/disp32 +698 # . . discard args +699 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +700 # write(f, " expected") +701 # . . push args +702 68/push " expected"/imm32 +703 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +704 # . . call +705 e8/call write/disp32 +706 # . . discard args +707 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +708 # write(f, Newline) +709 # . . push args +710 68/push Newline/imm32 +711 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +712 # . . call +713 e8/call write/disp32 +714 # . . discard args +715 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +716 # stop(ed, 1) +717 # . . push args +718 68/push 1/imm32 +719 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) +720 # . . call +721 e8/call stop/disp32 +722 # should never get past this point +723 # . epilog +724 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +725 5d/pop-to-EBP +726 c3/return +727 +728 # write(f, "Error: "+s+"\n") then stop(ed, 1) +729 error: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> +730 # . prolog +731 55/push-EBP +732 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +733 # write(f, "Error: ") +734 # . . push args +735 68/push "Error: "/imm32 +736 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +737 # . . call +738 e8/call write/disp32 +739 # . . discard args +740 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +741 # write(f, s) +742 # . . push args +743 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) +744 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +745 # . . call +746 e8/call write/disp32 +747 # . . discard args +748 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +749 # write(f, Newline) +750 # . . push args +751 68/push Newline/imm32 +752 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) +753 # . . call +754 e8/call write/disp32 +755 # . . discard args +756 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +757 # stop(ed, 1) +758 # . . push args +759 68/push 1/imm32 +760 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) +761 # . . call +762 e8/call stop/disp32 +763 # should never get past this point +764 # . epilog +765 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +766 5d/pop-to-EBP +767 c3/return +768 +769 # read a byte from 'f', and save it in 'Look' +770 get-char: # f : (address buffered-file) -> <void> +771 # . prolog +772 55/push-EBP +773 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +774 # . save registers +775 50/push-EAX +776 # read-byte(f) +777 # . . push args +778 ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) +779 # . . call +780 e8/call read-byte/disp32 +781 # . . discard args +782 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +783 # save EAX to Look +784 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look +785 # . restore registers +786 58/pop-to-EAX +787 # . epilog +788 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +789 5d/pop-to-EBP +790 c3/return +791 +792 is-digit?: # c : int -> bool/EAX +793 # . prolog +794 55/push-EBP +795 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +796 # EAX = false +797 b8/copy-to-EAX 0/imm32 +798 # if c < '0' return false +799 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) +800 7c/jump-if-lesser $is-digit?:end/disp8 +801 # if c > '9' return false +802 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) +803 7f/jump-if-greater $is-digit?:end/disp8 +804 # otherwise return true +805 b8/copy-to-EAX 1/imm32 +806 $is-digit?:end: +807 # . epilog +808 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +809 5d/pop-to-EBP +810 c3/return 811 -812 _test-output-stream: -813 # current write index -814 00 00 00 00 -815 # current read index -816 00 00 00 00 -817 # length (= 8) -818 08 00 00 00 -819 # data -820 00 00 00 00 00 00 00 00 # 8 bytes -821 -822 _test-error-stream: -823 # current write index -824 00 00 00 00 -825 # current read index -826 00 00 00 00 -827 # length (= 8) -828 08 00 00 00 -829 # data -830 00 00 00 00 00 00 00 00 # 8 bytes -831 -832 # . . vim:nowrap:textwidth=0 +812 == data +813 +814 Look: # (char) +815 00 00 00 00 # = 0 +816 +817 _test-output-stream: +818 # current write index +819 00 00 00 00 +820 # current read index +821 00 00 00 00 +822 # length (= 8) +823 08 00 00 00 +824 # data +825 00 00 00 00 00 00 00 00 # 8 bytes +826 +827 _test-error-stream: +828 # current write index +829 00 00 00 00 +830 # current read index +831 00 00 00 00 +832 # length (= 8) +833 08 00 00 00 +834 # data +835 00 00 00 00 00 00 00 00 # 8 bytes +836 +837 # . . vim:nowrap:textwidth=0 diff --git a/subx/apps/crenshaw2-1.subx b/subx/apps/crenshaw2-1.subx index b202f25f..c523634a 100644 --- a/subx/apps/crenshaw2-1.subx +++ b/subx/apps/crenshaw2-1.subx @@ -30,6 +30,10 @@ # . op subop mod rm32 base index scale r32 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes +#? # for debugging: run a single test; don't bother setting status code +#? e8/call test-get-num-reads-single-digit/disp32 +#? eb/jump $main:end/disp8 + # main: run tests if necessary, call 'compile' if not # . prolog 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -50,7 +54,6 @@ 75/jump-if-not-equal $run-main/disp8 # . run-tests() e8/call run-tests/disp32 -#? e8/call test-get-num-reads-multiple-digits/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: @@ -94,13 +97,14 @@ compile: # in : (address buffered-file), out : fd or (address stream), err : fd 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP # var num/ECX : (address stream) on the stack # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - # We won't add more, so that we can get overflow-handling for free. + # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. + # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX - # num->length = 7 + # initialize the stream + # . num->length = 7 c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) - # read a digit from 'in' into 'num' # . clear-stream(num) # . . push args 51/push-ECX @@ -108,6 +112,7 @@ compile: # in : (address buffered-file), out : fd or (address stream), err : fd e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # read a digit from 'in' into 'num' # . get-num(in, num, err, ed) # . . push args ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) diff --git a/subx/apps/crenshaw2-1b.subx b/subx/apps/crenshaw2-1b.subx index a3c56ed5..1baf90c3 100644 --- a/subx/apps/crenshaw2-1b.subx +++ b/subx/apps/crenshaw2-1b.subx @@ -30,6 +30,10 @@ # . op subop mod rm32 base index scale r32 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes +#? # for debugging: run a single test; don't bother setting status code +#? e8/call test-get-num-reads-single-digit/disp32 +#? eb/jump $main:end/disp8 + # main: run tests if necessary, call 'compile' if not # . prolog 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -50,7 +54,6 @@ 75/jump-if-not-equal $run-main/disp8 # . run-tests() e8/call run-tests/disp32 -#? e8/call test-get-num-reads-multiple-digits/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: @@ -95,13 +98,14 @@ compile: # in : (address buffered-file), out : fd or (address stream), err : fd 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP # var num/ECX : (address stream) on the stack # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - # We won't add more, so that we can get overflow-handling for free. + # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. + # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX - # num->length = 7 + # initialize the stream + # . num->length = 7 c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) - # read a digit from 'in' into 'num' # . clear-stream(num) # . . push args 51/push-ECX @@ -109,6 +113,7 @@ compile: # in : (address buffered-file), out : fd or (address stream), err : fd e8/call clear-stream/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # read a digit from 'in' into 'num' # . get-num(in, num, err, ed) # . . push args ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20)