From ce2c1efc41470764126e9a1a7f4e0cfec4213587 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 14 Jul 2019 09:42:36 -0700 Subject: . --- html/subx/apps/crenshaw2-1.subx.html | 1116 +++++++++++++++++----------------- 1 file changed, 560 insertions(+), 556 deletions(-) (limited to 'html/subx/apps/crenshaw2-1.subx.html') diff --git a/html/subx/apps/crenshaw2-1.subx.html b/html/subx/apps/crenshaw2-1.subx.html index bbae9106..c7f3c335 100644 --- a/html/subx/apps/crenshaw2-1.subx.html +++ b/html/subx/apps/crenshaw2-1.subx.html @@ -3,8 +3,8 @@ Mu - subx/apps/crenshaw2-1.subx - - + + @@ -14,18 +14,17 @@ pre { font-family: monospace; color: #000000; background-color: #c6c6c6; } body { font-size:12pt; font-family: monospace; color: #000000; background-color: #c6c6c6; } a { color:inherit; } * { font-size:12pt; font-size: 1em; } +.subxH1Comment { color: #005faf; text-decoration: underline; } .subxComment { color: #005faf; } -.subxS2Comment { color: #8a8a8a; } -.LineNr { } .subxS1Comment { color: #0000af; } +.LineNr { } .SpecialChar { color: #d70000; } -.Constant { color: #008787; } -.subxFunction { color: #af5f00; text-decoration: underline; } -.subxMinorFunction { color: #875f5f; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxTest { color: #5f8700; } -.CommentedCode { color: #8a8a8a; } -.subxH1Comment { color: #005faf; text-decoration: underline; } +.subxFunction { color: #af5f00; text-decoration: underline; } +.Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } +.subxS2Comment { color: #8a8a8a; } --> @@ -42,7 +41,7 @@ function JumpToLine() if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } - lineElem = document.getElementById(lineNum); + var lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ @@ -94,553 +93,558 @@ 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 - 35 #? # for debugging: run a single test; don't bother setting status code - 36 #? e8/call test-get-num-aborts-on-non-digit-in-Look/disp32 - 37 #? eb/jump $main:end/disp8 - 38 - 39 # . prolog - 40 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 41 # - if argc > 1 and argv[1] == "test", then return run_tests() - 42 # . argc > 1 - 43 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 44 7e/jump-if-lesser-or-equal $run-main/disp8 - 45 # . argv[1] == "test" - 46 # . . push args - 47 68/push "test"/imm32 - 48 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 49 # . . call - 50 e8/call kernel-string-equal?/disp32 - 51 # . . discard args - 52 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 53 # . check result - 54 3d/compare-EAX-and 1/imm32 - 55 75/jump-if-not-equal $run-main/disp8 - 56 # . run-tests() - 57 e8/call run-tests/disp32 - 58 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 59 eb/jump $main:end/disp8 - 60 $run-main: - 61 # - otherwise read a program from stdin and emit its translation to stdout - 62 # var ed/EAX : exit-descriptor - 63 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 64 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - 65 # configure ed to really exit() - 66 # . ed->target = 0 - 67 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 68 # return compile(Stdin, 1/stdout, 2/stderr, ed) - 69 # . . push args - 70 50/push-EAX/ed - 71 68/push 2/imm32/stderr - 72 68/push 1/imm32/stdout - 73 68/push Stdin/imm32 - 74 # . . call - 75 e8/call compile/disp32 - 76 # . . discard args - 77 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 78 # . syscall(exit, 0) - 79 bb/copy-to-EBX 0/imm32 - 80 $main:end: - 81 b8/copy-to-EAX 1/imm32/exit - 82 cd/syscall 0x80/imm8 - 83 - 84 # the main entry point - 85 compile: # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> - 86 # . prolog - 87 55/push-EBP - 88 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 89 # . save registers - 90 50/push-EAX - 91 51/push-ECX - 92 # prime the pump - 93 # . Look = get-char(in) - 94 # . . push args - 95 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 96 # . . call - 97 e8/call get-char/disp32 - 98 # . . discard args - 99 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -100 # var num/ECX : (address stream) on the stack -101 # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. -102 # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. -103 # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. -104 # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. -105 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP -106 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -107 # initialize the stream -108 # . num->length = 7 -109 c7 0/subop/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) -110 # . clear-stream(num) -111 # . . push args -112 51/push-ECX -113 # . . call -114 e8/call clear-stream/disp32 -115 # . . discard args -116 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -117 # read a digit from 'in' into 'num' -118 # . get-num(in, num, err, ed) -119 # . . push args -120 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -121 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -122 51/push-ECX/num -123 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -124 # . . call -125 e8/call get-num/disp32 -126 # . . discard args -127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -128 # render 'num' into the following template on 'out': -129 # bb/copy-to-EBX _num_ -130 # b8/copy-to-EAX 1/imm32/exit -131 # cd/syscall 0x80/imm8 -132 # -133 # . write(out, "bb/copy-to-EBX ") -134 # . . push args -135 68/push "bb/copy-to-EBX "/imm32 -136 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -137 # . . call -138 e8/call write/disp32 -139 # . . discard args -140 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -141 # . write-stream(out, num) -142 # . . push args -143 51/push-ECX/num -144 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -145 # . . call -146 e8/call write-stream/disp32 -147 # . . discard args -148 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -149 # . write(out, Newline) -150 # . . push args -151 68/push Newline/imm32 -152 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -153 # . . call -154 e8/call write/disp32 -155 # . . discard args -156 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -157 # . write(out, "b8/copy-to-EAX 1/imm32/exit\n") -158 # . . push args -159 68/push "b8/copy-to-EAX 1/imm32/exit\n"/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, "cd/syscall 0x80/imm8\n") -166 # . . push args -167 68/push "cd/syscall 0x80/imm8\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 $compile:end: -174 # . restore registers -175 59/pop-to-ECX -176 58/pop-to-EAX -177 # . epilog -178 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -179 5d/pop-to-EBP -180 c3/return -181 -182 # Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'. -183 # Input comes from the global variable 'Look', and we leave the next byte from -184 # 'in' into it on exit. -185 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> -186 # pseudocode: -187 # if (!is-digit?(Look)) expected(ed, err, "integer") -188 # if out->write >= out->length -189 # write(err, "Error: too many digits in number\n") -190 # stop(ed, 1) -191 # out->data[out->write] = LSB(Look) -192 # ++out->write -193 # Look = get-char(in) -194 # -195 # registers: -196 # in: ESI -197 # out: EDI -198 # out->write: ECX (cached copy; need to keep in sync) -199 # out->length: EDX -200 # temporaries: EAX, EBX -201 # We can't allocate Look to a register because it gets written implicitly in -202 # get-char in each iteration of the loop. (Thereby demonstrating that it's -203 # not the right interface for us. But we'll keep it just to follow Crenshaw.) -204 # -205 # . prolog -206 55/push-EBP -207 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -208 # - if (is-digit?(Look)) expected(ed, err, "integer") -209 # . EAX = is-digit?(Look) -210 # . . push args -211 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look -212 # . . call -213 e8/call is-digit?/disp32 -214 # . . discard args -215 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -216 # . if (EAX == 0) -217 3d/compare-EAX-and 0/imm32 -218 75/jump-if-not-equal $get-num:main/disp8 -219 # . expected(ed, err, "integer") -220 # . . push args -221 68/push "integer"/imm32 -222 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -223 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -224 # . . call -225 e8/call expected/disp32 # never returns -226 # . . discard args -227 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -228 $get-num:main: -229 # - otherwise read a digit -230 # . save registers -231 50/push-EAX -232 51/push-ECX -233 52/push-EDX -234 53/push-EBX -235 56/push-ESI -236 57/push-EDI -237 # read necessary variables to registers -238 # ESI = in -239 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -240 # EDI = out -241 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -242 # ECX = out->write -243 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX -244 # EDX = out->length -245 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX -246 # if (out->write >= out->length) error -247 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX -248 7d/jump-if-lesser $get-num:stage2/disp8 -249 # . error(ed, err, msg) # TODO: show full number -250 # . . push args -251 68/push "get-num: too many digits in number"/imm32 -252 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -253 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) -254 # . . call -255 e8/call error/disp32 # never returns -256 # . . discard args -257 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -258 $get-num:stage2: -259 # out->data[out->write] = LSB(Look) -260 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 -261 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX -262 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX -263 # ++out->write -264 41/increment-ECX -265 # Look = get-char(in) -266 # . . push args -267 56/push-ESI -268 # . . call -269 e8/call get-char/disp32 -270 # . . discard args -271 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -272 $get-num:loop-end: -273 # persist necessary variables from registers -274 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI -275 $get-num:end: -276 # . restore registers -277 5f/pop-to-EDI -278 5e/pop-to-ESI -279 5b/pop-to-EBX -280 5a/pop-to-EDX -281 59/pop-to-ECX -282 58/pop-to-EAX -283 # . epilog -284 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -285 5d/pop-to-EBP -286 c3/return -287 -288 test-get-num-reads-single-digit: -289 # - check that get-num returns first character if it's a digit -290 # This test uses exit-descriptors. Use EBP for setting up local variables. -291 55/push-EBP -292 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -293 # clear all streams -294 # . clear-stream(_test-stream) -295 # . . push args -296 68/push _test-stream/imm32 -297 # . . call -298 e8/call clear-stream/disp32 -299 # . . discard args -300 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -301 # . clear-stream(_test-buffered-file+4) -302 # . . push args -303 b8/copy-to-EAX _test-buffered-file/imm32 -304 05/add-to-EAX 4/imm32 -305 50/push-EAX -306 # . . call -307 e8/call clear-stream/disp32 -308 # . . discard args -309 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -310 # . clear-stream(_test-output-stream) -311 # . . push args -312 68/push _test-output-stream/imm32 -313 # . . call -314 e8/call clear-stream/disp32 -315 # . . discard args -316 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -317 # . clear-stream(_test-error-stream) -318 # . . push args -319 68/push _test-error-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 # initialize 'in' -325 # . write(_test-stream, "3") -326 # . . push args -327 68/push "3"/imm32 -328 68/push _test-stream/imm32 -329 # . . call -330 e8/call write/disp32 -331 # . . discard args -332 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -333 # initialize exit-descriptor 'ed' for the call to 'get-num' below -334 # . var ed/EAX : exit-descriptor -335 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -336 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -337 # . tailor-exit-descriptor(ed, 16) -338 # . . push args -339 68/push 0x10/imm32/nbytes-of-args-for-get-num -340 50/push-EAX/ed -341 # . . call -342 e8/call tailor-exit-descriptor/disp32 -343 # . . discard args -344 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -345 # prime the pump -346 # . get-char(_test-buffered-file) -347 # . . push args -348 68/push _test-buffered-file/imm32 -349 # . . call -350 e8/call get-char/disp32 -351 # . . discard args -352 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -353 # get-num(in, out, err, ed) -354 # . . push args -355 50/push-EAX/ed -356 68/push _test-error-stream/imm32 -357 68/push _test-output-stream/imm32 -358 68/push _test-buffered-file/imm32 -359 # . . call -360 e8/call get-num/disp32 -361 # registers except ESP may be clobbered at this point -362 # . . discard args -363 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -364 # check-ints-equal(*_test-output-stream->data, '3', msg) -365 # . . push args -366 68/push "F - test-get-num-reads-single-digit"/imm32 -367 68/push 0x33/imm32 -368 b8/copy-to-EAX _test-output-stream/imm32 -369 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -370 # . . call -371 e8/call check-ints-equal/disp32 -372 # . . discard args -373 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -374 # . reclaim locals -375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -376 5d/pop-to-EBP -377 c3/return -378 -379 test-get-num-aborts-on-non-digit-in-Look: -380 # - check that get-num returns first character if it's a digit -381 # This test uses exit-descriptors. Use EBP for setting up local variables. -382 55/push-EBP -383 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -384 # clear all streams -385 # . clear-stream(_test-stream) -386 # . . push args -387 68/push _test-stream/imm32 -388 # . . call -389 e8/call clear-stream/disp32 -390 # . . discard args -391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -392 # . clear-stream(_test-buffered-file+4) -393 # . . push args -394 b8/copy-to-EAX _test-buffered-file/imm32 -395 05/add-to-EAX 4/imm32 -396 50/push-EAX -397 # . . call -398 e8/call clear-stream/disp32 -399 # . . discard args -400 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -401 # . clear-stream(_test-output-stream) -402 # . . push args -403 68/push _test-output-stream/imm32 -404 # . . call -405 e8/call clear-stream/disp32 -406 # . . discard args -407 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -408 # . clear-stream(_test-error-stream) -409 # . . push args -410 68/push _test-error-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 # initialize 'in' -416 # . write(_test-stream, "3") -417 # . . push args -418 68/push "3"/imm32 -419 68/push _test-stream/imm32 -420 # . . call -421 e8/call write/disp32 -422 # . . discard args -423 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -424 # initialize exit-descriptor 'ed' for the call to 'get-num' below -425 # . var ed/EAX : (address exit-descriptor) -426 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP -427 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX -428 # . tailor-exit-descriptor(ed, 16) -429 # . . push args -430 68/push 0x10/imm32/nbytes-of-args-for-get-num -431 50/push-EAX/ed -432 # . . call -433 e8/call tailor-exit-descriptor/disp32 -434 # . . discard args -435 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -436 # *don't* prime the pump -437 # get-num(in, out, err, ed) -438 # . . push args -439 50/push-EAX/ed -440 68/push _test-error-stream/imm32 -441 68/push _test-output-stream/imm32 -442 68/push _test-buffered-file/imm32 -443 # . . call -444 e8/call get-num/disp32 -445 # registers except ESP may be clobbered at this point -446 # . . discard args -447 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP -448 # check that get-num tried to call exit(1) -449 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 -450 # . . push args -451 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 -452 68/push 2/imm32 -453 # . . push ed->value -454 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -455 # . . call -456 e8/call check-ints-equal/disp32 -457 # . . discard args -458 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -459 # . reclaim locals -460 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -461 5d/pop-to-EBP -462 c3/return -463 -464 ## helpers -465 -466 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) -467 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> -468 # . prolog -469 55/push-EBP -470 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -471 # write(f, "Error: ") -472 # . . push args -473 68/push "Error: "/imm32 -474 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) -475 # . . call -476 e8/call write/disp32 -477 # . . discard args -478 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -479 # write(f, s) -480 # . . push args -481 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) -482 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 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, " expected") -488 # . . push args -489 68/push " expected\n"/imm32 -490 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 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 # stop(ed, 1) -496 # . . push args -497 68/push 1/imm32 -498 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -499 # . . call -500 e8/call stop/disp32 -501 # should never get past this point -502 $expected:dead-end: -503 # . epilog -504 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -505 5d/pop-to-EBP -506 c3/return -507 -508 # read a byte from 'f', and save it in 'Look' -509 get-char: # f : (address buffered-file) -> <void> -510 # . prolog -511 55/push-EBP -512 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -513 # . save registers -514 50/push-EAX -515 # EAX = read-byte-buffered(f) -516 # . . push args -517 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -518 # . . call -519 e8/call read-byte-buffered/disp32 -520 # . . discard args -521 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -522 # save EAX to Look -523 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look -524 $get-char:end: -525 # . restore registers -526 58/pop-to-EAX -527 # . epilog -528 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -529 5d/pop-to-EBP -530 c3/return -531 -532 is-digit?: # c : int -> EAX : boolean -533 # . prolog -534 55/push-EBP -535 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -536 # EAX = false -537 b8/copy-to-EAX 0/imm32 -538 # if (c < '0') return false -539 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) -540 7c/jump-if-lesser $is-digit?:end/disp8 -541 # if (c > '9') return false -542 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) -543 7f/jump-if-greater $is-digit?:end/disp8 -544 # otherwise return true -545 b8/copy-to-EAX 1/imm32 -546 $is-digit?:end: -547 # . epilog -548 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -549 5d/pop-to-EBP -550 c3/return -551 -552 == data -553 -554 Look: # (char with some extra padding) -555 0/imm32 + 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 single digit into 'out'. Abort if there are none, or if there is no space in 'out'. +188 # Input comes from the global variable 'Look', and we leave the next byte from +189 # 'in' into it on exit. +190 get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> +191 # pseudocode: +192 # if (!is-digit?(Look)) expected(ed, err, "integer") +193 # if out->write >= out->length +194 # write(err, "Error: too many digits in number\n") +195 # stop(ed, 1) +196 # out->data[out->write] = LSB(Look) +197 # ++out->write +198 # Look = get-char(in) +199 # +200 # registers: +201 # in: ESI +202 # out: EDI +203 # out->write: ECX (cached copy; need to keep in sync) +204 # out->length: EDX +205 # temporaries: EAX, EBX +206 # We can't allocate Look to a register because it gets written implicitly in +207 # get-char in each iteration of the loop. (Thereby demonstrating that it's +208 # not the right interface for us. But we'll keep it just to follow Crenshaw.) +209 # +210 # . prolog +211 55/push-EBP +212 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +213 # - if (is-digit?(Look)) expected(ed, err, "integer") +214 # . EAX = is-digit?(Look) +215 # . . push args +216 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +217 # . . call +218 e8/call is-digit?/disp32 +219 # . . discard args +220 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +221 # . if (EAX == 0) +222 3d/compare-EAX-and 0/imm32 +223 75/jump-if-not-equal $get-num:main/disp8 +224 # . expected(ed, err, "integer") +225 # . . push args +226 68/push "integer"/imm32 +227 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +228 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) +229 # . . call +230 e8/call expected/disp32 # never returns +231 # . . discard args +232 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +233 $get-num:main: +234 # - otherwise read a digit +235 # . save registers +236 50/push-EAX +237 51/push-ECX +238 52/push-EDX +239 53/push-EBX +240 56/push-ESI +241 57/push-EDI +242 # read necessary variables to registers +243 # ESI = in +244 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +245 # EDI = out +246 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI +247 # ECX = out->write +248 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX +249 # EDX = out->length +250 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX +251 # if (out->write >= out->length) error +252 39/compare 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # compare EDX with ECX +253 7d/jump-if-lesser $get-num:stage2/disp8 +254 # . error(ed, err, msg) # TODO: show full number +255 # . . push args +256 68/push "get-num: too many digits in number"/imm32 +257 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +258 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x14/disp8 . # push *(EBP+20) +259 # . . call +260 e8/call error/disp32 # never returns +261 # . . discard args +262 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +263 $get-num:stage2: +264 # out->data[out->write] = LSB(Look) +265 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 +266 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX +267 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX +268 # ++out->write +269 41/increment-ECX +270 # Look = get-char(in) +271 # . . push args +272 56/push-ESI +273 # . . call +274 e8/call get-char/disp32 +275 # . . discard args +276 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +277 $get-num:loop-end: +278 # persist necessary variables from registers +279 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI +280 $get-num:end: +281 # . restore registers +282 5f/pop-to-EDI +283 5e/pop-to-ESI +284 5b/pop-to-EBX +285 5a/pop-to-EDX +286 59/pop-to-ECX +287 58/pop-to-EAX +288 # . epilog +289 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +290 5d/pop-to-EBP +291 c3/return +292 +293 test-get-num-reads-single-digit: +294 # - check that get-num returns first character if it's a digit +295 # This test uses exit-descriptors. Use EBP for setting up local variables. +296 55/push-EBP +297 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +298 # clear all streams +299 # . clear-stream(_test-stream) +300 # . . push args +301 68/push _test-stream/imm32 +302 # . . call +303 e8/call clear-stream/disp32 +304 # . . discard args +305 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +306 # . clear-stream(_test-buffered-file+4) +307 # . . push args +308 b8/copy-to-EAX _test-buffered-file/imm32 +309 05/add-to-EAX 4/imm32 +310 50/push-EAX +311 # . . call +312 e8/call clear-stream/disp32 +313 # . . discard args +314 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +315 # . clear-stream(_test-output-stream) +316 # . . push args +317 68/push _test-output-stream/imm32 +318 # . . call +319 e8/call clear-stream/disp32 +320 # . . discard args +321 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +322 # . clear-stream(_test-error-stream) +323 # . . push args +324 68/push _test-error-stream/imm32 +325 # . . call +326 e8/call clear-stream/disp32 +327 # . . discard args +328 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +329 # initialize 'in' +330 # . write(_test-stream, "3") +331 # . . push args +332 68/push "3"/imm32 +333 68/push _test-stream/imm32 +334 # . . call +335 e8/call write/disp32 +336 # . . discard args +337 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +338 # initialize exit-descriptor 'ed' for the call to 'get-num' below +339 # . var ed/EAX : exit-descriptor +340 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +341 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +342 # . tailor-exit-descriptor(ed, 16) +343 # . . push args +344 68/push 0x10/imm32/nbytes-of-args-for-get-num +345 50/push-EAX/ed +346 # . . call +347 e8/call tailor-exit-descriptor/disp32 +348 # . . discard args +349 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +350 # prime the pump +351 # . get-char(_test-buffered-file) +352 # . . push args +353 68/push _test-buffered-file/imm32 +354 # . . call +355 e8/call get-char/disp32 +356 # . . discard args +357 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +358 # get-num(in, out, err, ed) +359 # . . push args +360 50/push-EAX/ed +361 68/push _test-error-stream/imm32 +362 68/push _test-output-stream/imm32 +363 68/push _test-buffered-file/imm32 +364 # . . call +365 e8/call get-num/disp32 +366 # registers except ESP may be clobbered at this point +367 # . . discard args +368 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +369 # check-ints-equal(*_test-output-stream->data, '3', msg) +370 # . . push args +371 68/push "F - test-get-num-reads-single-digit"/imm32 +372 68/push 0x33/imm32 +373 b8/copy-to-EAX _test-output-stream/imm32 +374 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +375 # . . call +376 e8/call check-ints-equal/disp32 +377 # . . discard args +378 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +379 # . reclaim locals +380 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +381 5d/pop-to-EBP +382 c3/return +383 +384 test-get-num-aborts-on-non-digit-in-Look: +385 # - check that get-num returns first character if it's a digit +386 # This test uses exit-descriptors. Use EBP for setting up local variables. +387 55/push-EBP +388 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +389 # clear all streams +390 # . clear-stream(_test-stream) +391 # . . push args +392 68/push _test-stream/imm32 +393 # . . call +394 e8/call clear-stream/disp32 +395 # . . discard args +396 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +397 # . clear-stream(_test-buffered-file+4) +398 # . . push args +399 b8/copy-to-EAX _test-buffered-file/imm32 +400 05/add-to-EAX 4/imm32 +401 50/push-EAX +402 # . . call +403 e8/call clear-stream/disp32 +404 # . . discard args +405 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +406 # . clear-stream(_test-output-stream) +407 # . . push args +408 68/push _test-output-stream/imm32 +409 # . . call +410 e8/call clear-stream/disp32 +411 # . . discard args +412 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +413 # . clear-stream(_test-error-stream) +414 # . . push args +415 68/push _test-error-stream/imm32 +416 # . . call +417 e8/call clear-stream/disp32 +418 # . . discard args +419 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +420 # initialize 'in' +421 # . write(_test-stream, "3") +422 # . . push args +423 68/push "3"/imm32 +424 68/push _test-stream/imm32 +425 # . . call +426 e8/call write/disp32 +427 # . . discard args +428 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +429 # initialize exit-descriptor 'ed' for the call to 'get-num' below +430 # . var ed/EAX : (address exit-descriptor) +431 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP +432 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX +433 # . tailor-exit-descriptor(ed, 16) +434 # . . push args +435 68/push 0x10/imm32/nbytes-of-args-for-get-num +436 50/push-EAX/ed +437 # . . call +438 e8/call tailor-exit-descriptor/disp32 +439 # . . discard args +440 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +441 # *don't* prime the pump +442 # get-num(in, out, err, ed) +443 # . . push args +444 50/push-EAX/ed +445 68/push _test-error-stream/imm32 +446 68/push _test-output-stream/imm32 +447 68/push _test-buffered-file/imm32 +448 # . . call +449 e8/call get-num/disp32 +450 # registers except ESP may be clobbered at this point +451 # . . discard args +452 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +453 # check that get-num tried to call exit(1) +454 # . check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 +455 # . . push args +456 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 +457 68/push 2/imm32 +458 # . . push ed->value +459 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) +460 # . . call +461 e8/call check-ints-equal/disp32 +462 # . . discard args +463 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +464 # . reclaim locals +465 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +466 5d/pop-to-EBP +467 c3/return +468 +469 ## helpers +470 +471 # write(f, "Error: "+s+" expected\n") then stop(ed, 1) +472 expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> +473 # . prolog +474 55/push-EBP +475 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +476 # write(f, "Error: ") +477 # . . push args +478 68/push "Error: "/imm32 +479 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +480 # . . call +481 e8/call write/disp32 +482 # . . discard args +483 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +484 # write(f, s) +485 # . . push args +486 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +487 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 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, " expected") +493 # . . push args +494 68/push " expected\n"/imm32 +495 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 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 # stop(ed, 1) +501 # . . push args +502 68/push 1/imm32 +503 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +504 # . . call +505 e8/call stop/disp32 +506 # should never get past this point +507 $expected:dead-end: +508 # . epilog +509 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +510 5d/pop-to-EBP +511 c3/return +512 +513 # read a byte from 'f', and save it in 'Look' +514 get-char: # f : (address buffered-file) -> <void> +515 # . prolog +516 55/push-EBP +517 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +518 # . save registers +519 50/push-EAX +520 # EAX = read-byte-buffered(f) +521 # . . push args +522 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +523 # . . call +524 e8/call read-byte-buffered/disp32 +525 # . . discard args +526 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +527 # save EAX to Look +528 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look +529 $get-char:end: +530 # . restore registers +531 58/pop-to-EAX +532 # . epilog +533 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +534 5d/pop-to-EBP +535 c3/return +536 +537 is-digit?: # c : int -> EAX : boolean +538 # . prolog +539 55/push-EBP +540 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +541 # EAX = false +542 b8/copy-to-EAX 0/imm32 +543 # if (c < '0') return false +544 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) +545 7c/jump-if-lesser $is-digit?:end/disp8 +546 # if (c > '9') return false +547 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) +548 7f/jump-if-greater $is-digit?:end/disp8 +549 # otherwise return true +550 b8/copy-to-EAX 1/imm32 +551 $is-digit?:end: +552 # . epilog +553 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +554 5d/pop-to-EBP +555 c3/return 556 -557 _test-output-stream: -558 # current write index -559 0/imm32 -560 # current read index -561 0/imm32 -562 # length -563 8/imm32 -564 # data -565 00 00 00 00 00 00 00 00 # 8 bytes -566 -567 _test-error-stream: -568 # current write index -569 0/imm32 -570 # current read index -571 0/imm32 -572 # length -573 0x40/imm32 -574 # data (4 lines x 16 bytes/line) -575 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -576 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -577 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -578 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -579 -580 # . . vim:nowrap:textwidth=0 +557 == data +558 +559 Look: # (char with some extra padding) +560 0/imm32 +561 +562 _test-output-stream: +563 # current write index +564 0/imm32 +565 # current read index +566 0/imm32 +567 # length +568 8/imm32 +569 # data +570 00 00 00 00 00 00 00 00 # 8 bytes +571 +572 _test-error-stream: +573 # current write index +574 0/imm32 +575 # current read index +576 0/imm32 +577 # length +578 0x40/imm32 +579 # data (4 lines x 16 bytes/line) +580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +581 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +582 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +583 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +584 +585 # . . vim:nowrap:textwidth=0 -- cgit 1.4.1-2-gfad0