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