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