diff --git a/html/subx/apps/pack.subx.html b/html/subx/apps/pack.subx.html index 36bb9206..460cb861 100644 --- a/html/subx/apps/pack.subx.html +++ b/html/subx/apps/pack.subx.html @@ -21,6 +21,7 @@ a { color:inherit; } .CommentedCode { color: #8a8a8a; } .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; } @@ -60,577 +61,1252 @@ if ('onhashchange' in window) {
https://github.com/akkartik/mu/blob/master/subx/apps/pack.subx- 1 # Read a text file of SubX instructions from stdin, and convert it into a list - 2 # of whitespace-separated ascii hex bytes on stdout, suitable to be further - 3 # processed by apps/hex. - 4 # - 5 # To run (from the subx/ directory): - 6 # $ ./subx translate *.subx apps/pack.subx -o apps/pack - 7 # $ echo '05/add-to-EAX 0x20/imm32' |./subx run apps/pack - 8 # Expected output: - 9 # 05 20 00 00 00 # 05/add-to-EAX 0x20/imm32 - 10 # The original instruction gets included as a comment at the end of each - 11 # converted line. - 12 # - 13 # There's zero error-checking. For now we assume the input program is valid. - 14 # We'll continue to rely on the C++ version for error messages. - 15 # - 16 # Label definitions and uses are left untouched for a future 'pass'. - 17 - 18 == code - 19 # instruction effective address register displacement immediate - 20 # . op subop mod rm32 base index scale r32 - 21 # . 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 - 22 - 23 # for debugging: run a single test - 24 #? e8/call test-emit-hex-zero-pad/disp32 - 25 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 26 #? eb/jump $main:end/disp8 - 27 - 28 # main: run tests if necessary, convert stdin if not - 29 # . prolog - 30 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 31 # - if argc > 1 and argv[1] == "test" then return run_tests() - 32 # . argc > 1 - 33 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP - 34 7e/jump-if-lesser-or-equal $run-main/disp8 - 35 # . argv[1] == "test" - 36 # . . push args - 37 68/push "test"/imm32 - 38 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 39 # . . call - 40 e8/call kernel-string-equal/disp32 - 41 # . . discard args - 42 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 43 # . check result - 44 3d/compare-EAX 1/imm32 - 45 75/jump-if-not-equal $run-main/disp8 - 46 # . run-tests() - 47 e8/call run-tests/disp32 - 48 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX - 49 eb/jump $main:end/disp8 - 50 $run-main: - 51 # - otherwise convert stdin - 52 # var ed/EAX : exit-descriptor - 53 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 54 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX - 55 # configure ed to really exit() - 56 # . ed->target = 0 - 57 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX - 58 # return convert(Stdin, 1/stdout, 2/stderr, ed) - 59 # . . push args - 60 50/push-EAX/ed - 61 68/push Stderr/imm32 - 62 68/push Stdout/imm32 - 63 68/push Stdin/imm32 - 64 # . . call - 65 e8/call convert/disp32 - 66 # . . discard args - 67 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 68 # . syscall(exit, 0) - 69 bb/copy-to-EBX 0/imm32 - 70 $main:end: - 71 b8/copy-to-EAX 1/imm32/exit - 72 cd/syscall 0x80/imm8 - 73 - 74 # - big picture - 75 # We'll operate on each line/instruction in isolation. That way we only need to - 76 # allocate memory for converting a single instruction. - 77 # - 78 # To pack an entire file: - 79 # skip segment headers - 80 # pack every instruction in the code segment - 81 # skip other segments - 82 - 83 # - To pack an instruction, following the C++ version: - 84 # read line - 85 # parse words - 86 # read first word as opcode and write-slice - 87 # if 0f or f2 or f3 read second opcode and write-slice - 88 # if 'f2 0f' or 'f3 0f' read third opcode and write-slice - 89 # scan words - 90 # if has metadata 'mod', parse into mod - 91 # if has metadata 'rm32', parse into rm32 - 92 # if has metadata 'r32', parse into r32 - 93 # if has metadata 'subop', parse into r32 - 94 # if at least one of the 3 was present, print-byte - 95 # scan words - 96 # if has metadata 'base', parse into base - 97 # if has metadata 'index', parse into index - 98 # if has metadata 'scale', parse into scale - 99 # if at least one of the 3 was present, print-byte -100 # parse errors => <abort> -101 # scan words -102 # if has metadata 'disp8', emit as 1 byte -103 # if has metadata 'disp16', emit as 2 bytes -104 # if has metadata 'disp32', emit as 4 bytes -105 # scan words -106 # if has metadata 'imm8', emit -107 # if has metadata 'imm32', emit as 4 bytes -108 # finally, emit line prefixed with a ' # ' -109 -110 # simplifications since we perform zero error handling (continuing to rely on the C++ version for that): -111 # missing fields are always 0-filled -112 # bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 modrm byte. You get *no* modrm byte. -113 # in case of conflict, last operand with a name is recognized -114 # silently drop extraneous operands -115 # unceremoniously abort on non-numeric operands except disp or imm -116 -117 # primary state: line -118 # stream of 512 bytes; abort if it ever overflows -119 # -120 # conceptual hierarchy within a line: -121 # line = words separated by ' ', maybe followed by comment starting with '#' -122 # word = name until '/', then 0 or more metadata separated by '/' -123 # -124 # we won't bother saving the internal structure of lines; reparsing should be cheap using two primitives: -125 # next-token(stream, delim char) -> slice (start, end pointers) -126 # slice-equal?(slice, kernel string) -127 -128 # helpers: -129 # emit(out : &buffered-file, word : &slice, width : int) -130 # if slice is all hex digits, parse and print appropriate digits -131 # otherwise just write-slice -132 # has-metadata?(word : &slice, s : &kernel-string) -> bool -133 -134 convert: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void> -135 # pseudocode: -136 # line = new-stream(512, 1) -137 # repeatedly -138 # clear-stream(line) -139 # EAX = read-line(in, line, err, ed) -140 # if EAX == EOF break -141 # convert-instruction(line, out, err, ed) -142 # flush(out) -143 # -144 # . prolog -145 55/push-EBP -146 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -147 # . save registers -148 # . restore registers -149 # . epilog -150 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -151 5d/pop-to-EBP -152 c3/return -153 -154 # (re)compute the bounds of the next word in the line -155 next-word: # line : (address stream byte), out : (address slice) -156 # . prolog -157 55/push-EBP -158 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -159 # . save registers -160 50/push-EAX -161 51/push-ECX -162 56/push-ESI -163 57/push-EDI -164 # ESI = line -165 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -166 # EDI = out -167 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI -168 # skip-chars-matching(line, ' ') -169 # . . push args -170 68/push 0x20/imm32/space -171 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -172 # . . call -173 e8/call skip-chars-matching/disp32 -174 # . . discard args -175 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -176 # out->start = &line->data[line->read] -177 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX -178 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX -179 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI -180 # if line->data[line->read] == '#': out->end = &line->data[line->write]), skip rest of stream and return -181 # . EAX = line->data[line->read] -182 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -183 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL -184 # . compare -185 3d/compare-EAX-with 0x23/imm32/pound -186 75/jump-if-not-equal $next-word:not-comment/disp8 -187 # . out->end = &line->data[line->write] -188 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX -189 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX -190 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -191 # . line->read = line->write -192 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4) -193 # . return -194 eb/jump $next-word:end/disp8 -195 $next-word:not-comment: -196 # otherwise skip-chars-not-matching(line, ' ') -197 # . . push args -198 68/push 0x20/imm32/space -199 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -200 # . . call -201 e8/call skip-chars-not-matching/disp32 -202 # . . discard args -203 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -204 # out->end = &line->data[line->read] -205 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX -206 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX -207 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) -208 $next-word:end: -209 # . restore registers -210 5f/pop-to-EDI -211 5e/pop-to-ESI -212 59/pop-to-ECX -213 58/pop-to-EAX -214 # . epilog -215 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -216 5d/pop-to-EBP -217 c3/return -218 -219 test-next-word: -220 # . prolog -221 55/push-EBP -222 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -223 # setup -224 # . clear-stream(_test-stream) -225 # . . push args -226 68/push _test-stream/imm32 -227 # . . call -228 e8/call clear-stream/disp32 -229 # . . discard args -230 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -231 # var slice/ECX = {0, 0} -232 68/push 0/imm32/end -233 68/push 0/imm32/start -234 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -235 # write(_test-stream, " ab") -236 # . . push args -237 68/push " ab"/imm32 -238 68/push _test-stream/imm32 -239 # . . call -240 e8/call write/disp32 -241 # . . discard args -242 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -243 # next-word(_test-stream, slice) -244 # . . push args -245 51/push-ECX -246 68/push _test-stream/imm32 -247 # . . call -248 e8/call next-word/disp32 -249 # . . discard args -250 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -251 # check-ints-equal(slice->start - _test-stream->data, 2, msg) -252 # . check-ints-equal(slice->start - _test-stream, 14, msg) -253 # . . push args -254 68/push "F - test-next-word: start"/imm32 -255 68/push 0xe/imm32 -256 # . . push slice->start - _test-stream -257 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX -258 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX -259 50/push-EAX -260 # . . call -261 e8/call check-ints-equal/disp32 -262 # . . discard args -263 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -264 # check-ints-equal(slice->end - _test-stream->data, 4, msg) -265 # . check-ints-equal(slice->end - _test-stream, 16, msg) -266 # . . push args -267 68/push "F - test-next-word: end"/imm32 -268 68/push 0x10/imm32 -269 # . . push slice->end - _test-stream -270 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -271 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX -272 50/push-EAX -273 # . . call -274 e8/call check-ints-equal/disp32 -275 # . . discard args -276 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -277 # . epilog -278 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -279 5d/pop-to-EBP -280 c3/return -281 -282 test-next-word-returns-whole-comment: -283 # . prolog -284 55/push-EBP -285 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -286 # setup -287 # . clear-stream(_test-stream) -288 # . . push args -289 68/push _test-stream/imm32 -290 # . . call -291 e8/call clear-stream/disp32 -292 # . . discard args -293 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -294 # var slice/ECX = {0, 0} -295 68/push 0/imm32/end -296 68/push 0/imm32/start -297 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -298 # write(_test-stream, " # a") -299 # . . push args -300 68/push " # a"/imm32 -301 68/push _test-stream/imm32 -302 # . . call -303 e8/call write/disp32 -304 # . . discard args -305 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -306 # next-word(_test-stream, slice) -307 # . . push args -308 51/push-ECX -309 68/push _test-stream/imm32 -310 # . . call -311 e8/call next-word/disp32 -312 # . . discard args -313 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -314 # check-ints-equal(slice->start - _test-stream->data, 2, msg) -315 # . check-ints-equal(slice->start - _test-stream, 14, msg) -316 # . . push args -317 68/push "F - test-next-word-returns-whole-comment: start"/imm32 -318 68/push 0xe/imm32 -319 # . . push slice->start - _test-stream -320 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX -321 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX -322 50/push-EAX -323 # . . call -324 e8/call check-ints-equal/disp32 -325 # . . discard args -326 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -327 # check-ints-equal(slice->end - _test-stream->data, 5, msg) -328 # . check-ints-equal(slice->end - _test-stream, 17, msg) -329 # . . push args -330 68/push "F - test-next-word-returns-whole-comment: end"/imm32 -331 68/push 0x11/imm32 -332 # . . push slice->end - _test-stream -333 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX -334 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX -335 50/push-EAX -336 # . . call -337 e8/call check-ints-equal/disp32 -338 # . . discard args -339 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -340 # . epilog -341 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -342 5d/pop-to-EBP -343 c3/return -344 -345 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte -346 emit-hex: # out : (address buffered-file), n : int, width : int -347 # . prolog -348 55/push-EBP -349 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -350 # . save registers -351 50/push-EAX -352 51/push-ECX -353 52/push-EDX -354 53/push-EBX -355 57/push-EDI -356 # EDI = out -357 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI -358 # EBX = n -359 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX -360 # EDX = width -361 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX -362 # var curr/ECX = 0 -363 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -364 $emit-hex:loop: -365 # if (curr >= width) break -366 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX and EDX -367 7d/jump-if-greater-or-equal $emit-hex:end/disp8 -368 # print-byte(out, EBX) -369 # . . push args -370 53/push-EBX -371 57/push-EDI -372 # . . call -373 e8/call print-byte/disp32 -374 # . . discard args -375 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -376 # write-byte(out, ' ') -377 # . . push args -378 68/push 0x20/imm32/space -379 57/push-EDI -380 # . . call -381 e8/call write-byte/disp32 -382 # . . discard args -383 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -384 # EBX = EBX >> 8 -385 c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/EBX . . . . . 8/imm8 # shift EBX right by 8 bits, while padding zeroes -386 # ++curr -387 41/increment-ECX -388 eb/jump $emit-hex:loop/disp8 -389 $emit-hex:end: -390 # . restore registers -391 5f/pop-to-EDI -392 5b/pop-to-EBX -393 5a/pop-to-EAX -394 59/pop-to-ECX -395 58/pop-to-EAX -396 # . epilog -397 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -398 5d/pop-to-EBP -399 c3/return -400 -401 test-emit-hex-single-byte: -402 # setup -403 # . clear-stream(_test-stream) -404 # . . push args -405 68/push _test-stream/imm32 -406 # . . call -407 e8/call clear-stream/disp32 -408 # . . discard args -409 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -410 # . clear-stream(_test-buffered-file+4) -411 # . . push args -412 b8/copy-to-EAX _test-buffered-file/imm32 -413 05/add-to-EAX 4/imm32 -414 50/push-EAX -415 # . . call -416 e8/call clear-stream/disp32 -417 # . . discard args -418 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -419 # emit-hex(_test-buffered-file, 0xab, 1) -420 # . . push args -421 68/push 1/imm32 -422 68/push 0xab/imm32 -423 68/push _test-buffered-file/imm32 -424 # . . call -425 e8/call emit-hex/disp32 -426 # . . discard args -427 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -428 # flush(_test-buffered-file) -429 # . . push args -430 68/push _test-buffered-file/imm32 -431 # . . call -432 e8/call flush/disp32 -433 # . . discard args -434 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -435 # check-ints-equal(*_test-stream->data, 'ab ', msg) -436 # . . push args -437 68/push "F - test-emit-hex-single-byte"/imm32 -438 68/push 0x206261/imm32 -439 # . . push *_test-stream->data -440 b8/copy-to-EAX _test-stream/imm32 -441 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -442 # . . call -443 e8/call check-ints-equal/disp32 -444 # . . discard args -445 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -446 # . end -447 c3/return -448 -449 test-emit-hex-multiple-byte: -450 # setup -451 # . clear-stream(_test-stream) -452 # . . push args -453 68/push _test-stream/imm32 -454 # . . call -455 e8/call clear-stream/disp32 -456 # . . discard args -457 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -458 # . clear-stream(_test-buffered-file+4) -459 # . . push args -460 b8/copy-to-EAX _test-buffered-file/imm32 -461 05/add-to-EAX 4/imm32 -462 50/push-EAX -463 # . . call -464 e8/call clear-stream/disp32 -465 # . . discard args -466 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -467 # emit-hex(_test-buffered-file, 0x1234, 2) -468 # . . push args -469 68/push 2/imm32 -470 68/push 0x1234/imm32 -471 68/push _test-buffered-file/imm32 -472 # . . call -473 e8/call emit-hex/disp32 -474 # . . discard args -475 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -476 # flush(_test-buffered-file) -477 # . . push args -478 68/push _test-buffered-file/imm32 -479 # . . call -480 e8/call flush/disp32 -481 # . . discard args -482 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -483 # check(_test-stream->data == '34 12 ') -484 # . check-ints-equal(_test-stream->data[0..3], '34 1', msg) -485 # . . push args -486 68/push "F - test-emit-hex-multiple-byte/1"/imm32 -487 68/push 0x31203433/imm32 -488 # . . push *_test-stream->data -489 b8/copy-to-EAX _test-stream/imm32 -490 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -491 # . . call -492 e8/call check-ints-equal/disp32 -493 # . . discard args -494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -495 # . check-ints-equal(_test-stream->data[4..7], '2 ', msg) -496 # . . push args -497 68/push "F - test-emit-hex-multiple-byte/2"/imm32 -498 68/push 0x2032/imm32 -499 # . . push *_test-stream->data -500 b8/copy-to-EAX _test-stream/imm32 -501 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0x10/disp8 . # push *(EAX+16) -502 # . . call -503 e8/call check-ints-equal/disp32 -504 # . . discard args -505 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -506 # . end -507 c3/return -508 # . end -509 c3/return -510 -511 test-emit-hex-zero-pad: -512 # setup -513 # . clear-stream(_test-stream) -514 # . . push args -515 68/push _test-stream/imm32 -516 # . . call -517 e8/call clear-stream/disp32 -518 # . . discard args -519 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -520 # . clear-stream(_test-buffered-file+4) -521 # . . push args -522 b8/copy-to-EAX _test-buffered-file/imm32 -523 05/add-to-EAX 4/imm32 -524 50/push-EAX -525 # . . call -526 e8/call clear-stream/disp32 -527 # . . discard args -528 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -529 # emit-hex(_test-buffered-file, 0xab, 2) -530 # . . push args -531 68/push 2/imm32 -532 68/push 0xab/imm32 -533 68/push _test-buffered-file/imm32 -534 # . . call -535 e8/call emit-hex/disp32 -536 # . . discard args -537 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -538 # flush(_test-buffered-file) -539 # . . push args -540 68/push _test-buffered-file/imm32 -541 # . . call -542 e8/call flush/disp32 -543 # . . discard args -544 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -545 # check(_test-stream->data == '00 ab') -546 # . check-ints-equal(*_test-stream->data, 'ab 0', msg) -547 # . . push args -548 68/push "F - test-emit-hex-zero-pad/1"/imm32 -549 68/push 0x30206261/imm32 -550 # . . push *_test-stream->data -551 b8/copy-to-EAX _test-stream/imm32 -552 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -553 # . . call -554 e8/call check-ints-equal/disp32 -555 # . . discard args -556 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -557 # . check-ints-equal(*_test-stream->data[1], '0 ', msg) -558 # . . push args -559 68/push "F - test-emit-hex-zero-pad/2"/imm32 -560 68/push 0x2030/imm32 -561 # . . push *_test-stream->data -562 b8/copy-to-EAX _test-stream/imm32 -563 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0x10/disp8 . # push *(EAX+16) -564 # . . call -565 e8/call check-ints-equal/disp32 -566 # . . discard args -567 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -568 # . end -569 c3/return -570 -571 # . . vim:nowrap:textwidth=0 + 1 # Read a text file of SubX instructions from stdin, and convert it into a list + 2 # of whitespace-separated ascii hex bytes on stdout, suitable to be further + 3 # processed by apps/hex. + 4 # + 5 # To run (from the subx/ directory): + 6 # $ ./subx translate *.subx apps/pack.subx -o apps/pack + 7 # $ echo '05/add-to-EAX 0x20/imm32' |./subx run apps/pack + 8 # Expected output: + 9 # 05 20 00 00 00 # 05/add-to-EAX 0x20/imm32 + 10 # The original instruction gets included as a comment at the end of each + 11 # converted line. + 12 # + 13 # There's zero error-checking. For now we assume the input program is valid. + 14 # We'll continue to rely on the C++ version for error messages. + 15 # + 16 # Label definitions and uses are left untouched for a future 'pass'. + 17 + 18 == code + 19 # instruction effective address register displacement immediate + 20 # . op subop mod rm32 base index scale r32 + 21 # . 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 + 22 + 23 # for debugging: run a single test + 24 #? e8/call test-emit-non-number-with-metadata/disp32 + 25 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 26 #? eb/jump $main:end/disp8 + 27 + 28 # main: run tests if necessary, convert stdin if not + 29 # . prolog + 30 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 31 # - if argc > 1 and argv[1] == "test" then return run_tests() + 32 # . argc > 1 + 33 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP + 34 7e/jump-if-lesser-or-equal $run-main/disp8 + 35 # . argv[1] == "test" + 36 # . . push args + 37 68/push "test"/imm32 + 38 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 39 # . . call + 40 e8/call kernel-string-equal?/disp32 + 41 # . . discard args + 42 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 43 # . check result + 44 3d/compare-EAX 1/imm32 + 45 75/jump-if-not-equal $run-main/disp8 + 46 # . run-tests() + 47 e8/call run-tests/disp32 + 48 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + 49 eb/jump $main:end/disp8 + 50 $run-main: + 51 # - otherwise convert stdin + 52 # var ed/EAX : exit-descriptor + 53 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 54 89/copy 3/mod/direct 0/rm32/EAX . . . 4/r32/ESP . . # copy ESP to EAX + 55 # configure ed to really exit() + 56 # . ed->target = 0 + 57 c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + 58 # return convert(Stdin, 1/stdout, 2/stderr, ed) + 59 # . . push args + 60 50/push-EAX/ed + 61 68/push Stderr/imm32 + 62 68/push Stdout/imm32 + 63 68/push Stdin/imm32 + 64 # . . call + 65 e8/call convert/disp32 + 66 # . . discard args + 67 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 68 # . syscall(exit, 0) + 69 bb/copy-to-EBX 0/imm32 + 70 $main:end: + 71 b8/copy-to-EAX 1/imm32/exit + 72 cd/syscall 0x80/imm8 + 73 + 74 # - big picture + 75 # We'll operate on each line/instruction in isolation. That way we only need to + 76 # allocate memory for converting a single instruction. + 77 # + 78 # To pack an entire file: + 79 # skip segment headers + 80 # pack every instruction in the code segment + 81 # skip other segments + 82 + 83 # - To pack an instruction, following the C++ version: + 84 # read line + 85 # parse words + 86 # read first word as opcode and write-slice + 87 # if 0f or f2 or f3 read second opcode and write-slice + 88 # if 'f2 0f' or 'f3 0f' read third opcode and write-slice + 89 # while true: + 90 # word-slice = next-word + 91 # if empty(word-slice) break + 92 # if has metadata 'mod', parse into mod + 93 # if has metadata 'rm32', parse into rm32 + 94 # if has metadata 'r32', parse into r32 + 95 # if has metadata 'subop', parse into r32 + 96 # if at least one of the 3 was present, print-byte + 97 # while true: + 98 # word-slice = next-word + 99 # if empty(word-slice) break + 100 # if has metadata 'base', parse into base + 101 # if has metadata 'index', parse into index + 102 # if has metadata 'scale', parse into scale + 103 # if at least one of the 3 was present, print-byte + 104 # parse errors => <abort> + 105 # while true: + 106 # word-slice = next-word + 107 # if empty(word-slice) break + 108 # if has metadata 'disp8', emit as 1 byte + 109 # if has metadata 'disp16', emit as 2 bytes + 110 # if has metadata 'disp32', emit as 4 bytes + 111 # while true: + 112 # word-slice = next-word + 113 # if empty(word-slice) break + 114 # if has metadata 'imm8', emit + 115 # if has metadata 'imm32', emit as 4 bytes + 116 # finally, emit line prefixed with a ' # ' + 117 + 118 # simplifications since we perform zero error handling (continuing to rely on the C++ version for that): + 119 # missing fields are always 0-filled + 120 # bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 modrm byte. You get *no* modrm byte. + 121 # in case of conflict, last operand with a name is recognized + 122 # silently drop extraneous operands + 123 # unceremoniously abort on non-numeric operands except disp or imm + 124 + 125 # primary state: line + 126 # stream of 512 bytes; abort if it ever overflows + 127 # + 128 # conceptual hierarchy within a line: + 129 # line = words separated by ' ', maybe followed by comment starting with '#' + 130 # word = name until '/', then 0 or more metadata separated by '/' + 131 # + 132 # we won't bother saving the internal structure of lines; reparsing should be cheap using two primitives: + 133 # next-token(stream, delim char) -> slice (start, end pointers) + 134 # next-token(stream, slice, delim char) -> slice' + 135 # slice-equal?(slice, string) + 136 + 137 convert: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void> + 138 # pseudocode: + 139 # line = new-stream(512, 1) + 140 # repeatedly + 141 # clear-stream(line) + 142 # EAX = read-line(in, line, err, ed) + 143 # if EAX == EOF break + 144 # convert-instruction(line, out, err, ed) + 145 # flush(out) + 146 # + 147 # . prolog + 148 55/push-EBP + 149 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 150 # . save registers + 151 # . restore registers + 152 # . epilog + 153 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 154 5d/pop-to-EBP + 155 c3/return + 156 + 157 # (re)compute the bounds of the next word in the line + 158 next-word: # line : (address stream byte), out : (address slice) + 159 # . prolog + 160 55/push-EBP + 161 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 162 # . save registers + 163 50/push-EAX + 164 51/push-ECX + 165 56/push-ESI + 166 57/push-EDI + 167 # ESI = line + 168 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 169 # EDI = out + 170 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + 171 # skip-chars-matching(line, ' ') + 172 # . . push args + 173 68/push 0x20/imm32/space + 174 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 175 # . . call + 176 e8/call skip-chars-matching/disp32 + 177 # . . discard args + 178 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 179 # out->start = &line->data[line->read] + 180 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX + 181 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX + 182 89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI + 183 # if line->data[line->read] == '#': out->end = &line->data[line->write]), skip rest of stream and return + 184 # . EAX = line->data[line->read] + 185 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 186 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL + 187 # . compare + 188 3d/compare-EAX-with 0x23/imm32/pound + 189 75/jump-if-not-equal $next-word:not-comment/disp8 + 190 # . out->end = &line->data[line->write] + 191 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX + 192 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy ESI+EAX+12 to EAX + 193 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) + 194 # . line->read = line->write + 195 89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4) + 196 # . return + 197 eb/jump $next-word:end/disp8 + 198 $next-word:not-comment: + 199 # otherwise skip-chars-not-matching(line, ' ') + 200 # . . push args + 201 68/push 0x20/imm32/space + 202 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 203 # . . call + 204 e8/call skip-chars-not-matching/disp32 + 205 # . . discard args + 206 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 207 # out->end = &line->data[line->read] + 208 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX + 209 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX + 210 89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4) + 211 $next-word:end: + 212 # . restore registers + 213 5f/pop-to-EDI + 214 5e/pop-to-ESI + 215 59/pop-to-ECX + 216 58/pop-to-EAX + 217 # . epilog + 218 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 219 5d/pop-to-EBP + 220 c3/return + 221 + 222 test-next-word: + 223 # . prolog + 224 55/push-EBP + 225 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 226 # setup + 227 # . clear-stream(_test-stream) + 228 # . . push args + 229 68/push _test-stream/imm32 + 230 # . . call + 231 e8/call clear-stream/disp32 + 232 # . . discard args + 233 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 234 # var slice/ECX = {0, 0} + 235 68/push 0/imm32/end + 236 68/push 0/imm32/start + 237 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 238 # write(_test-stream, " ab") + 239 # . . push args + 240 68/push " ab"/imm32 + 241 68/push _test-stream/imm32 + 242 # . . call + 243 e8/call write/disp32 + 244 # . . discard args + 245 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 246 # next-word(_test-stream, slice) + 247 # . . push args + 248 51/push-ECX + 249 68/push _test-stream/imm32 + 250 # . . call + 251 e8/call next-word/disp32 + 252 # . . discard args + 253 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 254 # check-ints-equal(slice->start - _test-stream->data, 2, msg) + 255 # . check-ints-equal(slice->start - _test-stream, 14, msg) + 256 # . . push args + 257 68/push "F - test-next-word: start"/imm32 + 258 68/push 0xe/imm32 + 259 # . . push slice->start - _test-stream + 260 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX + 261 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX + 262 50/push-EAX + 263 # . . call + 264 e8/call check-ints-equal/disp32 + 265 # . . discard args + 266 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 267 # check-ints-equal(slice->end - _test-stream->data, 4, msg) + 268 # . check-ints-equal(slice->end - _test-stream, 16, msg) + 269 # . . push args + 270 68/push "F - test-next-word: end"/imm32 + 271 68/push 0x10/imm32 + 272 # . . push slice->end - _test-stream + 273 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX + 274 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX + 275 50/push-EAX + 276 # . . call + 277 e8/call check-ints-equal/disp32 + 278 # . . discard args + 279 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 280 # . epilog + 281 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 282 5d/pop-to-EBP + 283 c3/return + 284 + 285 test-next-word-returns-whole-comment: + 286 # . prolog + 287 55/push-EBP + 288 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 289 # setup + 290 # . clear-stream(_test-stream) + 291 # . . push args + 292 68/push _test-stream/imm32 + 293 # . . call + 294 e8/call clear-stream/disp32 + 295 # . . discard args + 296 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 297 # var slice/ECX = {0, 0} + 298 68/push 0/imm32/end + 299 68/push 0/imm32/start + 300 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 301 # write(_test-stream, " # a") + 302 # . . push args + 303 68/push " # a"/imm32 + 304 68/push _test-stream/imm32 + 305 # . . call + 306 e8/call write/disp32 + 307 # . . discard args + 308 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 309 # next-word(_test-stream, slice) + 310 # . . push args + 311 51/push-ECX + 312 68/push _test-stream/imm32 + 313 # . . call + 314 e8/call next-word/disp32 + 315 # . . discard args + 316 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 317 # check-ints-equal(slice->start - _test-stream->data, 2, msg) + 318 # . check-ints-equal(slice->start - _test-stream, 14, msg) + 319 # . . push args + 320 68/push "F - test-next-word-returns-whole-comment: start"/imm32 + 321 68/push 0xe/imm32 + 322 # . . push slice->start - _test-stream + 323 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX + 324 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX + 325 50/push-EAX + 326 # . . call + 327 e8/call check-ints-equal/disp32 + 328 # . . discard args + 329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 330 # check-ints-equal(slice->end - _test-stream->data, 5, msg) + 331 # . check-ints-equal(slice->end - _test-stream, 17, msg) + 332 # . . push args + 333 68/push "F - test-next-word-returns-whole-comment: end"/imm32 + 334 68/push 0x11/imm32 + 335 # . . push slice->end - _test-stream + 336 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX + 337 81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX + 338 50/push-EAX + 339 # . . call + 340 e8/call check-ints-equal/disp32 + 341 # . . discard args + 342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 343 # . epilog + 344 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 345 5d/pop-to-EBP + 346 c3/return + 347 + 348 has-metadata?: # word : (address slice), s : (address string) -> EAX : boolean + 349 # pseudocode: + 350 # var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name + 351 # curr = twig->end + 352 # while true: + 353 # twig = next-token-from-slice(curr, word->end, '/') + 354 # if twig.empty() break + 355 # if slice-equal?(twig, s) return true + 356 # curr = twig->end + 357 # return false + 358 # . prolog + 359 55/push-EBP + 360 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 361 # . save registers + 362 51/push-ECX + 363 52/push-EDX + 364 56/push-ESI + 365 57/push-EDI + 366 # ESI = word + 367 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 368 # EDX = word->end + 369 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX + 370 # var twig/EDI : (address slice) = {0, 0} + 371 68/push 0/imm32/end + 372 68/push 0/imm32/start + 373 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI + 374 # next-token-from-slice(word->start, word->end, '/', twig) + 375 # . . push args + 376 57/push-EDI + 377 68/push 0x2f/imm32/slash + 378 52/push-EDX + 379 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI + 380 # . . call + 381 e8/call next-token-from-slice/disp32 + 382 # . . discard args + 383 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 384 # curr/ECX = twig->end + 385 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX + 386 $has-metadata?:loop: + 387 # next-token-from-slice(curr, word->end, '/', twig) + 388 # . . push args + 389 57/push-EDI + 390 68/push 0x2f/imm32/slash + 391 52/push-EDX + 392 51/push-ECX + 393 # . . call + 394 e8/call next-token-from-slice/disp32 + 395 # . . discard args + 396 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 397 # if slice-empty?(twig) return false + 398 # . EAX = slice-empty?(twig) + 399 # . . push args + 400 57/push-EDI + 401 # . . call + 402 e8/call slice-empty?/disp32 + 403 # . . discard args + 404 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 405 # . if (EAX != 0) return false + 406 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX + 407 75/compare-if-not-equal $has-metadata?:false/disp8 + 408 # if slice-equal?(twig, s) return true + 409 # . EAX = slice-equal?(twig, s) + 410 # . . push args + 411 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 412 57/push-EDI + 413 # . . call + 414 e8/call slice-equal?/disp32 + 415 # . . discard args + 416 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 417 # . if (EAX != 0) return true + 418 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX + 419 75/compare-if-not-equal $has-metadata?:true/disp8 + 420 # curr = twig->end + 421 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX + 422 eb/jump $has-metadata?:loop/disp8 + 423 $has-metadata?:true: + 424 b8/copy-to-EAX 1/imm32/true + 425 eb/jump $has-metadata?:end/disp8 + 426 $has-metadata?:false: + 427 b8/copy-to-EAX 0/imm32/false + 428 $has-metadata?:end: + 429 # . reclaim locals + 430 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 431 # . restore registers + 432 5f/pop-to-EDI + 433 5e/pop-to-ESI + 434 5a/pop-to-EDX + 435 59/pop-to-ECX + 436 # . epilog + 437 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 438 5d/pop-to-EBP + 439 c3/return + 440 + 441 test-has-metadata-true: + 442 # . prolog + 443 55/push-EBP + 444 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 445 # (EAX..ECX) = "ab/c" + 446 b8/copy-to-EAX "ab/c"/imm32 + 447 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 448 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 449 05/add-to-EAX 4/imm32 + 450 # var in/ESI : (address slice) = {EAX, ECX} + 451 51/push-ECX + 452 50/push-EAX + 453 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI + 454 # EAX = has-metadata?(ESI, "c") + 455 # . . push args + 456 68/push "c"/imm32 + 457 56/push-ESI + 458 # . . call + 459 e8/call has-metadata?/disp32 + 460 # . . discard args + 461 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x8/imm32 # add to ESP + 462 # check-ints-equal(EAX, 1, msg) + 463 # . . push args + 464 68/push "F - test-has-metadata-true"/imm32 + 465 68/push 1/imm32/true + 466 50/push-EAX + 467 # . . call + 468 e8/call check-ints-equal/disp32 + 469 # . . discard args + 470 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 471 # . epilog + 472 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 473 5d/pop-to-EBP + 474 c3/return + 475 + 476 test-has-metadata-false: + 477 # . prolog + 478 55/push-EBP + 479 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 480 # (EAX..ECX) = "ab/c" + 481 b8/copy-to-EAX "ab/c"/imm32 + 482 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 483 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 484 05/add-to-EAX 4/imm32 + 485 # var in/ESI : (address slice) = {EAX, ECX} + 486 51/push-ECX + 487 50/push-EAX + 488 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI + 489 # EAX = has-metadata?(ESI, "c") + 490 # . . push args + 491 68/push "d"/imm32 + 492 56/push-ESI + 493 # . . call + 494 e8/call has-metadata?/disp32 + 495 # . . discard args + 496 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x8/imm32 # add to ESP + 497 # check-ints-equal(EAX, 0, msg) + 498 # . . push args + 499 68/push "F - test-has-metadata-false"/imm32 + 500 68/push 0/imm32/false + 501 50/push-EAX + 502 # . . call + 503 e8/call check-ints-equal/disp32 + 504 # . . discard args + 505 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 506 # . epilog + 507 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 508 5d/pop-to-EBP + 509 c3/return + 510 + 511 test-has-metadata-ignore-name: + 512 # . prolog + 513 55/push-EBP + 514 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 515 # (EAX..ECX) = "a/b" + 516 b8/copy-to-EAX "a/b"/imm32 + 517 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 518 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 519 05/add-to-EAX 4/imm32 + 520 # var in/ESI : (address slice) = {EAX, ECX} + 521 51/push-ECX + 522 50/push-EAX + 523 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI + 524 # EAX = has-metadata?(ESI, "a") + 525 # . . push args + 526 68/push "a"/imm32 + 527 56/push-ESI + 528 # . . call + 529 e8/call has-metadata?/disp32 + 530 # . . discard args + 531 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x8/imm32 # add to ESP + 532 # check-ints-equal(EAX, 0, msg) + 533 # . . push args + 534 68/push "F - test-has-metadata-ignore-name"/imm32 + 535 68/push 0/imm32/false + 536 50/push-EAX + 537 # . . call + 538 e8/call check-ints-equal/disp32 + 539 # . . discard args + 540 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 541 # . epilog + 542 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 543 5d/pop-to-EBP + 544 c3/return + 545 + 546 test-has-metadata-multiple-true: + 547 # . prolog + 548 55/push-EBP + 549 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 550 # (EAX..ECX) = "a/b/c" + 551 b8/copy-to-EAX "a/b/c"/imm32 + 552 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 553 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 554 05/add-to-EAX 4/imm32 + 555 # var in/ESI : (address slice) = {EAX, ECX} + 556 51/push-ECX + 557 50/push-EAX + 558 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI + 559 # EAX = has-metadata?(ESI, "c") + 560 # . . push args + 561 68/push "c"/imm32 + 562 56/push-ESI + 563 # . . call + 564 e8/call has-metadata?/disp32 + 565 # . . discard args + 566 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x8/imm32 # add to ESP + 567 # check-ints-equal(EAX, 1, msg) + 568 # . . push args + 569 68/push "F - test-has-metadata-multiple-true"/imm32 + 570 68/push 1/imm32/true + 571 50/push-EAX + 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 # . epilog + 577 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 578 5d/pop-to-EBP + 579 c3/return + 580 + 581 test-has-metadata-multiple-false: + 582 # . prolog + 583 55/push-EBP + 584 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 585 # (EAX..ECX) = "a/b/c" + 586 b8/copy-to-EAX "a/b/c"/imm32 + 587 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 588 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 1/index/ECX . 1/r32/ECX 4/disp8 . # copy EAX+ECX+4 to ECX + 589 05/add-to-EAX 4/imm32 + 590 # var in/ESI : (address slice) = {EAX, ECX} + 591 51/push-ECX + 592 50/push-EAX + 593 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI + 594 # EAX = has-metadata?(ESI, "d") + 595 # . . push args + 596 68/push "d"/imm32 + 597 56/push-ESI + 598 # . . call + 599 e8/call has-metadata?/disp32 + 600 # . . discard args + 601 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x8/imm32 # add to ESP + 602 # check-ints-equal(EAX, 0, msg) + 603 # . . push args + 604 68/push "F - test-has-metadata-multiple-false"/imm32 + 605 68/push 0/imm32/false + 606 50/push-EAX + 607 # . . call + 608 e8/call check-ints-equal/disp32 + 609 # . . discard args + 610 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 611 # . epilog + 612 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 613 5d/pop-to-EBP + 614 c3/return + 615 + 616 # if the name of 'word' is all hex digits, parse and emit it in 'width' bytes + 617 # of hex otherwise just write-slice + 618 emit: # out : (address buffered-file), word : (address slice), width : int + 619 # . prolog + 620 55/push-EBP + 621 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 622 # . save registers + 623 50/push-EAX + 624 56/push-ESI + 625 57/push-EDI + 626 # ESI = word + 627 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + 628 # var name/EDI : (address slice) = {0, 0} + 629 68/push 0/imm32/end + 630 68/push 0/imm32/start + 631 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI + 632 # name = next-token-from-slice(word->start, word->end, '/') + 633 # . . push args + 634 57/push-EDI + 635 68/push 0x2f/imm32/slash + 636 ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) + 637 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI + 638 # . . call + 639 e8/call next-token-from-slice/disp32 + 640 # . . discard args + 641 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 642 # if !is-hex-int?(name) write-slice(out, word) and return + 643 # . is-hex-int?(name) + 644 # . . push args + 645 57/push-EDI + 646 # . . call + 647 e8/call is-hex-int?/disp32 + 648 # . . discard args + 649 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 650 # . if EAX == 0 + 651 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX + 652 75/jump-if-not-equal $emit:hex-int/disp8 + 653 # . write-slice(out, word) + 654 # . . push args + 655 56/push-ESI + 656 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 657 # . . call + 658 e8/call write-slice/disp32 + 659 # . . discard args + 660 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 661 # . return + 662 eb/jump $emit:end/disp8 + 663 # otherwise emit-hex(out, parse-hex-int(name), width) + 664 $emit:hex-int: + 665 # . n/EAX = parse-hex-int(name) + 666 # . . push args + 667 57/push-EDI + 668 # . . call + 669 e8/call parse-hex-int/disp32 + 670 # . . discard args + 671 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 672 # . emit-hex(out, n, width) + 673 # . . push args + 674 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) + 675 50/push-EAX + 676 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + 677 # . . call + 678 e8/call emit-hex/disp32 + 679 # . . discard args + 680 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 681 $emit:end: + 682 # . reclaim locals + 683 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 684 # . restore registers + 685 5f/pop-to-EDI + 686 5e/pop-to-ESI + 687 58/pop-to-EAX + 688 # . epilog + 689 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 690 5d/pop-to-EBP + 691 c3/return + 692 + 693 test-emit-number: + 694 # . prolog + 695 55/push-EBP + 696 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 697 # setup + 698 # . clear-stream(_test-stream) + 699 # . . push args + 700 68/push _test-stream/imm32 + 701 # . . call + 702 e8/call clear-stream/disp32 + 703 # . . discard args + 704 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 705 # . clear-stream(_test-buffered-file+4) + 706 # . . push args + 707 b8/copy-to-EAX _test-buffered-file/imm32 + 708 05/add-to-EAX 4/imm32 + 709 50/push-EAX + 710 # . . call + 711 e8/call clear-stream/disp32 + 712 # . . discard args + 713 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 714 # var slice/ECX = "-2" + 715 68/push _test-slice-negative-two-end/imm32/end + 716 68/push _test-slice-negative-two/imm32/start + 717 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 718 # emit(_test-buffered-file, slice, 2) + 719 # . . push args + 720 68/push 2/imm32 + 721 51/push-ECX + 722 68/push _test-buffered-file/imm32 + 723 # . . call + 724 e8/call emit/disp32 + 725 # . . discard args + 726 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 727 # flush(_test-buffered-file) + 728 # . . push args + 729 68/push _test-buffered-file/imm32 + 730 # . . call + 731 e8/call flush/disp32 + 732 # . . discard args + 733 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 734 # check(_test-stream->data == 'fe ff ') + 735 # . check-ints-equal(_test-stream->data[0..3], 'fe f', msg) + 736 # . . push args + 737 68/push "F - test-emit-number/1"/imm32 + 738 68/push 0x66206566/imm32 + 739 # . . push *_test-stream->data + 740 b8/copy-to-EAX _test-stream/imm32 + 741 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + 742 # . . call + 743 e8/call check-ints-equal/disp32 + 744 # . . discard args + 745 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 746 # . check-ints-equal(_test-stream->data[4..7], 'f ', msg) + 747 # . . push args + 748 68/push "F - test-emit-number/2"/imm32 + 749 68/push 0x2066/imm32 + 750 # . . push *_test-stream->data + 751 b8/copy-to-EAX _test-stream/imm32 + 752 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0x10/disp8 . # push *(EAX+16) + 753 # . . call + 754 e8/call check-ints-equal/disp32 + 755 # . . discard args + 756 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 757 # . epilog + 758 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 759 5d/pop-to-EBP + 760 c3/return + 761 + 762 test-emit-number-with-metadata: + 763 # . prolog + 764 55/push-EBP + 765 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 766 # setup + 767 # . clear-stream(_test-stream) + 768 # . . push args + 769 68/push _test-stream/imm32 + 770 # . . call + 771 e8/call clear-stream/disp32 + 772 # . . discard args + 773 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 774 # . clear-stream(_test-buffered-file+4) + 775 # . . push args + 776 b8/copy-to-EAX _test-buffered-file/imm32 + 777 05/add-to-EAX 4/imm32 + 778 50/push-EAX + 779 # . . call + 780 e8/call clear-stream/disp32 + 781 # . . discard args + 782 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 783 # var slice/ECX = "-2/foo" + 784 68/push _test-slice-negative-two-metadata-end/imm32/end + 785 68/push _test-slice-negative-two/imm32/start + 786 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 787 # emit(_test-buffered-file, slice, 2) + 788 # . . push args + 789 68/push 2/imm32 + 790 51/push-ECX + 791 68/push _test-buffered-file/imm32 + 792 # . . call + 793 e8/call emit/disp32 + 794 # . . discard args + 795 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 796 # flush(_test-buffered-file) + 797 # . . push args + 798 68/push _test-buffered-file/imm32 + 799 # . . call + 800 e8/call flush/disp32 + 801 # . . discard args + 802 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 803 # the '/foo' will have no impact on the output + 804 # check(_test-stream->data == 'fe ff ') + 805 # . check-ints-equal(_test-stream->data[0..3], 'fe f', msg) + 806 # . . push args + 807 68/push "F - test-emit-number-with-metadata/1"/imm32 + 808 68/push 0x66206566/imm32 + 809 # . . push *_test-stream->data + 810 b8/copy-to-EAX _test-stream/imm32 + 811 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + 812 # . . call + 813 e8/call check-ints-equal/disp32 + 814 # . . discard args + 815 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 816 # . check-ints-equal(_test-stream->data[4..7], 'f ', msg) + 817 # . . push args + 818 68/push "F - test-emit-number-with-metadata/2"/imm32 + 819 68/push 0x2066/imm32 + 820 # . . push *_test-stream->data + 821 b8/copy-to-EAX _test-stream/imm32 + 822 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0x10/disp8 . # push *(EAX+16) + 823 # . . call + 824 e8/call check-ints-equal/disp32 + 825 # . . discard args + 826 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 827 # . epilog + 828 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 829 5d/pop-to-EBP + 830 c3/return + 831 + 832 test-emit-non-number: + 833 # . prolog + 834 55/push-EBP + 835 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 836 # setup + 837 # . clear-stream(_test-stream) + 838 # . . push args + 839 68/push _test-stream/imm32 + 840 # . . call + 841 e8/call clear-stream/disp32 + 842 # . . discard args + 843 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 844 # . clear-stream(_test-buffered-file+4) + 845 # . . push args + 846 b8/copy-to-EAX _test-buffered-file/imm32 + 847 05/add-to-EAX 4/imm32 + 848 50/push-EAX + 849 # . . call + 850 e8/call clear-stream/disp32 + 851 # . . discard args + 852 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 853 # var slice/ECX = "xyz" + 854 68/push _test-slice-non-number-word-end/imm32/end + 855 68/push _test-slice-non-number-word/imm32/start + 856 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 857 # emit(_test-buffered-file, slice, 2) + 858 # . . push args + 859 68/push 2/imm32 + 860 51/push-ECX + 861 68/push _test-buffered-file/imm32 + 862 # . . call + 863 e8/call emit/disp32 + 864 # . . discard args + 865 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 866 # flush(_test-buffered-file) + 867 # . . push args + 868 68/push _test-buffered-file/imm32 + 869 # . . call + 870 e8/call flush/disp32 + 871 # . . discard args + 872 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 873 # check(_test-stream->data == 'xyz') + 874 # . check-ints-equal(_test-stream->data[0..3], 'xyz', msg) + 875 # . . push args + 876 68/push "F - test-emit-non-number"/imm32 + 877 68/push 0x7a7978/imm32 + 878 # . . push *_test-stream->data + 879 b8/copy-to-EAX _test-stream/imm32 + 880 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + 881 # . . call + 882 e8/call check-ints-equal/disp32 + 883 # . . discard args + 884 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 885 # . epilog + 886 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 887 5d/pop-to-EBP + 888 c3/return + 889 + 890 test-emit-non-number-with-metadata: + 891 # . prolog + 892 55/push-EBP + 893 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 894 # setup + 895 # . clear-stream(_test-stream) + 896 # . . push args + 897 68/push _test-stream/imm32 + 898 # . . call + 899 e8/call clear-stream/disp32 + 900 # . . discard args + 901 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 902 # . clear-stream(_test-buffered-file+4) + 903 # . . push args + 904 b8/copy-to-EAX _test-buffered-file/imm32 + 905 05/add-to-EAX 4/imm32 + 906 50/push-EAX + 907 # . . call + 908 e8/call clear-stream/disp32 + 909 # . . discard args + 910 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 911 # var slice/ECX = "xyz/" + 912 68/push _test-slice-non-number-word-metadata-end/imm32/end + 913 68/push _test-slice-non-number-word/imm32/start + 914 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 915 # emit(_test-buffered-file, slice, 2) + 916 # . . push args + 917 68/push 2/imm32 + 918 51/push-ECX + 919 68/push _test-buffered-file/imm32 + 920 # . . call + 921 e8/call emit/disp32 + 922 # . . discard args + 923 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 924 # flush(_test-buffered-file) + 925 # . . push args + 926 68/push _test-buffered-file/imm32 + 927 # . . call + 928 e8/call flush/disp32 + 929 # . . discard args + 930 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 931 # check(_test-stream->data == 'xyz/') + 932 # . check-ints-equal(_test-stream->data[0..3], 'xyz/', msg) + 933 # . . push args + 934 68/push "F - test-emit-non-number-with-metadata"/imm32 + 935 68/push 0x2f7a7978/imm32 + 936 # . . push *_test-stream->data + 937 b8/copy-to-EAX _test-stream/imm32 + 938 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + 939 # . . call + 940 e8/call check-ints-equal/disp32 + 941 # . . discard args + 942 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 943 # . epilog + 944 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 945 5d/pop-to-EBP + 946 c3/return + 947 + 948 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte + 949 emit-hex: # out : (address buffered-file), n : int, width : int + 950 # . prolog + 951 55/push-EBP + 952 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 953 # . save registers + 954 50/push-EAX + 955 51/push-ECX + 956 52/push-EDX + 957 53/push-EBX + 958 57/push-EDI + 959 # EDI = out + 960 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI + 961 # EBX = n + 962 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX + 963 # EDX = width + 964 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX + 965 # var curr/ECX = 0 + 966 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX + 967 $emit-hex:loop: + 968 # if (curr >= width) break + 969 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX and EDX + 970 7d/jump-if-greater-or-equal $emit-hex:end/disp8 + 971 # print-byte(out, EBX) + 972 # . . push args + 973 53/push-EBX + 974 57/push-EDI + 975 # . . call + 976 e8/call print-byte/disp32 + 977 # . . discard args + 978 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 979 # write-byte(out, ' ') + 980 # . . push args + 981 68/push 0x20/imm32/space + 982 57/push-EDI + 983 # . . call + 984 e8/call write-byte/disp32 + 985 # . . discard args + 986 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 987 # EBX = EBX >> 8 + 988 c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/EBX . . . . . 8/imm8 # shift EBX right by 8 bits, while padding zeroes + 989 # ++curr + 990 41/increment-ECX + 991 eb/jump $emit-hex:loop/disp8 + 992 $emit-hex:end: + 993 # . restore registers + 994 5f/pop-to-EDI + 995 5b/pop-to-EBX + 996 5a/pop-to-EAX + 997 59/pop-to-ECX + 998 58/pop-to-EAX + 999 # . epilog +1000 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1001 5d/pop-to-EBP +1002 c3/return +1003 +1004 test-emit-hex-single-byte: +1005 # setup +1006 # . clear-stream(_test-stream) +1007 # . . push args +1008 68/push _test-stream/imm32 +1009 # . . call +1010 e8/call clear-stream/disp32 +1011 # . . discard args +1012 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1013 # . clear-stream(_test-buffered-file+4) +1014 # . . push args +1015 b8/copy-to-EAX _test-buffered-file/imm32 +1016 05/add-to-EAX 4/imm32 +1017 50/push-EAX +1018 # . . call +1019 e8/call clear-stream/disp32 +1020 # . . discard args +1021 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1022 # emit-hex(_test-buffered-file, 0xab, 1) +1023 # . . push args +1024 68/push 1/imm32 +1025 68/push 0xab/imm32 +1026 68/push _test-buffered-file/imm32 +1027 # . . call +1028 e8/call emit-hex/disp32 +1029 # . . discard args +1030 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1031 # flush(_test-buffered-file) +1032 # . . push args +1033 68/push _test-buffered-file/imm32 +1034 # . . call +1035 e8/call flush/disp32 +1036 # . . discard args +1037 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1038 # check-ints-equal(*_test-stream->data, 'ab ', msg) +1039 # . . push args +1040 68/push "F - test-emit-hex-single-byte"/imm32 +1041 68/push 0x206261/imm32 +1042 # . . push *_test-stream->data +1043 b8/copy-to-EAX _test-stream/imm32 +1044 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +1045 # . . call +1046 e8/call check-ints-equal/disp32 +1047 # . . discard args +1048 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1049 # . end +1050 c3/return +1051 +1052 test-emit-hex-multiple-byte: +1053 # setup +1054 # . clear-stream(_test-stream) +1055 # . . push args +1056 68/push _test-stream/imm32 +1057 # . . call +1058 e8/call clear-stream/disp32 +1059 # . . discard args +1060 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1061 # . clear-stream(_test-buffered-file+4) +1062 # . . push args +1063 b8/copy-to-EAX _test-buffered-file/imm32 +1064 05/add-to-EAX 4/imm32 +1065 50/push-EAX +1066 # . . call +1067 e8/call clear-stream/disp32 +1068 # . . discard args +1069 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1070 # emit-hex(_test-buffered-file, 0x1234, 2) +1071 # . . push args +1072 68/push 2/imm32 +1073 68/push 0x1234/imm32 +1074 68/push _test-buffered-file/imm32 +1075 # . . call +1076 e8/call emit-hex/disp32 +1077 # . . discard args +1078 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1079 # flush(_test-buffered-file) +1080 # . . push args +1081 68/push _test-buffered-file/imm32 +1082 # . . call +1083 e8/call flush/disp32 +1084 # . . discard args +1085 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1086 # check(_test-stream->data == '34 12 ') +1087 # . check-ints-equal(_test-stream->data[0..3], '34 1', msg) +1088 # . . push args +1089 68/push "F - test-emit-hex-multiple-byte/1"/imm32 +1090 68/push 0x31203433/imm32 +1091 # . . push *_test-stream->data +1092 b8/copy-to-EAX _test-stream/imm32 +1093 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +1094 # . . call +1095 e8/call check-ints-equal/disp32 +1096 # . . discard args +1097 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1098 # . check-ints-equal(_test-stream->data[4..7], '2 ', msg) +1099 # . . push args +1100 68/push "F - test-emit-hex-multiple-byte/2"/imm32 +1101 68/push 0x2032/imm32 +1102 # . . push *_test-stream->data +1103 b8/copy-to-EAX _test-stream/imm32 +1104 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0x10/disp8 . # push *(EAX+16) +1105 # . . call +1106 e8/call check-ints-equal/disp32 +1107 # . . discard args +1108 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1109 # . end +1110 c3/return +1111 +1112 test-emit-hex-zero-pad: +1113 # setup +1114 # . clear-stream(_test-stream) +1115 # . . push args +1116 68/push _test-stream/imm32 +1117 # . . call +1118 e8/call clear-stream/disp32 +1119 # . . discard args +1120 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1121 # . clear-stream(_test-buffered-file+4) +1122 # . . push args +1123 b8/copy-to-EAX _test-buffered-file/imm32 +1124 05/add-to-EAX 4/imm32 +1125 50/push-EAX +1126 # . . call +1127 e8/call clear-stream/disp32 +1128 # . . discard args +1129 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1130 # emit-hex(_test-buffered-file, 0xab, 2) +1131 # . . push args +1132 68/push 2/imm32 +1133 68/push 0xab/imm32 +1134 68/push _test-buffered-file/imm32 +1135 # . . call +1136 e8/call emit-hex/disp32 +1137 # . . discard args +1138 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1139 # flush(_test-buffered-file) +1140 # . . push args +1141 68/push _test-buffered-file/imm32 +1142 # . . call +1143 e8/call flush/disp32 +1144 # . . discard args +1145 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1146 # check(_test-stream->data == '00 ab') +1147 # . check-ints-equal(*_test-stream->data, 'ab 0', msg) +1148 # . . push args +1149 68/push "F - test-emit-hex-zero-pad/1"/imm32 +1150 68/push 0x30206261/imm32 +1151 # . . push *_test-stream->data +1152 b8/copy-to-EAX _test-stream/imm32 +1153 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +1154 # . . call +1155 e8/call check-ints-equal/disp32 +1156 # . . discard args +1157 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1158 # . check-ints-equal(*_test-stream->data[1], '0 ', msg) +1159 # . . push args +1160 68/push "F - test-emit-hex-zero-pad/2"/imm32 +1161 68/push 0x2030/imm32 +1162 # . . push *_test-stream->data +1163 b8/copy-to-EAX _test-stream/imm32 +1164 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0x10/disp8 . # push *(EAX+16) +1165 # . . call +1166 e8/call check-ints-equal/disp32 +1167 # . . discard args +1168 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1169 # . end +1170 c3/return +1171 +1172 test-emit-hex-negative: +1173 # setup +1174 # . clear-stream(_test-stream) +1175 # . . push args +1176 68/push _test-stream/imm32 +1177 # . . call +1178 e8/call clear-stream/disp32 +1179 # . . discard args +1180 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1181 # . clear-stream(_test-buffered-file+4) +1182 # . . push args +1183 b8/copy-to-EAX _test-buffered-file/imm32 +1184 05/add-to-EAX 4/imm32 +1185 50/push-EAX +1186 # . . call +1187 e8/call clear-stream/disp32 +1188 # . . discard args +1189 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1190 # emit-hex(_test-buffered-file, -1, 2) +1191 # . . push args +1192 68/push 2/imm32 +1193 68/push -1/imm32 +1194 68/push _test-buffered-file/imm32 +1195 # . . call +1196 e8/call emit-hex/disp32 +1197 # . . discard args +1198 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1199 # flush(_test-buffered-file) +1200 # . . push args +1201 68/push _test-buffered-file/imm32 +1202 # . . call +1203 e8/call flush/disp32 +1204 # . . discard args +1205 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1206 # check(_test-stream->data == 'ff ff ') +1207 # . check-ints-equal(_test-stream->data[0..3], 'ff f', msg) +1208 # . . push args +1209 68/push "F - test-emit-hex-negative/1"/imm32 +1210 68/push 0x66206666/imm32 +1211 # . . push *_test-stream->data +1212 b8/copy-to-EAX _test-stream/imm32 +1213 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) +1214 # . . call +1215 e8/call check-ints-equal/disp32 +1216 # . . discard args +1217 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1218 # . check-ints-equal(_test-stream->data[4..7], 'f ', msg) +1219 # . . push args +1220 68/push "F - test-emit-hex-negative/2"/imm32 +1221 68/push 0x2066/imm32 +1222 # . . push *_test-stream->data +1223 b8/copy-to-EAX _test-stream/imm32 +1224 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0x10/disp8 . # push *(EAX+16) +1225 # . . call +1226 e8/call check-ints-equal/disp32 +1227 # . . discard args +1228 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1229 # . end +1230 c3/return +1231 +1232 == data +1233 +1234 _test-slice-negative-two: +1235 2d/- 32/2 +1236 _test-slice-negative-two-end: +1237 2f/slash 66/f 6f/o 6f/o +1238 _test-slice-negative-two-metadata-end: +1239 +1240 _test-slice-non-number-word: +1241 78/x 79/y 7a/z +1242 _test-slice-non-number-word-end: +1243 2f/slash +1244 _test-slice-non-number-word-metadata-end: +1245 +1246 # . . vim:nowrap:textwidth=0