From 09492d3867b5da6d0b2f21b139d097cd481a8fa1 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Wed, 10 Apr 2019 21:18:55 -0700 Subject: 5077 --- html/subx/052kernel-string-equal.subx.html | 4 +- html/subx/056trace.subx.html | 25 +- html/subx/058stream-equal.subx.html | 2 +- html/subx/060read.subx.html | 571 +- html/subx/061read-byte.subx.html | 2 +- html/subx/065hex.subx.html | 12 +- html/subx/069allocate.subx.html | 2 +- html/subx/071read-line.subx.html | 16 +- html/subx/072slice.subx.html | 1894 +++--- html/subx/073next-token.subx.html | 12 +- html/subx/apps/assort.subx.html | 1217 ++++ html/subx/apps/crenshaw2-1.subx.html | 19 +- html/subx/apps/crenshaw2-1b.subx.html | 215 +- html/subx/apps/factorial.subx.html | 2 +- html/subx/apps/handle.subx.html | 2 +- html/subx/apps/hex.subx.html | 111 +- html/subx/apps/pack.subx.html | 9080 ++++++++++++++++++---------- html/subx/apps/subx-common.subx.html | 314 + 18 files changed, 9025 insertions(+), 4475 deletions(-) create mode 100644 html/subx/apps/assort.subx.html create mode 100644 html/subx/apps/subx-common.subx.html (limited to 'html/subx') diff --git a/html/subx/052kernel-string-equal.subx.html b/html/subx/052kernel-string-equal.subx.html index ab05597e..01a65cd8 100644 --- a/html/subx/052kernel-string-equal.subx.html +++ b/html/subx/052kernel-string-equal.subx.html @@ -142,7 +142,7 @@ if ('onhashchange' in window) { 80 # c2 = *s2 81 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL 82 # if (c1 == 0) return false - 83 3d/compare-EAX 0/imm32 + 83 3d/compare-EAX-and 0/imm32 84 74/jump-if-equal $kernel-string-equal?:false/disp8 85 # if (c1 != c2) return false 86 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX @@ -157,7 +157,7 @@ if ('onhashchange' in window) { 95 $kernel-string-equal?:break: 96 # return *s1 == 0 97 8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 0/r32/AL . . # copy byte at *EDI to AL - 98 3d/compare-EAX 0/imm32 + 98 3d/compare-EAX-and 0/imm32 99 75/jump-if-not-equal $kernel-string-equal?:false/disp8 100 $kernel-string-equal?:true: 101 b8/copy-to-EAX 1/imm32 diff --git a/html/subx/056trace.subx.html b/html/subx/056trace.subx.html index 2b0b7596..37f7a043 100644 --- a/html/subx/056trace.subx.html +++ b/html/subx/056trace.subx.html @@ -154,7 +154,7 @@ if ('onhashchange' in window) { 92 # . . discard args 93 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 94 # if (EAX == 0) return - 95 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EDX + 95 3d/compare-EAX-and 0/imm32 96 74/jump-if-equal $trace:end/disp8 97 # t->write += EAX 98 01/add 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # add EAX to *EDI @@ -334,7 +334,7 @@ if ('onhashchange' in window) { 272 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP 273 # . save registers 274 51/push-ECX -275 # _append-4(out, outend, &s->data[0], &s->data[s->length]) -> num_bytes_appended/EAX +275 # EAX = _append-4(out, outend, &s->data[0], &s->data[s->length]) 276 # . . push &s->data[s->length] 277 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX 278 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX @@ -384,9 +384,9 @@ if ('onhashchange' in window) { 322 # if (in >= inend) break 323 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX 324 7d/jump-if-greater-or-equal $_append-4:end/disp8 -325 # if (out >= outend) break # for now silently ignore filled up buffer +325 # if (out >= outend) abort # just to catch test failures fast 326 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX -327 7d/jump-if-greater-or-equal $_append-4:end/disp8 +327 7d/jump-if-greater-or-equal $_append-4:abort/disp8 328 # *out = *in 329 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL 330 88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI @@ -409,7 +409,22 @@ if ('onhashchange' in window) { 347 5d/pop-to-EBP 348 c3/return 349 -350 # . . vim:nowrap:textwidth=0 +350 $_append-4:abort: +351 # . _write(2/stderr, error) +352 # . . push args +353 68/push "stream overflow"/imm32 +354 68/push 2/imm32/stderr +355 # . . call +356 e8/call _write/disp32 +357 # . . discard args +358 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +359 # . syscall(exit, 1) +360 bb/copy-to-EBX 1/imm32 +361 b8/copy-to-EAX 1/imm32/exit +362 cd/syscall 0x80/imm8 +363 # never gets here +364 +365 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/058stream-equal.subx.html b/html/subx/058stream-equal.subx.html index 4737a3bd..e947be92 100644 --- a/html/subx/058stream-equal.subx.html +++ b/html/subx/058stream-equal.subx.html @@ -355,7 +355,7 @@ if ('onhashchange' in window) { 294 # AL = *(f->data + f->read) 295 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 296 # if (EAX == '\n') break -297 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xa/imm32/newline # compare EAX +297 3d/compare-EAX-and 0xa/imm32/newline 298 74/jump-if-equal $next-stream-line-equal?:break/disp8 299 # if (currs >= s->length) return false 300 3b/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare EDX with *EDI diff --git a/html/subx/060read.subx.html b/html/subx/060read.subx.html index 68fa8994..dca5f163 100644 --- a/html/subx/060read.subx.html +++ b/html/subx/060read.subx.html @@ -117,7 +117,7 @@ if ('onhashchange' in window) { 56 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) 57 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 58 # . . call - 59 e8/call _read/disp32 + 59 e8/call _read/disp32 60 # . . discard args 61 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 62 # return @@ -131,7 +131,7 @@ if ('onhashchange' in window) { 70 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI 71 # EDI = s 72 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to ESI - 73 # EAX = _append-4(out = &s->data[s->write], outend = &s->data[s->length], + 73 # EAX = _buffer-4(out = &s->data[s->write], outend = &s->data[s->length], 74 # in = &f->data[f->read], inend = &f->data[f->write]) 75 # . . push &f->data[f->write] 76 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX @@ -150,7 +150,7 @@ if ('onhashchange' in window) { 89 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 0/r32/EAX 0xc/disp8 . # copy EDI+EAX+12 to EAX 90 50/push-EAX 91 # . . call - 92 e8/call _append-4/disp32 + 92 e8/call _buffer-4/disp32 93 # . . discard args 94 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP 95 # s->write += EAX @@ -168,252 +168,337 @@ if ('onhashchange' in window) { 107 108 # - helpers 109 -110 # idea: a clear-if-empty method on streams that clears only if f->read == f->write -111 # Unclear how I'd use it, though. Callers seem to need the check anyway. -112 # Maybe a better helper would be 'empty-stream?' -113 -114 _read: # fd : int, s : (address stream) -> num-bytes-read/EAX -115 # . prolog -116 55/push-EBP -117 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -118 # . save registers -119 51/push-ECX -120 52/push-EDX -121 53/push-EBX -122 56/push-ESI -123 # ESI = s -124 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI -125 # EAX = s->write -126 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX -127 # EDX = s->length -128 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 8/disp8 . # copy *(ESI+8) to EDX -129 # syscall(read, fd, &s->data[s->write], s->length - s->write) -130 # . . fd : EBX -131 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX -132 # . . data : ECX = &s->data[s->write] -133 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 1/r32/ECX 0xc/disp8 . # copy ESI+EAX+12 to ECX -134 # . . size : EDX = s->length - s->write -135 29/subtract 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # subtract EAX from EDX -136 # . . syscall -137 b8/copy-to-EAX 3/imm32/read -138 cd/syscall 0x80/imm8 -139 # add the result EAX to s->write -140 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI -141 $_read:end: -142 # . restore registers -143 5e/pop-to-ESI -144 5b/pop-to-EBX -145 5a/pop-to-EDX -146 59/pop-to-ECX -147 # . epilog -148 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -149 5d/pop-to-EBP -150 c3/return -151 -152 # Two options: -153 # 1 (what we have above): -154 # ECX = s -155 # EAX = s->write -156 # EDX = s->length -157 # # syscall -158 # ECX = lea ECX+EAX+12 -159 # EDX = sub EDX EAX -160 # -161 # 2: -162 # ECX = s -163 # EDX = s->length -164 # ECX = &s->data -165 # # syscall -166 # ECX = add ECX, s->write -167 # EDX = sub EDX, s->write -168 # -169 # Not much to choose between the two? Option 2 performs a duplicate load to -170 # use one less register, but doesn't increase the amount of spilling (ECX -171 # and EDX must be used, and EAX must be clobbered anyway). -172 -173 # - tests -174 -175 test-read-single: -176 # - write a single character into _test-stream, then read from its buffered-file -177 # clear-stream(_test-stream) -178 # . . push args -179 68/push _test-stream/imm32 -180 # . . call -181 e8/call clear-stream/disp32 -182 # . . discard args -183 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -184 # clear-stream(_test-tmp-stream) -185 # . . push args -186 68/push _test-tmp-stream/imm32 -187 # . . call -188 e8/call clear-stream/disp32 -189 # . . discard args -190 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -191 # write(_test-stream, "Ab") -192 # . . push args -193 68/push "Ab"/imm32 -194 68/push _test-stream/imm32 -195 # . . call -196 e8/call write/disp32 -197 # . . discard args -198 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -199 # read(_test-stream, _test-tmp-stream) -200 # . . push args -201 68/push _test-tmp-stream/imm32 -202 68/push _test-stream/imm32 -203 # . . call -204 e8/call read/disp32 -205 # . . discard args -206 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -207 # check-ints-equal(EAX, 2, msg) -208 # . . push args -209 68/push "F - test-read-single: return EAX"/imm32 -210 68/push 2/imm32 -211 50/push-EAX -212 # . . call -213 e8/call check-ints-equal/disp32 -214 # . . discard args -215 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -216 # check-stream-equal(_test-tmp-stream, "Ab", msg) -217 # . . push args -218 68/push "F - test-read-single"/imm32 -219 68/push "Ab"/imm32 -220 68/push _test-tmp-stream/imm32 -221 # . . call -222 e8/call check-stream-equal/disp32 -223 # . . discard args -224 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -225 # end -226 c3/return -227 -228 test-read-is-stateful: -229 # - make two consecutive reads, check that their results are appended -230 # clear-stream(_test-stream) -231 # . . push args -232 68/push _test-stream/imm32 -233 # . . call -234 e8/call clear-stream/disp32 -235 # . . discard args -236 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -237 # clear-stream(_test-tmp-stream) -238 # . . push args -239 68/push _test-tmp-stream/imm32 -240 # . . call -241 e8/call clear-stream/disp32 -242 # . . discard args -243 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -244 # write(_test-stream, "C") -245 # . . push args -246 68/push "C"/imm32 -247 68/push _test-stream/imm32 -248 # . . call -249 e8/call write/disp32 -250 # . . discard args -251 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -252 # read(_test-stream, _test-tmp-stream) -253 # . . push args -254 68/push _test-tmp-stream/imm32 -255 68/push _test-stream/imm32 -256 # . . call -257 e8/call read/disp32 -258 # . . discard args -259 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -260 # write(_test-stream, "D") -261 # . . push args -262 68/push "D"/imm32 -263 68/push _test-stream/imm32 -264 # . . call -265 e8/call write/disp32 -266 # . . discard args -267 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -268 # read(_test-stream, _test-tmp-stream) -269 # . . push args -270 68/push _test-tmp-stream/imm32 -271 68/push _test-stream/imm32 +110 # '_buffer' is like '_append', but silently stops instead of aborting when it runs out of space +111 +112 # 3-argument variant of _buffer +113 _buffer-3: # out : address, outend : address, s : (array byte) -> num_bytes_buffered/EAX +114 # . prolog +115 55/push-EBP +116 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +117 # . save registers +118 51/push-ECX +119 # EAX = _buffer-4(out, outend, &s->data[0], &s->data[s->length]) +120 # . . push &s->data[s->length] +121 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX +122 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +123 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 +124 51/push-ECX +125 # . . push &s->data[0] +126 8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 4/disp8 . # copy EAX+4 to ECX +127 51/push-ECX +128 # . . push outend +129 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) +130 # . . push out +131 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +132 # . . call +133 e8/call _buffer-4/disp32 +134 # . . discard args +135 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +136 $_buffer-3:end: +137 # . restore registers +138 59/pop-to-ECX +139 # . epilog +140 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +141 5d/pop-to-EBP +142 c3/return +143 +144 # 4-argument variant of _buffer +145 _buffer-4: # out : address, outend : address, in : address, inend : address -> num_bytes_buffered/EAX +146 # . prolog +147 55/push-EBP +148 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +149 # . save registers +150 51/push-ECX +151 52/push-EDX +152 53/push-EBX +153 56/push-ESI +154 57/push-EDI +155 # EAX/num_bytes_buffered = 0 +156 b8/copy-to-EAX 0/imm32 +157 # EDI = out +158 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI +159 # EDX = outend +160 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX +161 # ESI = in +162 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0x10/disp8 . # copy *(EBP+16) to ESI +163 # ECX = inend +164 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x14/disp8 . # copy *(EBP+20) to ECX +165 $_buffer-4:loop: +166 # if (in >= inend) break +167 39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX +168 7d/jump-if-greater-or-equal $_buffer-4:end/disp8 +169 # if (out >= outend) break # for now silently ignore filled up buffer +170 39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX +171 7d/jump-if-greater-or-equal $_buffer-4:end/disp8 +172 # *out = *in +173 8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL +174 88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI +175 # ++num_bytes_buffered +176 40/increment-EAX +177 # ++in +178 46/increment-ESI +179 # ++out +180 47/increment-EDI +181 eb/jump $_buffer-4:loop/disp8 +182 $_buffer-4:end: +183 # . restore registers +184 5f/pop-to-EDI +185 5e/pop-to-ESI +186 5b/pop-to-EBX +187 5a/pop-to-EDX +188 59/pop-to-ECX +189 # . epilog +190 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +191 5d/pop-to-EBP +192 c3/return +193 +194 +195 # idea: a clear-if-empty method on streams that clears only if f->read == f->write +196 # Unclear how I'd use it, though. Callers seem to need the check anyway. +197 # Maybe a better helper would be 'empty-stream?' +198 +199 _read: # fd : int, s : (address stream) -> num-bytes-read/EAX +200 # . prolog +201 55/push-EBP +202 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +203 # . save registers +204 51/push-ECX +205 52/push-EDX +206 53/push-EBX +207 56/push-ESI +208 # ESI = s +209 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI +210 # EAX = s->write +211 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX +212 # EDX = s->length +213 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 8/disp8 . # copy *(ESI+8) to EDX +214 # syscall(read, fd, &s->data[s->write], s->length - s->write) +215 # . . fd : EBX +216 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 8/disp8 . # copy *(EBP+8) to EBX +217 # . . data : ECX = &s->data[s->write] +218 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 1/r32/ECX 0xc/disp8 . # copy ESI+EAX+12 to ECX +219 # . . size : EDX = s->length - s->write +220 29/subtract 3/mod/direct 2/rm32/EDX . . . 0/r32/EAX . . # subtract EAX from EDX +221 # . . syscall +222 b8/copy-to-EAX 3/imm32/read +223 cd/syscall 0x80/imm8 +224 # add the result EAX to s->write +225 01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI +226 $_read:end: +227 # . restore registers +228 5e/pop-to-ESI +229 5b/pop-to-EBX +230 5a/pop-to-EDX +231 59/pop-to-ECX +232 # . epilog +233 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +234 5d/pop-to-EBP +235 c3/return +236 +237 # Two options: +238 # 1 (what we have above): +239 # ECX = s +240 # EAX = s->write +241 # EDX = s->length +242 # # syscall +243 # ECX = lea ECX+EAX+12 +244 # EDX = sub EDX EAX +245 # +246 # 2: +247 # ECX = s +248 # EDX = s->length +249 # ECX = &s->data +250 # # syscall +251 # ECX = add ECX, s->write +252 # EDX = sub EDX, s->write +253 # +254 # Not much to choose between the two? Option 2 performs a duplicate load to +255 # use one less register, but doesn't increase the amount of spilling (ECX +256 # and EDX must be used, and EAX must be clobbered anyway). +257 +258 # - tests +259 +260 test-read-single: +261 # - write a single character into _test-stream, then read from it +262 # clear-stream(_test-stream) +263 # . . push args +264 68/push _test-stream/imm32 +265 # . . call +266 e8/call clear-stream/disp32 +267 # . . discard args +268 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +269 # clear-stream(_test-tmp-stream) +270 # . . push args +271 68/push _test-tmp-stream/imm32 272 # . . call -273 e8/call read/disp32 +273 e8/call clear-stream/disp32 274 # . . discard args -275 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -276 # check-stream-equal(_test-tmp-stream, "CD", msg) +275 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +276 # write(_test-stream, "Ab") 277 # . . push args -278 68/push "F - test-read-is-stateful"/imm32 -279 68/push "CD"/imm32 -280 68/push _test-tmp-stream/imm32 -281 # . . call -282 e8/call check-stream-equal/disp32 -283 # . . discard args -284 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -285 # end -286 c3/return -287 -288 test-read-returns-0-on-end-of-file: -289 # - read after hitting end-of-file, check that result is 0 -290 # setup -291 # . clear-stream(_test-stream) -292 # . . push args -293 68/push _test-stream/imm32 -294 # . . call -295 e8/call clear-stream/disp32 -296 # . . discard args -297 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -298 # . clear-stream(_test-tmp-stream) -299 # . . push args -300 68/push _test-tmp-stream/imm32 -301 # . . call -302 e8/call clear-stream/disp32 -303 # . . discard args -304 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -305 # . write(_test-stream, "Ab") -306 # . . push args -307 68/push "Ab"/imm32 -308 68/push _test-stream/imm32 -309 # . . call -310 e8/call write/disp32 -311 # . . discard args -312 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -313 # first read gets to end-of-file -314 # . read(_test-stream, _test-tmp-stream) -315 # . . push args -316 68/push _test-tmp-stream/imm32 +278 68/push "Ab"/imm32 +279 68/push _test-stream/imm32 +280 # . . call +281 e8/call write/disp32 +282 # . . discard args +283 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +284 # EAX = read(_test-stream, _test-tmp-stream) +285 # . . push args +286 68/push _test-tmp-stream/imm32 +287 68/push _test-stream/imm32 +288 # . . call +289 e8/call read/disp32 +290 # . . discard args +291 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +292 # check-ints-equal(EAX, 2, msg) +293 # . . push args +294 68/push "F - test-read-single: return EAX"/imm32 +295 68/push 2/imm32 +296 50/push-EAX +297 # . . call +298 e8/call check-ints-equal/disp32 +299 # . . discard args +300 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +301 # check-stream-equal(_test-tmp-stream, "Ab", msg) +302 # . . push args +303 68/push "F - test-read-single"/imm32 +304 68/push "Ab"/imm32 +305 68/push _test-tmp-stream/imm32 +306 # . . call +307 e8/call check-stream-equal/disp32 +308 # . . discard args +309 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +310 # end +311 c3/return +312 +313 test-read-is-stateful: +314 # - make two consecutive reads, check that their results are appended +315 # clear-stream(_test-stream) +316 # . . push args 317 68/push _test-stream/imm32 318 # . . call -319 e8/call read/disp32 +319 e8/call clear-stream/disp32 320 # . . discard args -321 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -322 # second read -323 # . read(_test-stream, _test-tmp-stream) -324 # . . push args -325 68/push _test-tmp-stream/imm32 -326 68/push _test-stream/imm32 -327 # . . call -328 e8/call read/disp32 -329 # . . discard args -330 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -331 # check-ints-equal(EAX, 0, msg) -332 # . . push args -333 68/push "F - test-read-returns-0-on-end-of-file"/imm32 -334 68/push 0/imm32 -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 # end -341 c3/return -342 -343 == data -344 -345 _test-tmp-stream: -346 # current write index -347 0/imm32 -348 # current read index -349 0/imm32 -350 # length -351 8/imm32 -352 # data -353 00 00 00 00 00 00 00 00 # 8 bytes -354 -355 # . . vim:nowrap:textwidth=0 +321 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +322 # clear-stream(_test-tmp-stream) +323 # . . push args +324 68/push _test-tmp-stream/imm32 +325 # . . call +326 e8/call clear-stream/disp32 +327 # . . discard args +328 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +329 # write(_test-stream, "C") +330 # . . push args +331 68/push "C"/imm32 +332 68/push _test-stream/imm32 +333 # . . call +334 e8/call write/disp32 +335 # . . discard args +336 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +337 # read(_test-stream, _test-tmp-stream) +338 # . . push args +339 68/push _test-tmp-stream/imm32 +340 68/push _test-stream/imm32 +341 # . . call +342 e8/call read/disp32 +343 # . . discard args +344 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +345 # write(_test-stream, "D") +346 # . . push args +347 68/push "D"/imm32 +348 68/push _test-stream/imm32 +349 # . . call +350 e8/call write/disp32 +351 # . . discard args +352 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +353 # read(_test-stream, _test-tmp-stream) +354 # . . push args +355 68/push _test-tmp-stream/imm32 +356 68/push _test-stream/imm32 +357 # . . call +358 e8/call read/disp32 +359 # . . discard args +360 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +361 # check-stream-equal(_test-tmp-stream, "CD", msg) +362 # . . push args +363 68/push "F - test-read-is-stateful"/imm32 +364 68/push "CD"/imm32 +365 68/push _test-tmp-stream/imm32 +366 # . . call +367 e8/call check-stream-equal/disp32 +368 # . . discard args +369 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +370 # end +371 c3/return +372 +373 test-read-returns-0-on-end-of-file: +374 # - read after hitting end-of-file, check that result is 0 +375 # setup +376 # . clear-stream(_test-stream) +377 # . . push args +378 68/push _test-stream/imm32 +379 # . . call +380 e8/call clear-stream/disp32 +381 # . . discard args +382 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +383 # . clear-stream(_test-tmp-stream) +384 # . . push args +385 68/push _test-tmp-stream/imm32 +386 # . . call +387 e8/call clear-stream/disp32 +388 # . . discard args +389 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +390 # . write(_test-stream, "Ab") +391 # . . push args +392 68/push "Ab"/imm32 +393 68/push _test-stream/imm32 +394 # . . call +395 e8/call write/disp32 +396 # . . discard args +397 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +398 # first read gets to end-of-file +399 # . read(_test-stream, _test-tmp-stream) +400 # . . push args +401 68/push _test-tmp-stream/imm32 +402 68/push _test-stream/imm32 +403 # . . call +404 e8/call read/disp32 +405 # . . discard args +406 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +407 # second read +408 # . read(_test-stream, _test-tmp-stream) +409 # . . push args +410 68/push _test-tmp-stream/imm32 +411 68/push _test-stream/imm32 +412 # . . call +413 e8/call read/disp32 +414 # . . discard args +415 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +416 # check-ints-equal(EAX, 0, msg) +417 # . . push args +418 68/push "F - test-read-returns-0-on-end-of-file"/imm32 +419 68/push 0/imm32 +420 50/push-EAX +421 # . . call +422 e8/call check-ints-equal/disp32 +423 # . . discard args +424 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +425 # end +426 c3/return +427 +428 == data +429 +430 _test-tmp-stream: +431 # current write index +432 0/imm32 +433 # current read index +434 0/imm32 +435 # length +436 8/imm32 +437 # data +438 00 00 00 00 00 00 00 00 # 8 bytes +439 +440 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/061read-byte.subx.html b/html/subx/061read-byte.subx.html index 4de39db1..fa9a841d 100644 --- a/html/subx/061read-byte.subx.html +++ b/html/subx/061read-byte.subx.html @@ -137,7 +137,7 @@ if ('onhashchange' in window) { 74 # . . discard args 75 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 76 # if (EAX == 0) return 0xffffffff - 77 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX + 77 3d/compare-EAX-and 0/imm32 78 75/jump-if-not-equal $read-byte:from-stream/disp8 79 b8/copy-to-EAX 0xffffffff/imm32/Eof 80 eb/jump $read-byte:end/disp8 diff --git a/html/subx/065hex.subx.html b/html/subx/065hex.subx.html index 5cef0d9c..19119cac 100644 --- a/html/subx/065hex.subx.html +++ b/html/subx/065hex.subx.html @@ -125,7 +125,7 @@ if ('onhashchange' in window) { 65 # . . discard args 66 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 67 # if (EAX == false) return false - 68 3d/compare-with-EAX 0/imm32 + 68 3d/compare-EAX-and 0/imm32 69 74/jump-if-equal $is-hex-int?:end/disp8 70 # ++curr 71 41/increment-ECX @@ -399,7 +399,7 @@ if ('onhashchange' in window) { 339 # . if (*curr == '-') negate = true 340 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX 341 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -342 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0x2d/imm32/- # compare EAX +342 3d/compare-EAX-and 0x2d/imm32/- 343 75/jump-if-not-equal $parse-hex-int:initial-0/disp8 344 # . ++curr 345 41/increment-ECX @@ -409,7 +409,7 @@ if ('onhashchange' in window) { 349 # skip past leading '0x' 350 # . if (*curr != '0') jump to loop 351 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -352 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0x30/imm32/0 # compare EAX +352 3d/compare-EAX-and 0x30/imm32/0 353 75/jump-if-not-equal $parse-hex-int:loop/disp8 354 # . ++curr 355 41/increment-ECX @@ -420,7 +420,7 @@ if ('onhashchange' in window) { 360 # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again 361 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX 362 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -363 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0x78/imm32/x # compare EAX +363 3d/compare-EAX-and 0x78/imm32/x 364 75/jump-if-not-equal $parse-hex-int:loop/disp8 365 # . ++curr 366 41/increment-ECX @@ -792,7 +792,7 @@ if ('onhashchange' in window) { 732 from-hex-char: # in/EAX : byte -> out/EAX : num 733 # no error checking; accepts argument in EAX 734 # if (EAX <= '9') return EAX - '0' -735 3d/compare-EAX 0x39/imm32/9 +735 3d/compare-EAX-with 0x39/imm32/9 736 7f/jump-if-greater $from-hex-char:else/disp8 737 2d/subtract-from-EAX 0x30/imm32/0 738 c3/return @@ -804,7 +804,7 @@ if ('onhashchange' in window) { 744 to-hex-char: # in/EAX : nibble -> out/EAX : byte 745 # no error checking; accepts argument in EAX 746 # if (EAX <= 9) return EAX + '0' -747 3d/compare-EAX 0x9/imm32/9 +747 3d/compare-EAX-with 0x9/imm32/9 748 7f/jump-if-greater $to-hex-char:else/disp8 749 05/add-to-EAX 0x30/imm32/0 750 c3/return diff --git a/html/subx/069allocate.subx.html b/html/subx/069allocate.subx.html index 21f6118e..2662cd0d 100644 --- a/html/subx/069allocate.subx.html +++ b/html/subx/069allocate.subx.html @@ -218,7 +218,7 @@ if ('onhashchange' in window) { 158 # . . discard args 159 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 160 # if (EAX == 0) abort -161 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX +161 3d/compare-EAX-and 0/imm32 162 74/jump-if-equal $allocate-region:abort/disp8 163 # earmark 8 bytes at the start for a new allocation descriptor 164 # . *EAX = EAX + 8 diff --git a/html/subx/071read-line.subx.html b/html/subx/071read-line.subx.html index 557b130a..8d334d9c 100644 --- a/html/subx/071read-line.subx.html +++ b/html/subx/071read-line.subx.html @@ -131,7 +131,7 @@ if ('onhashchange' in window) { 69 # if (f->write == 0) break 70 # since f->read was initially 0, EAX is the same as f->write 71 # . if (EAX == 0) return true - 72 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX + 72 3d/compare-EAX-and 0/imm32 73 74/jump-if-equal $read-line:end/disp8 74 $read-line:from-stream: 75 # AL = f->data[f->read] @@ -144,7 +144,7 @@ if ('onhashchange' in window) { 82 # ++s->write 83 42/increment-EDX 84 # if (AL == '\n') return - 85 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xa/imm32 # compare EAX + 85 3d/compare-EAX-and 0xa/imm32 86 75/jump-if-not-equal $read-line:loop/disp8 87 $read-line:end: 88 # save f->read @@ -206,7 +206,7 @@ if ('onhashchange' in window) { 144 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 145 # . clear-stream(_test-tmp-stream) 146 # . . push args -147 68/push _test-tmp-stream/imm32 +147 68/push _test-tmp-stream/imm32 148 # . . call 149 e8/call clear-stream/disp32 150 # . . discard args @@ -239,7 +239,7 @@ if ('onhashchange' in window) { 177 # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream 178 # . EAX = read-line(_test-buffered-file, _test-tmp-stream) 179 # . . push args -180 68/push _test-tmp-stream/imm32 +180 68/push _test-tmp-stream/imm32 181 68/push _test-buffered-file/imm32 182 # . . call 183 e8/call read-line/disp32 @@ -249,7 +249,7 @@ if ('onhashchange' in window) { 187 # . . push args 188 68/push "F - test-read-line"/imm32 189 68/push "ab"/imm32 -190 68/push _test-tmp-stream/imm32 +190 68/push _test-tmp-stream/imm32 191 # . . call 192 e8/call check-next-stream-line-equal/disp32 193 # . . discard args @@ -277,7 +277,7 @@ if ('onhashchange' in window) { 215 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 216 # . clear-stream(_test-tmp-stream) 217 # . . push args -218 68/push _test-tmp-stream/imm32 +218 68/push _test-tmp-stream/imm32 219 # . . call 220 e8/call clear-stream/disp32 221 # . . discard args @@ -293,7 +293,7 @@ if ('onhashchange' in window) { 231 # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream 232 # . EAX = read-line(_test-buffered-file, _test-tmp-stream) 233 # . . push args -234 68/push _test-tmp-stream/imm32 +234 68/push _test-tmp-stream/imm32 235 68/push _test-buffered-file/imm32 236 # . . call 237 e8/call read-line/disp32 @@ -303,7 +303,7 @@ if ('onhashchange' in window) { 241 # . . push args 242 68/push "F - test-read-line-reads-final-line-until-Eof"/imm32 243 68/push "cd"/imm32 -244 68/push _test-tmp-stream/imm32 +244 68/push _test-tmp-stream/imm32 245 # . . call 246 e8/call check-stream-equal/disp32 247 # . . discard args diff --git a/html/subx/072slice.subx.html b/html/subx/072slice.subx.html index 95b18d2a..5bc4ae43 100644 --- a/html/subx/072slice.subx.html +++ b/html/subx/072slice.subx.html @@ -16,14 +16,16 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } .Constant { color: #008787; } +.subxMinorFunction { color: #875f5f; } .LineNr { } .subxS1Comment { color: #0000af; } .CommentedCode { color: #8a8a8a; } .subxFunction { color: #af5f00; text-decoration: underline; } .subxTest { color: #5f8700; } -.subxMinorFunction { color: #875f5f; } +.Folded { color: #080808; background-color: #949494; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxS2Comment { color: #8a8a8a; } +.SpecialChar { color: #d70000; } .subxH1Comment { color: #005faf; text-decoration: underline; } --> @@ -60,929 +62,973 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/subx/072slice.subx
-  1 # new data structure: a slice is an open interval of addresses [start, end)
-  2 # that includes 'start' but not 'end'
-  3 
-  4 == code
-  5 #   instruction                     effective address                                                   register    displacement    immediate
-  6 # . op          subop               mod             rm32          base        index         scale       r32
-  7 # . 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
-  8 
-  9 #? Entry:  # run a single test, while debugging
- 10 #?     e8/call test-slice-starts-with-fails/disp32
- 11 #?     e8/call test-slice-starts-with-single-character/disp32
- 12 #?     # syscall(exit, Num-test-failures)
- 13 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 14 #?     b8/copy-to-EAX  1/imm32/exit
- 15 #?     cd/syscall  0x80/imm8
- 16 
- 17 slice-empty?:  # s : (address slice) -> EAX : boolean
- 18     # . prolog
- 19     55/push-EBP
- 20     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 21     # . save registers
- 22     51/push-ECX
- 23     # ECX = s
- 24     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
- 25     # if (s->start == s->end) return true
- 26     # . EAX = s->start
- 27     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
- 28     # . compare EAX and s->end
- 29     39/compare                      1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # compare EAX and *(ECX+4)
- 30     b8/copy-to-EAX  1/imm32/true
- 31     74/jump-if-equal  $slice-empty?:end/disp8
- 32     b8/copy-to-EAX  0/imm32/false
- 33 $slice-empty?:end:
- 34     # . restore registers
- 35     59/pop-to-ECX
- 36     # . epilog
- 37     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 38     5d/pop-to-EBP
- 39     c3/return
- 40 
- 41 test-slice-empty-true:
- 42     # . prolog
- 43     55/push-EBP
- 44     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 45     # var slice/ECX = {34, 34}
- 46     68/push  34/imm32/end
- 47     68/push  34/imm32/start
- 48     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 49     # slice-empty?(slice)
- 50     # . . push args
- 51     51/push-ECX
- 52     # . . call
- 53     e8/call  slice-empty?/disp32
- 54     # . . discard args
- 55     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 56     # check-ints-equal(EAX, 1, msg)
- 57     # . . push args
- 58     68/push  "F - test-slice-empty-true"/imm32
- 59     68/push  1/imm32
- 60     50/push-EAX
- 61     # . . call
- 62     e8/call  check-ints-equal/disp32
- 63     # . . discard args
- 64     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 65     # . epilog
- 66     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 67     5d/pop-to-EBP
- 68     c3/return
- 69 
- 70 test-slice-empty-false:
- 71     # . prolog
- 72     55/push-EBP
- 73     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 74     # var slice/ECX = {34, 23}
- 75     68/push  23/imm32/end
- 76     68/push  34/imm32/start
- 77     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 78     # slice-empty?(slice)
- 79     # . . push args
- 80     51/push-ECX
- 81     # . . call
- 82     e8/call  slice-empty?/disp32
- 83     # . . discard args
- 84     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 85     # check-ints-equal(EAX, 0, msg)
- 86     # . . push args
- 87     68/push  "F - test-slice-empty-false"/imm32
- 88     68/push  0/imm32
- 89     50/push-EAX
- 90     # . . call
- 91     e8/call  check-ints-equal/disp32
- 92     # . . discard args
- 93     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 94     # . epilog
- 95     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 96     5d/pop-to-EBP
- 97     c3/return
- 98 
- 99 slice-equal?:  # s : (address slice), p : (address string) -> EAX : boolean
-100     # pseudocode:
-101     #   currs = s->start
-102     #   maxs = s->end
-103     #   if (maxs - currs != p->length) return false
-104     #   currp = p->data
-105     #   while currs < maxs
-106     #     if (*currs != *currp) return false
-107     #     ++currs
-108     #     ++currp
-109     #   return true
-110     #
-111     # registers:
-112     #   currs: EDX
-113     #   maxs: ESI
-114     #   currp: EBX
-115     #   *currs: EAX
-116     #   *currp: ECX
-117     #
-118     # . prolog
-119     55/push-EBP
-120     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-121     # . save registers
-122     51/push-ECX
-123     52/push-EDX
-124     53/push-EBX
-125     56/push-ESI
-126     # ESI = s
-127     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-128     # currs/EDX = s->start
-129     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
-130     # maxs/ESI = s->end
-131     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
-132     # EBX = p
-133     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
-134     # EAX = maxs - currs
-135     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy ESI to EAX
-136     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # subtract EDX from EAX
-137     # if (EAX != p->length) return false
-138     39/compare                      0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # compare *EBX and EAX
-139     75/jump-if-not-equal  $slice-equal?:false/disp8
-140     # currp/EBX = p->data
-141     81          0/subop/add         3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm32           # add to EBX
-142     # EAX = ECX = 0
-143     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-144     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
-145 $slice-equal?:loop:
-146     # if (currs >= maxs) return true
-147     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX with ESI
-148     7d/jump-if-greater-or-equal  $slice-equal?:true/disp8
-149     # AL = *currp
-150     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
-151     # CL = *currs
-152     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
-153     # if (EAX != ECX) return false
-154     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX and ECX
-155     75/jump-if-not-equal  $slice-equal?:false/disp8
-156     # ++currp
-157     43/increment-EBX
-158     # ++currs
-159     42/increment-EDX
-160     eb/jump $slice-equal?:loop/disp8
-161 $slice-equal?:false:
-162     b8/copy-to-EAX  0/imm32
-163     eb/jump  $slice-equal?:end/disp8
-164 $slice-equal?:true:
-165     b8/copy-to-EAX  1/imm32
-166 $slice-equal?:end:
-167     # . restore registers
-168     5e/pop-to-ESI
-169     5b/pop-to-EBX
-170     5a/pop-to-EDX
-171     59/pop-to-ECX
-172     # . epilog
-173     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-174     5d/pop-to-EBP
-175     c3/return
-176 
-177 test-slice-equal:
-178     # - slice-equal?(slice("Abc"), "Abc") == 1
-179     # . prolog
-180     55/push-EBP
-181     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-182     # var slice/ECX
-183     68/push  _test-slice-data-3/imm32/end
-184     68/push  _test-slice-data-0/imm32/start
-185     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-186     # EAX = slice-equal?(ECX, "Abc")
-187     # . . push args
-188     68/push  "Abc"/imm32
-189     51/push-ECX
-190     # . . call
-191     e8/call  slice-equal?/disp32
-192     # . . discard args
-193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-194     # check-ints-equal(EAX, 1, msg)
-195     # . . push args
-196     68/push  "F - test-slice-equal"/imm32
-197     68/push  1/imm32
-198     50/push-EAX
-199     # . . call
-200     e8/call  check-ints-equal/disp32
-201     # . . discard args
-202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-203     # . epilog
-204     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-205     5d/pop-to-EBP
-206     c3/return
-207 
-208 test-slice-equal-false:
-209     # - slice-equal?(slice("bcd"), "Abc") == 0
-210     # . prolog
-211     55/push-EBP
-212     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-213     # var slice/ECX
-214     68/push  _test-slice-data-4/imm32/end
-215     68/push  _test-slice-data-1/imm32/start
-216     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-217     # EAX = slice-equal?(ECX, "Abc")
-218     # . . push args
-219     68/push  "Abc"/imm32
-220     51/push-ECX
-221     # . . call
-222     e8/call  slice-equal?/disp32
-223     # . . discard args
-224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-225     # check-ints-equal(EAX, 0, msg)
-226     # . . push args
-227     68/push  "F - test-slice-equal-false"/imm32
-228     68/push  0/imm32
-229     50/push-EAX
-230     # . . call
-231     e8/call  check-ints-equal/disp32
-232     # . . discard args
-233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-234     # . epilog
-235     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-236     5d/pop-to-EBP
-237     c3/return
-238 
-239 test-slice-equal-too-long:
-240     # - slice-equal?(slice("Abcd"), "Abc") == 0
-241     # . prolog
-242     55/push-EBP
-243     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-244     # var slice/ECX
-245     68/push  _test-slice-data-4/imm32/end
-246     68/push  _test-slice-data-0/imm32/start
-247     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-248     # EAX = slice-equal?(ECX, "Abc")
-249     # . . push args
-250     68/push  "Abc"/imm32
-251     51/push-ECX
-252     # . . call
-253     e8/call  slice-equal?/disp32
-254     # . . discard args
-255     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-256     # check-ints-equal(EAX, 0, msg)
-257     # . . push args
-258     68/push  "F - test-slice-equal-too-long"/imm32
-259     68/push  0/imm32
-260     50/push-EAX
-261     # . . call
-262     e8/call  check-ints-equal/disp32
-263     # . . discard args
-264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-265     # . epilog
-266     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-267     5d/pop-to-EBP
-268     c3/return
-269 
-270 test-slice-equal-too-short:
-271     # - slice-equal?(slice("A"), "Abc") == 0
-272     # . prolog
-273     55/push-EBP
-274     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-275     # var slice/ECX
-276     68/push  _test-slice-data-1/imm32/end
-277     68/push  _test-slice-data-0/imm32/start
-278     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-279     # EAX = slice-equal?(ECX, "Abc")
-280     # . . push args
-281     68/push  "Abc"/imm32
-282     51/push-ECX
-283     # . . call
-284     e8/call  slice-equal?/disp32
-285     # . . discard args
-286     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-287     # check-ints-equal(EAX, 0, msg)
-288     # . . push args
-289     68/push  "F - test-slice-equal-too-short"/imm32
-290     68/push  0/imm32
-291     50/push-EAX
-292     # . . call
-293     e8/call  check-ints-equal/disp32
-294     # . . discard args
-295     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-296     # . epilog
-297     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-298     5d/pop-to-EBP
-299     c3/return
-300 
-301 test-slice-equal-empty:
-302     # - slice-equal?(slice(""), "Abc") == 0
-303     # . prolog
-304     55/push-EBP
-305     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-306     # var slice/ECX
-307     68/push  _test-slice-data-0/imm32/end
-308     68/push  _test-slice-data-0/imm32/start
-309     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-310     # EAX = slice-equal?(ECX, "Abc")
-311     # . . push args
-312     68/push  "Abc"/imm32
-313     51/push-ECX
-314     # . . call
-315     e8/call  slice-equal?/disp32
-316     # . . discard args
-317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-318     # check-ints-equal(EAX, 0, msg)
-319     # . . push args
-320     68/push  "F - test-slice-equal-empty"/imm32
-321     68/push  0/imm32
-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     # . epilog
-328     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-329     5d/pop-to-EBP
-330     c3/return
-331 
-332 test-slice-equal-with-empty:
-333     # - slice-equal?(slice("Ab"), "") == 0
-334     # . prolog
-335     55/push-EBP
-336     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-337     # var slice/ECX
-338     68/push  _test-slice-data-2/imm32/end
-339     68/push  _test-slice-data-0/imm32/start
-340     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-341     # EAX = slice-equal?(ECX, "")
-342     # . . push args
-343     68/push  ""/imm32
-344     51/push-ECX
-345     # . . call
-346     e8/call  slice-equal?/disp32
-347     # . . discard args
-348     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-349     # check-ints-equal(EAX, 0, msg)
-350     # . . push args
-351     68/push  "F - test-slice-equal-with-empty"/imm32
-352     68/push  0/imm32
-353     50/push-EAX
-354     # . . call
-355     e8/call  check-ints-equal/disp32
-356     # . . discard args
-357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-358     # . epilog
-359     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-360     5d/pop-to-EBP
-361     c3/return
-362 
-363 test-slice-equal-empty-with-empty:
-364     # - slice-equal?(slice(""), "") == 1
-365     # . prolog
-366     55/push-EBP
-367     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-368     # var slice/ECX
-369     68/push  _test-slice-data-0/imm32/end
-370     68/push  _test-slice-data-0/imm32/start
-371     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-372     # EAX = slice-equal?(ECX, "")
-373     # . . push args
-374     68/push  ""/imm32
-375     51/push-ECX
-376     # . . call
-377     e8/call  slice-equal?/disp32
-378     # . . discard args
-379     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-380     # check-ints-equal(EAX, 1, msg)
-381     # . . push args
-382     68/push  "F - test-slice-equal-empty-with-empty"/imm32
-383     68/push  1/imm32
-384     50/push-EAX
-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     # . epilog
-390     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-391     5d/pop-to-EBP
-392     c3/return
-393 
-394 slice-starts-with?:  # s : (address slice), head : (address string) -> EAX : boolean
-395     # pseudocode
-396     #   lenh = head->length
-397     #   if (lenh > s->end - s->start) return false
-398     #   i = 0
-399     #   currs = s->start
-400     #   currp = head->data
-401     #   while i < lenh
-402     #     if (*currs != *currh) return false
-403     #     ++i
-404     #     ++currs
-405     #     ++currh
-406     #   return true
-407     #
-408     # registers:
-409     #   currs: ESI
-410     #   currh: EDI
-411     #   *currs: EAX
-412     #   *currh: EBX
-413     #   i: ECX
-414     #   lenh: EDX
-415     #
-416     # . prolog
-417     55/push-EBP
-418     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-419     # . save registers
-420     51/push-ECX
-421     52/push-EDX
-422     53/push-EBX
-423     56/push-ESI
-424     57/push-EDI
-425     # ESI = s
-426     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-427     # ECX = s->end - s->start
-428     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-429     2b/subtract                     0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # subtract *ESI from ECX
-430     # EDI = head
-431     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
-432     # lenh/EDX = head->length
-433     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy *EDI to EDX
-434     # if (lenh > s->end - s->start) return false
-435     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           1/r32/ECX   .               .                 # compare EDX with ECX
-436     7f/jump-if-greater  $slice-starts-with?:false/disp8
-437     # currs/ESI = s->start
-438     8b/subtract                     0/mod/indirect  6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # copy *ESI to ESI
-439     # currh/EDI = head->data
-440     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm32           # add to EDI
-441     # i/ECX = 0
-442     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
-443     # EAX = EBX = 0
-444     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-445     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-446 $slice-starts-with?:loop:
-447     # if (i >= lenh) return true
-448     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-449     7d/jump-if-greater-or-equal  $slice-starts-with?:true/disp8
-450     # AL = *currs
-451     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
-452     # BL = *currh
-453     8a/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at *EDI to BL
-454     # if (*currs != *currh) return false
-455     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX and EBX
-456     75/jump-if-not-equal  $slice-starts-with?:false/disp8
-457     # ++i
-458     41/increment-ECX
-459     # ++currs
-460     46/increment-ESI
-461     # ++currh
-462     47/increment-EDI
-463     eb/jump $slice-starts-with?:loop/disp8
-464 $slice-starts-with?:true:
-465     b8/copy-to-EAX  1/imm32
-466     eb/jump  $slice-starts-with?:end/disp8
-467 $slice-starts-with?:false:
-468     b8/copy-to-EAX  0/imm32
-469 $slice-starts-with?:end:
-470     # . restore registers
-471     5f/pop-to-EDI
-472     5e/pop-to-ESI
-473     5b/pop-to-EBX
-474     5a/pop-to-EDX
-475     59/pop-to-ECX
-476     # . epilog
-477     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-478     5d/pop-to-EBP
-479     c3/return
-480 
-481 test-slice-starts-with-single-character:
-482     # - slice-starts-with?(slice("Abc"), "A") == 1
-483     # . prolog
-484     55/push-EBP
-485     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-486     # var slice/ECX
-487     68/push  _test-slice-data-3/imm32/end
-488     68/push  _test-slice-data-0/imm32/start
-489     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-490     # EAX = slice-starts-with?(ECX, "A")
-491     # . . push args
-492     68/push  "A"/imm32
-493     51/push-ECX
-494     # . . call
-495     e8/call  slice-starts-with?/disp32
-496     # . . discard args
-497     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-498     # check-ints-equal(EAX, 1, msg)
-499     # . . push args
-500     68/push  "F - test-slice-starts-with-single-character"/imm32
-501     68/push  1/imm32
-502     50/push-EAX
-503     # . . call
-504     e8/call  check-ints-equal/disp32
-505     # . . discard args
-506     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-507     # . epilog
-508     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-509     5d/pop-to-EBP
-510     c3/return
-511 
-512 test-slice-starts-with-empty-string:
-513     # - slice-starts-with?(slice("Abc"), "") == 1
-514     # . prolog
-515     55/push-EBP
-516     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-517     # var slice/ECX
-518     68/push  _test-slice-data-3/imm32/end
-519     68/push  _test-slice-data-0/imm32/start
-520     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-521     # EAX = slice-starts-with?(ECX, "")
-522     # . . push args
-523     68/push  ""/imm32
-524     51/push-ECX
-525     # . . call
-526     e8/call  slice-starts-with?/disp32
-527     # . . discard args
-528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-529     # check-ints-equal(EAX, 1, msg)
-530     # . . push args
-531     68/push  "F - test-slice-starts-with-empty-string"/imm32
-532     68/push  1/imm32
-533     50/push-EAX
-534     # . . call
-535     e8/call  check-ints-equal/disp32
-536     # . . discard args
-537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-538     # . epilog
-539     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-540     5d/pop-to-EBP
-541     c3/return
-542 
-543 test-slice-starts-with-multiple-characters:
-544     # - slice-starts-with?(slice("Abc"), "Ab") == 1
-545     # . prolog
-546     55/push-EBP
-547     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-548     # var slice/ECX
-549     68/push  _test-slice-data-3/imm32/end
-550     68/push  _test-slice-data-0/imm32/start
-551     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-552     # EAX = slice-starts-with?(ECX, "Ab")
-553     # . . push args
-554     68/push  "Ab"/imm32
-555     51/push-ECX
-556     # . . call
-557     e8/call  slice-starts-with?/disp32
-558     # . . discard args
-559     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-560     # check-ints-equal(EAX, 1, msg)
-561     # . . push args
-562     68/push  "F - test-slice-starts-with-multiple-characters"/imm32
-563     68/push  1/imm32
-564     50/push-EAX
-565     # . . call
-566     e8/call  check-ints-equal/disp32
-567     # . . discard args
-568     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-569     # . epilog
-570     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-571     5d/pop-to-EBP
-572     c3/return
-573 
-574 test-slice-starts-with-entire-string:
-575     # - slice-starts-with?(slice("Abc"), "Abc") == 1
-576     # . prolog
-577     55/push-EBP
-578     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-579     # var slice/ECX
-580     68/push  _test-slice-data-3/imm32/end
-581     68/push  _test-slice-data-0/imm32/start
-582     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-583     # EAX = slice-starts-with?(ECX, "Abc")
-584     # . . push args
-585     68/push  "Abc"/imm32
-586     51/push-ECX
-587     # . . call
-588     e8/call  slice-starts-with?/disp32
-589     # . . discard args
-590     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-591     # check-ints-equal(EAX, 1, msg)
-592     # . . push args
-593     68/push  "F - test-slice-starts-with-entire-string"/imm32
-594     68/push  1/imm32
-595     50/push-EAX
-596     # . . call
-597     e8/call  check-ints-equal/disp32
-598     # . . discard args
-599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-600     # . epilog
-601     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-602     5d/pop-to-EBP
-603     c3/return
-604 
-605 test-slice-starts-with-fails:
-606     # - slice-starts-with?(slice("Abc"), "Abd") == 1
-607     # . prolog
-608     55/push-EBP
-609     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-610     # var slice/ECX
-611     68/push  _test-slice-data-3/imm32/end
-612     68/push  _test-slice-data-0/imm32/start
-613     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-614     # EAX = slice-starts-with?(ECX, "Abd")
-615     # . . push args
-616     68/push  "Abd"/imm32
-617     51/push-ECX
-618     # . . call
-619     e8/call  slice-starts-with?/disp32
-620     # . . discard args
-621     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-622     # check-ints-equal(EAX, 0, msg)
-623     # . . push args
-624     68/push  "F - test-slice-starts-with-fails"/imm32
-625     68/push  0/imm32
-626     50/push-EAX
-627     # . . call
-628     e8/call  check-ints-equal/disp32
-629     # . . discard args
-630     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-631     # . epilog
-632     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-633     5d/pop-to-EBP
-634     c3/return
-635 
-636 test-slice-starts-with-fails-2:
-637     # - slice-starts-with?(slice("Abc"), "Ac") == 1
-638     # . prolog
-639     55/push-EBP
-640     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-641     # var slice/ECX
-642     68/push  _test-slice-data-3/imm32/end
-643     68/push  _test-slice-data-0/imm32/start
-644     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-645     # EAX = slice-starts-with?(ECX, "Ac")
-646     # . . push args
-647     68/push  "Ac"/imm32
-648     51/push-ECX
-649     # . . call
-650     e8/call  slice-starts-with?/disp32
-651     # . . discard args
-652     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-653     # check-ints-equal(EAX, 0, msg)
-654     # . . push args
-655     68/push  "F - test-slice-starts-with-fails-2"/imm32
-656     68/push  0/imm32
-657     50/push-EAX
-658     # . . call
-659     e8/call  check-ints-equal/disp32
-660     # . . discard args
-661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-662     # . epilog
-663     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-664     5d/pop-to-EBP
-665     c3/return
-666 
-667 write-slice:  # out : (address buffered-file), s : (address slice)
-668     # . prolog
-669     55/push-EBP
-670     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-671     # . save registers
-672     50/push-EAX
-673     51/push-ECX
-674     52/push-EDX
-675     53/push-EBX
-676     56/push-ESI
-677     57/push-EDI
-678     # ESI = s
-679     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
-680     # curr/ECX = s->start
-681     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
-682     # max/ESI = s->end
-683     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
-684     # EDI = out
-685     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
-686     # EDX = out->length
-687     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EDI+12) to EDX
-688     # EBX = out->write
-689     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(EDI+4) to EBX
-690 $write-slice:loop:
-691     # if (curr >= max) break
-692     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # compare ECX with ESI
-693     7d/jump-if-greater-or-equal  $write-slice:loop-end/disp8
-694     # if (out->write >= out->length) flush and clear out's stream
-695     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX with EDX
-696     7c/jump-if-lesser  $write-slice:to-stream/disp8
-697     # . persist out->write
-698     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
-699     # . flush(out)
-700     # . . push args
-701     57/push-EDI
-702     # . . call
-703     e8/call  flush/disp32
-704     # . . discard args
-705     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-706     # . clear-stream(stream = out+4)
-707     # . . push args
-708     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
-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     # . out->write must now be 0; update its cache at EBX
-715     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-716 $write-slice:to-stream:
-717     # out->data[out->write] = *in
-718     # . AL = *in
-719     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-720     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-721     # . out->data[out->write] = AL
-722     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  3/index/EBX   .           0/r32/AL    0x10/disp8      .                 # copy AL to *(EDI+EBX+16)
-723     # ++out->write
-724     43/increment-EBX
-725     # ++in
-726     41/increment-ECX
-727     eb/jump  $write-slice:loop/disp8
-728 $write-slice:loop-end:
-729     # persist necessary variables from registers
-730     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
-731 $write-slice:end:
-732     # . restore registers
-733     5f/pop-to-EDI
-734     5e/pop-to-ESI
-735     5b/pop-to-EBX
-736     5a/pop-to-EDX
-737     59/pop-to-ECX
-738     58/pop-to-EAX
-739     # . epilog
-740     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-741     5d/pop-to-EBP
-742     c3/return
-743 
-744 test-write-slice:
-745     # . prolog
-746     55/push-EBP
-747     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-748     # setup
-749     # . clear-stream(_test-stream)
-750     # . . push args
-751     68/push  _test-stream/imm32
-752     # . . call
-753     e8/call  clear-stream/disp32
-754     # . . discard args
-755     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-756     # . clear-stream(_test-buffered-file+4)
-757     # . . push args
-758     b8/copy-to-EAX  _test-buffered-file/imm32
-759     05/add-to-EAX  4/imm32
-760     50/push-EAX
-761     # . . call
-762     e8/call  clear-stream/disp32
-763     # . . discard args
-764     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-765     # var slice/ECX = "Abc"
-766     68/push  _test-slice-data-3/imm32/end
-767     68/push  _test-slice-data-0/imm32/start
-768     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-769     # write-slice(_test-buffered-file, slice)
-770     # . . push args
-771     51/push-ECX
-772     68/push  _test-buffered-file/imm32
-773     # . . call
-774     e8/call  write-slice/disp32
-775     # . . discard args
-776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-777     # flush(_test-buffered-file)
-778     # . . push args
-779     68/push  _test-buffered-file/imm32
-780     # . . call
-781     e8/call  flush/disp32
-782     # . . discard args
-783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-784     # check-stream-equal(_test-stream, "Abc", msg)
-785     # . . push args
-786     68/push  "F - test-write-slice"/imm32
-787     68/push  "Abc"/imm32
-788     68/push  _test-stream/imm32
-789     # . . call
-790     e8/call  check-stream-equal/disp32
-791     # . . discard args
-792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-793     # . epilog
-794     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-795     5d/pop-to-EBP
-796     c3/return
-797 
-798 # write an entire stream's contents to a buffered-file
-799 # ways to do this:
-800 #   - construct a 'maximal slice' and pass it to write-slice
-801 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
-802 # we'll go with the first way for now
-803 write-stream-buffered:  # f : (address buffered-file), s : (address stream) -> <void>
-804     # . prolog
-805     55/push-EBP
-806     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-807     # . save registers
-808     50/push-EAX
-809     51/push-ECX
-810     56/push-ESI
-811     # ESI = s
-812     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
-813     # var slice/ECX = {s->data, s->data + s->write}
-814     # . push s->data + s->write
-815     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-816     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
-817     50/push-EAX
-818     # . push s->data
-819     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
-820     50/push-EAX
-821     # . ECX = ESP
-822     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-823     # write-slice(f, slice)
-824     # . . push args
-825     51/push-ECX
-826     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-827     # . . call
-828     e8/call  write-slice/disp32
-829     # . . discard args
-830     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-831 $write-stream-buffered:end:
-832     # . restore locals
-833     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-834     # . restore registers
-835     5e/pop-to-ESI
-836     59/pop-to-ECX
-837     58/pop-to-EAX
-838     # . epilog
-839     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-840     5d/pop-to-EBP
-841     c3/return
-842 
-843 test-write-stream-buffered:
-844     # . prolog
-845     55/push-EBP
-846     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-847     # setup
-848     # . clear-stream(_test-stream)
-849     # . . push args
-850     68/push  _test-stream/imm32
-851     # . . call
-852     e8/call  clear-stream/disp32
-853     # . . discard args
-854     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-855     # . clear-stream(_test-buffered-file+4)
-856     # . . push args
-857     b8/copy-to-EAX  _test-buffered-file/imm32
-858     05/add-to-EAX  4/imm32
-859     50/push-EAX
-860     # . . call
-861     e8/call  clear-stream/disp32
-862     # . . discard args
-863     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-864     # . clear-stream(_test-tmp-stream)
-865     # . . push args
-866     68/push  _test-tmp-stream/imm32
-867     # . . call
-868     e8/call  clear-stream/disp32
-869     # . . discard args
-870     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-871     # initialize input
-872     # . write(_test-tmp-stream, "abcd")
-873     # . . push args
-874     68/push  "abcd"/imm32
-875     68/push  _test-tmp-stream/imm32
-876     # . . call
-877     e8/call  write/disp32
-878     # . . discard args
-879     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-880     # perform the write-stream-buffered
-881     # . write-stream-buffered(_test-buffered-file, _test-tmp-stream)
-882     # . . push args
-883     68/push  _test-tmp-stream/imm32
-884     68/push  _test-buffered-file/imm32
-885     # . . call
-886     e8/call  write-stream-buffered/disp32
-887     # . . discard args
-888     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-889     # check that the write happened as expected
-890     # . flush(_test-buffered-file)
-891     # . . push args
-892     68/push  _test-buffered-file/imm32
-893     # . . call
-894     e8/call  flush/disp32
-895     # . . discard args
-896     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-897     # . check-stream-equal(_test-stream, "abcd", msg)
-898     # . . push args
-899     68/push  "F - test-write-stream-buffered"/imm32
-900     68/push  "abcd"/imm32
-901     68/push  _test-stream/imm32
-902     # . . call
-903     e8/call  check-stream-equal/disp32
-904     # . . discard args
-905     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-906     # . epilog
-907     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-908     5d/pop-to-EBP
-909     c3/return
-910 
-911 == data
-912 
-913 _test-slice-data-0:
-914     41/A
-915 _test-slice-data-1:
-916     62/b
-917 _test-slice-data-2:
-918     63/c
-919 _test-slice-data-3:
-920     64/d
-921 _test-slice-data-4:
-922 
-923 # . _. vim:nowrap:textwidth=0
+   1 # new data structure: a slice is an open interval of addresses [start, end)
+   2 # that includes 'start' but not 'end'
+   3 
+   4 == code
+   5 #   instruction                     effective address                                                   register    displacement    immediate
+   6 # . op          subop               mod             rm32          base        index         scale       r32
+   7 # . 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
+   8 
+   9 #? Entry:  # run a single test, while debugging
+  10 #?     e8/call test-slice-to-string/disp32
+  11 #?     # syscall(exit, Num-test-failures)
+  12 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+  13 #?     b8/copy-to-EAX  1/imm32/exit
+  14 #?     cd/syscall  0x80/imm8
+  15 
+  16 slice-empty?:  # s : (address slice) -> EAX : boolean
+  17     # . prolog
+  18     55/push-EBP
+  19     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  20     # . save registers
+  21     51/push-ECX
+  22     # ECX = s
+  23     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+  24     # if (s->start == s->end) return true
+  25     # . EAX = s->start
+  26     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+  27     # . compare EAX and s->end
+  28     39/compare                      1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # compare EAX and *(ECX+4)
+  29     b8/copy-to-EAX  1/imm32/true
+  30     74/jump-if-equal  $slice-empty?:end/disp8
+  31     b8/copy-to-EAX  0/imm32/false
+  32 $slice-empty?:end:
+  33     # . restore registers
+  34     59/pop-to-ECX
+  35     # . epilog
+  36     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+  37     5d/pop-to-EBP
+  38     c3/return
+  39 
+  40 test-slice-empty-true:
+  41     # . prolog
+  42     55/push-EBP
+  43     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  44     # var slice/ECX = {34, 34}
+  45     68/push  34/imm32/end
+  46     68/push  34/imm32/start
+  47     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+  48     # slice-empty?(slice)
+  49     # . . push args
+  50     51/push-ECX
+  51     # . . call
+  52     e8/call  slice-empty?/disp32
+  53     # . . discard args
+  54     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+  55     # check-ints-equal(EAX, 1, msg)
+  56     # . . push args
+  57     68/push  "F - test-slice-empty-true"/imm32
+  58     68/push  1/imm32
+  59     50/push-EAX
+  60     # . . call
+  61     e8/call  check-ints-equal/disp32
+  62     # . . discard args
+  63     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+  64     # . epilog
+  65     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+  66     5d/pop-to-EBP
+  67     c3/return
+  68 
+  69 test-slice-empty-false:
+  70     # . prolog
+  71     55/push-EBP
+  72     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  73     # var slice/ECX = {34, 23}
+  74     68/push  23/imm32/end
+  75     68/push  34/imm32/start
+  76     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+  77     # slice-empty?(slice)
+  78     # . . push args
+  79     51/push-ECX
+  80     # . . call
+  81     e8/call  slice-empty?/disp32
+  82     # . . discard args
+  83     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+  84     # check-ints-equal(EAX, 0, msg)
+  85     # . . push args
+  86     68/push  "F - test-slice-empty-false"/imm32
+  87     68/push  0/imm32
+  88     50/push-EAX
+  89     # . . call
+  90     e8/call  check-ints-equal/disp32
+  91     # . . discard args
+  92     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+  93     # . epilog
+  94     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+  95     5d/pop-to-EBP
+  96     c3/return
+  97 
+  98 slice-equal?:  # s : (address slice), p : (address string) -> EAX : boolean
+  99     # pseudocode:
+ 100     #   if (p == 0) return (s == 0)
+ 101     #   currs = s->start
+ 102     #   maxs = s->end
+ 103     #   if (maxs - currs != p->length) return false
+ 104     #   currp = p->data
+ 105     #   while currs < maxs
+ 106     #     if (*currs != *currp) return false
+ 107     #     ++currs
+ 108     #     ++currp
+ 109     #   return true
+ 110     #
+ 111     # registers:
+ 112     #   currs: EDX
+ 113     #   maxs: ESI
+ 114     #   currp: EBX
+ 115     #   *currs: EAX
+ 116     #   *currp: ECX
+ 117     #
+ 118     # . prolog
+ 119     55/push-EBP
+ 120     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 121     # . save registers
+ 122     51/push-ECX
+ 123     52/push-EDX
+ 124     53/push-EBX
+ 125     56/push-ESI
+ 126     # ESI = s
+ 127     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 128     # currs/EDX = s->start
+ 129     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+ 130     # maxs/ESI = s->end
+ 131     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
+ 132     # EAX = maxs - currs
+ 133     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy ESI to EAX
+ 134     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # subtract EDX from EAX
+ 135     # EBX = p
+ 136     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
+ 137     # if (p != 0) goto next check
+ 138     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
+ 139     75/jump-if-not-equal  $slice-equal?:nonnull-string/disp8
+ 140 $slice-equal?:null-string:
+ 141     # return s->start == s->end
+ 142     3d/compare-EAX-and  0/imm32
+ 143     74/jump-if-equal  $slice-equal?:true/disp8
+ 144     eb/jump  $slice-equal?:false/disp8
+ 145 $slice-equal?:nonnull-string:
+ 146     # if (EAX != p->length) return false
+ 147     39/compare                      0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # compare *EBX and EAX
+ 148     75/jump-if-not-equal  $slice-equal?:false/disp8
+ 149     # currp/EBX = p->data
+ 150     81          0/subop/add         3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm32           # add to EBX
+ 151     # EAX = ECX = 0
+ 152     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 153     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+ 154 $slice-equal?:loop:
+ 155     # if (currs >= maxs) return true
+ 156     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX with ESI
+ 157     7d/jump-if-greater-or-equal  $slice-equal?:true/disp8
+ 158     # AL = *currp
+ 159     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
+ 160     # CL = *currs
+ 161     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
+ 162     # if (EAX != ECX) return false
+ 163     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX and ECX
+ 164     75/jump-if-not-equal  $slice-equal?:false/disp8
+ 165     # ++currp
+ 166     43/increment-EBX
+ 167     # ++currs
+ 168     42/increment-EDX
+ 169     eb/jump $slice-equal?:loop/disp8
+ 170 $slice-equal?:false:
+ 171     b8/copy-to-EAX  0/imm32
+ 172     eb/jump  $slice-equal?:end/disp8
+ 173 $slice-equal?:true:
+ 174     b8/copy-to-EAX  1/imm32
+ 175 $slice-equal?:end:
+ 176     # . restore registers
+ 177     5e/pop-to-ESI
+ 178     5b/pop-to-EBX
+ 179     5a/pop-to-EDX
+ 180     59/pop-to-ECX
+ 181     # . epilog
+ 182     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 183     5d/pop-to-EBP
+ 184     c3/return
+ 185 
+ 186 test-slice-equal:
+ 187     # - slice-equal?(slice("Abc"), "Abc") == 1
+ 188     # . prolog
+ 189     55/push-EBP
+ 190     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 191     # var slice/ECX
+ 192     68/push  _test-slice-data-3/imm32/end
+ 193     68/push  _test-slice-data-0/imm32/start
+ 194     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 195     # EAX = slice-equal?(ECX, "Abc")
+ 196     # . . push args
+ 197     68/push  "Abc"/imm32
+ 198     51/push-ECX
+ 199     # . . call
+ 200     e8/call  slice-equal?/disp32
+ 201     # . . discard args
+ 202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 203     # check-ints-equal(EAX, 1, msg)
+ 204     # . . push args
+ 205     68/push  "F - test-slice-equal"/imm32
+ 206     68/push  1/imm32
+ 207     50/push-EAX
+ 208     # . . call
+ 209     e8/call  check-ints-equal/disp32
+ 210     # . . discard args
+ 211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 212     # . epilog
+ 213     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 214     5d/pop-to-EBP
+ 215     c3/return
+ 216 
+ 217 test-slice-equal-false:
+ 218     # - slice-equal?(slice("bcd"), "Abc") == 0
+ 219     # . prolog
+ 220     55/push-EBP
+ 221     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 222     # var slice/ECX
+ 223     68/push  _test-slice-data-4/imm32/end
+ 224     68/push  _test-slice-data-1/imm32/start
+ 225     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 226     # EAX = slice-equal?(ECX, "Abc")
+ 227     # . . push args
+ 228     68/push  "Abc"/imm32
+ 229     51/push-ECX
+ 230     # . . call
+ 231     e8/call  slice-equal?/disp32
+ 232     # . . discard args
+ 233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 234     # check-ints-equal(EAX, 0, msg)
+ 235     # . . push args
+ 236     68/push  "F - test-slice-equal-false"/imm32
+ 237     68/push  0/imm32
+ 238     50/push-EAX
+ 239     # . . call
+ 240     e8/call  check-ints-equal/disp32
+ 241     # . . discard args
+ 242     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 243     # . epilog
+ 244     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 245     5d/pop-to-EBP
+ 246     c3/return
+ 247 
+ 248 test-slice-equal-too-long:
+ 249     # - slice-equal?(slice("Abcd"), "Abc") == 0
+ 250     # . prolog
+ 251     55/push-EBP
+ 252     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 253     # var slice/ECX
+ 254     68/push  _test-slice-data-4/imm32/end
+ 255     68/push  _test-slice-data-0/imm32/start
+ 256     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 257     # EAX = slice-equal?(ECX, "Abc")
+ 258     # . . push args
+ 259     68/push  "Abc"/imm32
+ 260     51/push-ECX
+ 261     # . . call
+ 262     e8/call  slice-equal?/disp32
+ 263     # . . discard args
+ 264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 265     # check-ints-equal(EAX, 0, msg)
+ 266     # . . push args
+ 267     68/push  "F - test-slice-equal-too-long"/imm32
+ 268     68/push  0/imm32
+ 269     50/push-EAX
+ 270     # . . call
+ 271     e8/call  check-ints-equal/disp32
+ 272     # . . discard args
+ 273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 274     # . epilog
+ 275     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 276     5d/pop-to-EBP
+ 277     c3/return
+ 278 
+ 279 test-slice-equal-too-short:
+ 280     # - slice-equal?(slice("A"), "Abc") == 0
+ 281     # . prolog
+ 282     55/push-EBP
+ 283     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 284     # var slice/ECX
+ 285     68/push  _test-slice-data-1/imm32/end
+ 286     68/push  _test-slice-data-0/imm32/start
+ 287     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 288     # EAX = slice-equal?(ECX, "Abc")
+ 289     # . . push args
+ 290     68/push  "Abc"/imm32
+ 291     51/push-ECX
+ 292     # . . call
+ 293     e8/call  slice-equal?/disp32
+ 294     # . . discard args
+ 295     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 296     # check-ints-equal(EAX, 0, msg)
+ 297     # . . push args
+ 298     68/push  "F - test-slice-equal-too-short"/imm32
+ 299     68/push  0/imm32
+ 300     50/push-EAX
+ 301     # . . call
+ 302     e8/call  check-ints-equal/disp32
+ 303     # . . discard args
+ 304     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 305     # . epilog
+ 306     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 307     5d/pop-to-EBP
+ 308     c3/return
+ 309 
+ 310 test-slice-equal-empty:
+ 311     # - slice-equal?(slice(""), "Abc") == 0
+ 312     # . prolog
+ 313     55/push-EBP
+ 314     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 315     # var slice/ECX
+ 316     68/push  _test-slice-data-0/imm32/end
+ 317     68/push  _test-slice-data-0/imm32/start
+ 318     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 319     # EAX = slice-equal?(ECX, "Abc")
+ 320     # . . push args
+ 321     68/push  "Abc"/imm32
+ 322     51/push-ECX
+ 323     # . . call
+ 324     e8/call  slice-equal?/disp32
+ 325     # . . discard args
+ 326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 327     # check-ints-equal(EAX, 0, msg)
+ 328     # . . push args
+ 329     68/push  "F - test-slice-equal-empty"/imm32
+ 330     68/push  0/imm32
+ 331     50/push-EAX
+ 332     # . . call
+ 333     e8/call  check-ints-equal/disp32
+ 334     # . . discard args
+ 335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 336     # . epilog
+ 337     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 338     5d/pop-to-EBP
+ 339     c3/return
+ 340 
+ 341 test-slice-equal-with-empty:
+ 342     # - slice-equal?(slice("Ab"), "") == 0
+ 343     # . prolog
+ 344     55/push-EBP
+ 345     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 346     # var slice/ECX
+ 347     68/push  _test-slice-data-2/imm32/end
+ 348     68/push  _test-slice-data-0/imm32/start
+ 349     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 350     # EAX = slice-equal?(ECX, "")
+ 351     # . . push args
+ 352     68/push  ""/imm32
+ 353     51/push-ECX
+ 354     # . . call
+ 355     e8/call  slice-equal?/disp32
+ 356     # . . discard args
+ 357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 358     # check-ints-equal(EAX, 0, msg)
+ 359     # . . push args
+ 360     68/push  "F - test-slice-equal-with-empty"/imm32
+ 361     68/push  0/imm32
+ 362     50/push-EAX
+ 363     # . . call
+ 364     e8/call  check-ints-equal/disp32
+ 365     # . . discard args
+ 366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 367     # . epilog
+ 368     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 369     5d/pop-to-EBP
+ 370     c3/return
+ 371 
+ 372 test-slice-equal-empty-with-empty:
+ 373     # - slice-equal?(slice(""), "") == 1
+ 374     # . prolog
+ 375     55/push-EBP
+ 376     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 377     # var slice/ECX
+ 378     68/push  _test-slice-data-0/imm32/end
+ 379     68/push  _test-slice-data-0/imm32/start
+ 380     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 381     # EAX = slice-equal?(ECX, "")
+ 382     # . . push args
+ 383     68/push  ""/imm32
+ 384     51/push-ECX
+ 385     # . . call
+ 386     e8/call  slice-equal?/disp32
+ 387     # . . discard args
+ 388     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 389     # check-ints-equal(EAX, 1, msg)
+ 390     # . . push args
+ 391     68/push  "F - test-slice-equal-empty-with-empty"/imm32
+ 392     68/push  1/imm32
+ 393     50/push-EAX
+ 394     # . . call
+ 395     e8/call  check-ints-equal/disp32
+ 396     # . . discard args
+ 397     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 398     # . epilog
+ 399     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 400     5d/pop-to-EBP
+ 401     c3/return
+ 402 
+ 403 test-slice-equal-with-null:
+ 404     # - slice-equal?(slice("Ab"), null) == 0
+ 405     # . prolog
+ 406     55/push-EBP
+ 407     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 408     # var slice/ECX
+ 409     68/push  _test-slice-data-2/imm32/end
+ 410     68/push  _test-slice-data-0/imm32/start
+ 411     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 412     # EAX = slice-equal?(ECX, 0)
+ 413     # . . push args
+ 414     68/push  0/imm32
+ 415     51/push-ECX
+ 416     # . . call
+ 417     e8/call  slice-equal?/disp32
+ 418     # . . discard args
+ 419     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 420     # check-ints-equal(EAX, 0, msg)
+ 421     # . . push args
+ 422     68/push  "F - test-slice-equal-with-null"/imm32
+ 423     68/push  0/imm32
+ 424     50/push-EAX
+ 425     # . . call
+ 426     e8/call  check-ints-equal/disp32
+ 427     # . . discard args
+ 428     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 429     # . epilog
+ 430     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 431     5d/pop-to-EBP
+ 432     c3/return
+ 433 
+ 434 slice-starts-with?:  # s : (address slice), head : (address string) -> EAX : boolean
+ 435     # pseudocode
+ 436     #   lenh = head->length
+ 437     #   if (lenh > s->end - s->start) return false
+ 438     #   i = 0
+ 439     #   currs = s->start
+ 440     #   currp = head->data
+ 441     #   while i < lenh
+ 442     #     if (*currs != *currh) return false
+ 443     #     ++i
+ 444     #     ++currs
+ 445     #     ++currh
+ 446     #   return true
+ 447     #
+ 448     # registers:
+ 449     #   currs: ESI
+ 450     #   currh: EDI
+ 451     #   *currs: EAX
+ 452     #   *currh: EBX
+ 453     #   i: ECX
+ 454     #   lenh: EDX
+ 455     #
+ 456     # . prolog
+ 457     55/push-EBP
+ 458     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 459     # . save registers
+ 460     51/push-ECX
+ 461     52/push-EDX
+ 462     53/push-EBX
+ 463     56/push-ESI
+ 464     57/push-EDI
+ 465     # ESI = s
+ 466     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 467     # ECX = s->end - s->start
+ 468     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 469     2b/subtract                     0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # subtract *ESI from ECX
+ 470     # EDI = head
+ 471     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+ 472     # lenh/EDX = head->length
+ 473     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy *EDI to EDX
+ 474     # if (lenh > s->end - s->start) return false
+ 475     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           1/r32/ECX   .               .                 # compare EDX with ECX
+ 476     7f/jump-if-greater  $slice-starts-with?:false/disp8
+ 477     # currs/ESI = s->start
+ 478     8b/subtract                     0/mod/indirect  6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # copy *ESI to ESI
+ 479     # currh/EDI = head->data
+ 480     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm32           # add to EDI
+ 481     # i/ECX = 0
+ 482     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+ 483     # EAX = EBX = 0
+ 484     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 485     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+ 486 $slice-starts-with?:loop:
+ 487     # if (i >= lenh) return true
+ 488     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+ 489     7d/jump-if-greater-or-equal  $slice-starts-with?:true/disp8
+ 490     # AL = *currs
+ 491     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
+ 492     # BL = *currh
+ 493     8a/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at *EDI to BL
+ 494     # if (*currs != *currh) return false
+ 495     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX and EBX
+ 496     75/jump-if-not-equal  $slice-starts-with?:false/disp8
+ 497     # ++i
+ 498     41/increment-ECX
+ 499     # ++currs
+ 500     46/increment-ESI
+ 501     # ++currh
+ 502     47/increment-EDI
+ 503     eb/jump $slice-starts-with?:loop/disp8
+ 504 $slice-starts-with?:true:
+ 505     b8/copy-to-EAX  1/imm32
+ 506     eb/jump  $slice-starts-with?:end/disp8
+ 507 $slice-starts-with?:false:
+ 508     b8/copy-to-EAX  0/imm32
+ 509 $slice-starts-with?:end:
+ 510     # . restore registers
+ 511     5f/pop-to-EDI
+ 512     5e/pop-to-ESI
+ 513     5b/pop-to-EBX
+ 514     5a/pop-to-EDX
+ 515     59/pop-to-ECX
+ 516     # . epilog
+ 517     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 518     5d/pop-to-EBP
+ 519     c3/return
+ 520 
+ 521 test-slice-starts-with-single-character:
+ 522     # - slice-starts-with?(slice("Abc"), "A") == 1
+ 523     # . prolog
+ 524     55/push-EBP
+ 525     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 526     # var slice/ECX
+ 527     68/push  _test-slice-data-3/imm32/end
+ 528     68/push  _test-slice-data-0/imm32/start
+ 529     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 530     # EAX = slice-starts-with?(ECX, "A")
+ 531     # . . push args
+ 532     68/push  "A"/imm32
+ 533     51/push-ECX
+ 534     # . . call
+ 535     e8/call  slice-starts-with?/disp32
+ 536     # . . discard args
+ 537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 538     # check-ints-equal(EAX, 1, msg)
+ 539     # . . push args
+ 540     68/push  "F - test-slice-starts-with-single-character"/imm32
+ 541     68/push  1/imm32
+ 542     50/push-EAX
+ 543     # . . call
+ 544     e8/call  check-ints-equal/disp32
+ 545     # . . discard args
+ 546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 547     # . epilog
+ 548     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 549     5d/pop-to-EBP
+ 550     c3/return
+ 551 
+ 552 test-slice-starts-with-empty-string:
+ 553     # - slice-starts-with?(slice("Abc"), "") == 1
+ 554     # . prolog
+ 555     55/push-EBP
+ 556     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 557     # var slice/ECX
+ 558     68/push  _test-slice-data-3/imm32/end
+ 559     68/push  _test-slice-data-0/imm32/start
+ 560     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 561     # EAX = slice-starts-with?(ECX, "")
+ 562     # . . push args
+ 563     68/push  ""/imm32
+ 564     51/push-ECX
+ 565     # . . call
+ 566     e8/call  slice-starts-with?/disp32
+ 567     # . . discard args
+ 568     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 569     # check-ints-equal(EAX, 1, msg)
+ 570     # . . push args
+ 571     68/push  "F - test-slice-starts-with-empty-string"/imm32
+ 572     68/push  1/imm32
+ 573     50/push-EAX
+ 574     # . . call
+ 575     e8/call  check-ints-equal/disp32
+ 576     # . . discard args
+ 577     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 578     # . epilog
+ 579     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 580     5d/pop-to-EBP
+ 581     c3/return
+ 582 
+ 583 test-slice-starts-with-multiple-characters:
+ 584     # - slice-starts-with?(slice("Abc"), "Ab") == 1
+ 585     # . prolog
+ 586     55/push-EBP
+ 587     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 588     # var slice/ECX
+ 589     68/push  _test-slice-data-3/imm32/end
+ 590     68/push  _test-slice-data-0/imm32/start
+ 591     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 592     # EAX = slice-starts-with?(ECX, "Ab")
+ 593     # . . push args
+ 594     68/push  "Ab"/imm32
+ 595     51/push-ECX
+ 596     # . . call
+ 597     e8/call  slice-starts-with?/disp32
+ 598     # . . discard args
+ 599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 600     # check-ints-equal(EAX, 1, msg)
+ 601     # . . push args
+ 602     68/push  "F - test-slice-starts-with-multiple-characters"/imm32
+ 603     68/push  1/imm32
+ 604     50/push-EAX
+ 605     # . . call
+ 606     e8/call  check-ints-equal/disp32
+ 607     # . . discard args
+ 608     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 609     # . epilog
+ 610     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 611     5d/pop-to-EBP
+ 612     c3/return
+ 613 
+ 614 test-slice-starts-with-entire-string:
+ 615     # - slice-starts-with?(slice("Abc"), "Abc") == 1
+ 616     # . prolog
+ 617     55/push-EBP
+ 618     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 619     # var slice/ECX
+ 620     68/push  _test-slice-data-3/imm32/end
+ 621     68/push  _test-slice-data-0/imm32/start
+ 622     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 623     # EAX = slice-starts-with?(ECX, "Abc")
+ 624     # . . push args
+ 625     68/push  "Abc"/imm32
+ 626     51/push-ECX
+ 627     # . . call
+ 628     e8/call  slice-starts-with?/disp32
+ 629     # . . discard args
+ 630     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 631     # check-ints-equal(EAX, 1, msg)
+ 632     # . . push args
+ 633     68/push  "F - test-slice-starts-with-entire-string"/imm32
+ 634     68/push  1/imm32
+ 635     50/push-EAX
+ 636     # . . call
+ 637     e8/call  check-ints-equal/disp32
+ 638     # . . discard args
+ 639     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 640     # . epilog
+ 641     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 642     5d/pop-to-EBP
+ 643     c3/return
+ 644 
+ 645 test-slice-starts-with-fails:
+ 646     # - slice-starts-with?(slice("Abc"), "Abd") == 1
+ 647     # . prolog
+ 648     55/push-EBP
+ 649     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 650     # var slice/ECX
+ 651     68/push  _test-slice-data-3/imm32/end
+ 652     68/push  _test-slice-data-0/imm32/start
+ 653     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 654     # EAX = slice-starts-with?(ECX, "Abd")
+ 655     # . . push args
+ 656     68/push  "Abd"/imm32
+ 657     51/push-ECX
+ 658     # . . call
+ 659     e8/call  slice-starts-with?/disp32
+ 660     # . . discard args
+ 661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 662     # check-ints-equal(EAX, 0, msg)
+ 663     # . . push args
+ 664     68/push  "F - test-slice-starts-with-fails"/imm32
+ 665     68/push  0/imm32
+ 666     50/push-EAX
+ 667     # . . call
+ 668     e8/call  check-ints-equal/disp32
+ 669     # . . discard args
+ 670     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 671     # . epilog
+ 672     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 673     5d/pop-to-EBP
+ 674     c3/return
+ 675 
+ 676 test-slice-starts-with-fails-2:
+ 677     # - slice-starts-with?(slice("Abc"), "Ac") == 1
+ 678     # . prolog
+ 679     55/push-EBP
+ 680     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 681     # var slice/ECX
+ 682     68/push  _test-slice-data-3/imm32/end
+ 683     68/push  _test-slice-data-0/imm32/start
+ 684     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 685     # EAX = slice-starts-with?(ECX, "Ac")
+ 686     # . . push args
+ 687     68/push  "Ac"/imm32
+ 688     51/push-ECX
+ 689     # . . call
+ 690     e8/call  slice-starts-with?/disp32
+ 691     # . . discard args
+ 692     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 693     # check-ints-equal(EAX, 0, msg)
+ 694     # . . push args
+ 695     68/push  "F - test-slice-starts-with-fails-2"/imm32
+ 696     68/push  0/imm32
+ 697     50/push-EAX
+ 698     # . . call
+ 699     e8/call  check-ints-equal/disp32
+ 700     # . . discard args
+ 701     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 702     # . epilog
+ 703     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 704     5d/pop-to-EBP
+ 705     c3/return
+ 706 
+ 707 write-slice:  # out : (address buffered-file), s : (address slice)
+ 708     # . prolog
+ 709     55/push-EBP
+ 710     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 711     # . save registers
+ 712     50/push-EAX
+ 713     51/push-ECX
+ 714     52/push-EDX
+ 715     53/push-EBX
+ 716     56/push-ESI
+ 717     57/push-EDI
+ 718     # ESI = s
+ 719     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+ 720     # curr/ECX = s->start
+ 721     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+ 722     # max/ESI = s->end
+ 723     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
+ 724     # EDI = out
+ 725     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+ 726     # EDX = out->length
+ 727     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EDI+12) to EDX
+ 728     # EBX = out->write
+ 729     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(EDI+4) to EBX
+ 730 $write-slice:loop:
+ 731     # if (curr >= max) break
+ 732     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # compare ECX with ESI
+ 733     7d/jump-if-greater-or-equal  $write-slice:loop-end/disp8
+ 734     # if (out->write >= out->length) flush and clear out's stream
+ 735     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX with EDX
+ 736     7c/jump-if-lesser  $write-slice:to-stream/disp8
+ 737     # . persist out->write
+ 738     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
+ 739     # . flush(out)
+ 740     # . . push args
+ 741     57/push-EDI
+ 742     # . . call
+ 743     e8/call  flush/disp32
+ 744     # . . discard args
+ 745     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 746     # . clear-stream(stream = out+4)
+ 747     # . . push args
+ 748     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
+ 749     50/push-EAX
+ 750     # . . call
+ 751     e8/call  clear-stream/disp32
+ 752     # . . discard args
+ 753     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 754     # . out->write must now be 0; update its cache at EBX
+ 755     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+ 756 $write-slice:to-stream:
+ 757     # out->data[out->write] = *in
+ 758     # . AL = *in
+ 759     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 760     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+ 761     # . out->data[out->write] = AL
+ 762     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  3/index/EBX   .           0/r32/AL    0x10/disp8      .                 # copy AL to *(EDI+EBX+16)
+ 763     # ++out->write
+ 764     43/increment-EBX
+ 765     # ++in
+ 766     41/increment-ECX
+ 767     eb/jump  $write-slice:loop/disp8
+ 768 $write-slice:loop-end:
+ 769     # persist necessary variables from registers
+ 770     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
+ 771 $write-slice:end:
+ 772     # . restore registers
+ 773     5f/pop-to-EDI
+ 774     5e/pop-to-ESI
+ 775     5b/pop-to-EBX
+ 776     5a/pop-to-EDX
+ 777     59/pop-to-ECX
+ 778     58/pop-to-EAX
+ 779     # . epilog
+ 780     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 781     5d/pop-to-EBP
+ 782     c3/return
+ 783 
+ 784 test-write-slice:
+ 785     # . prolog
+ 786     55/push-EBP
+ 787     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 788     # setup
+ 789     # . clear-stream(_test-stream)
+ 790     # . . push args
+ 791     68/push  _test-stream/imm32
+ 792     # . . call
+ 793     e8/call  clear-stream/disp32
+ 794     # . . discard args
+ 795     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 796     # . clear-stream(_test-buffered-file+4)
+ 797     # . . push args
+ 798     b8/copy-to-EAX  _test-buffered-file/imm32
+ 799     05/add-to-EAX  4/imm32
+ 800     50/push-EAX
+ 801     # . . call
+ 802     e8/call  clear-stream/disp32
+ 803     # . . discard args
+ 804     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 805     # var slice/ECX = "Abc"
+ 806     68/push  _test-slice-data-3/imm32/end
+ 807     68/push  _test-slice-data-0/imm32/start
+ 808     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 809     # write-slice(_test-buffered-file, slice)
+ 810     # . . push args
+ 811     51/push-ECX
+ 812     68/push  _test-buffered-file/imm32
+ 813     # . . call
+ 814     e8/call  write-slice/disp32
+ 815     # . . discard args
+ 816     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 817     # flush(_test-buffered-file)
+ 818     # . . push args
+ 819     68/push  _test-buffered-file/imm32
+ 820     # . . call
+ 821     e8/call  flush/disp32
+ 822     # . . discard args
+ 823     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 824     # check-stream-equal(_test-stream, "Abc", msg)
+ 825     # . . push args
+ 826     68/push  "F - test-write-slice"/imm32
+ 827     68/push  "Abc"/imm32
+ 828     68/push  _test-stream/imm32
+ 829     # . . call
+ 830     e8/call  check-stream-equal/disp32
+ 831     # . . discard args
+ 832     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 833     # . epilog
+ 834     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 835     5d/pop-to-EBP
+ 836     c3/return
+ 837 
+ 838 # copy a slice into a new (dynamically allocated) string
+ 839 slice-to-string:  # ad : (address allocation-descriptor), in : (address slice) -> out/EAX : (address array)
+ 840     # . prolog
+ 841     55/push-EBP
+ 842     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 843     # . save registers
+ 844     51/push-ECX
+ 845     52/push-EDX
+ 846     53/push-EBX
+ 847     56/push-ESI
+ 848     # ESI = in
+ 849     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+ 850     # curr/EDX = in->start
+ 851     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+ 852     # max/EBX = in->end
+ 853     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(ESI+4) to EBX
+ 854     # size/ECX = max - curr + 4  # total size of output string (including the initial length)
+ 855     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # copy EBX to ECX
+ 856     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # subtract EDX from ECX
+ 857     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               4/imm32           # add to ECX
+ 858     # out/EAX = allocate(ad, size)
+ 859     # . . push args
+ 860     51/push-ECX
+ 861     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 862     # . . call
+ 863     e8/call  allocate/disp32
+ 864     # . . discard args
+ 865     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 866     # if (EAX == 0) abort
+ 867     3d/compare-EAX-and  0/imm32
+ 868     74/jump-if-equal  $slice-to-string:abort/disp8
+ 869     # *out = size-4
+ 870     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
+ 871     81          5/subop/subtract    0/mod/indirect  0/rm32/EAX    .           .             .           .           .               4/imm32           # subtract 4 from *EAX
+ 872     # save out
+ 873     50/push-EAX
+ 874     # EAX = _append-4(EAX+4, EAX+size, curr, max)  # clobbering ECX
+ 875     # . . push args
+ 876     53/push-EBX
+ 877     52/push-EDX
+ 878     # . . push EAX+size (clobbering ECX)
+ 879     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # add EAX to ECX
+ 880     51/push-ECX
+ 881     # . . push EAX+4 (clobbering EAX)
+ 882     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm32           # add to EAX
+ 883     50/push-EAX
+ 884     # . . call
+ 885     e8/call  _append-4/disp32
+ 886     # . . discard args
+ 887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+ 888     # restore out (assumes _append-4 can't error)
+ 889     58/pop-to-EAX
+ 890 $slice-to-string:end:
+ 891     # . restore registers
+ 892     5e/pop-to-ESI
+ 893     5b/pop-to-EBX
+ 894     5a/pop-to-EDX
+ 895     59/pop-to-ECX
+ 896     # . epilog
+ 897     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 898     5d/pop-to-EBP
+ 899     c3/return
+ 900 
+ 901 $slice-to-string:abort:
+ 902     # . _write(2/stderr, error)
+ 903     # . . push args
+ 904     68/push  "slice-to-string: out of space"/imm32
+ 905     68/push  2/imm32/stderr
+ 906     # . . call
+ 907     e8/call  _write/disp32
+ 908     # . . discard args
+ 909     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 910     # . syscall(exit, 1)
+ 911     bb/copy-to-EBX  1/imm32
+ 912     b8/copy-to-EAX  1/imm32/exit
+ 913     cd/syscall  0x80/imm8
+ 914     # never gets here
+ 915 
+ 916 test-slice-to-string:
+ 917     # . prolog
+ 918     55/push-EBP
+ 919     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 920     # var slice/ECX = "Abc"
+ 921     68/push  _test-slice-data-3/imm32/end
+ 922     68/push  _test-slice-data-0/imm32/start
+ 923     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 924     # EAX = slice-to-string(Heap, slice)
+ 925     # . . push args
+ 926     51/push-ECX
+ 927     68/push  Heap/imm32
+ 928     # . . call
+ 929     e8/call  slice-to-string/disp32
+ 930     # . . discard args
+ 931     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 932 +-- 34 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+ 966     # EAX = string-equal?(EAX, "Abc")
+ 967     # . . push args
+ 968     68/push  "Abc"/imm32
+ 969     50/push-EAX
+ 970     # . . call
+ 971     e8/call  string-equal?/disp32
+ 972     # . . discard args
+ 973     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 974     # check-ints-equal(EAX, 1, msg)
+ 975     # . . push args
+ 976     68/push  "F - test-slice-to-string"/imm32
+ 977     68/push  1/imm32/true
+ 978     50/push-EAX
+ 979     # . . call
+ 980     e8/call  check-ints-equal/disp32
+ 981     # . . discard args
+ 982     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 983     # . epilog
+ 984     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 985     5d/pop-to-EBP
+ 986     c3/return
+ 987 
+ 988 == data
+ 989 
+ 990 _test-slice-data-0:
+ 991     41/A
+ 992 _test-slice-data-1:
+ 993     62/b
+ 994 _test-slice-data-2:
+ 995     63/c
+ 996 _test-slice-data-3:
+ 997     64/d
+ 998 _test-slice-data-4:
+ 999 
+1000 # . _. vim:nowrap:textwidth=0
 
diff --git a/html/subx/073next-token.subx.html b/html/subx/073next-token.subx.html index 2ddf6c3a..3febade6 100644 --- a/html/subx/073next-token.subx.html +++ b/html/subx/073next-token.subx.html @@ -712,16 +712,16 @@ if ('onhashchange' in window) { 652 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX 653 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 654 # if (EAX == ' ') break -655 3d/compare-with-EAX 0x20/imm32/space +655 3d/compare-EAX-and 0x20/imm32/space 656 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 657 # if (EAX == '\n') break -658 3d/compare-with-EAX 0x0a/imm32/newline +658 3d/compare-EAX-and 0x0a/imm32/newline 659 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 660 # if (EAX == '\t') break -661 3d/compare-with-EAX 0x09/imm32/tab +661 3d/compare-EAX-and 0x09/imm32/tab 662 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 663 # if (EAX == '\r') break -664 3d/compare-with-EAX 0x0d/imm32/cr +664 3d/compare-EAX-and 0x0d/imm32/cr 665 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 666 # ++in->read 667 41/inc-ECX @@ -763,7 +763,7 @@ if ('onhashchange' in window) { 703 8a/copy-byte 0/mod/indirect 0/rm32/EAX . . . 3/r32/BL . . # copy byte at *EAX to BL 704 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX and EDX 705 75/jump-if-not-equal $skip-chars-matching-in-slice:end/disp8 -706 # ++in->read +706 # ++curr 707 40/inc-EAX 708 eb/jump $skip-chars-matching-in-slice:loop/disp8 709 $skip-chars-matching-in-slice:end: @@ -859,7 +859,7 @@ if ('onhashchange' in window) { 799 8a/copy-byte 0/mod/indirect 0/rm32/EAX . . . 3/r32/BL . . # copy byte at *EAX to BL 800 39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX and EDX 801 74/jump-if-equal $skip-chars-not-matching-in-slice:end/disp8 -802 # ++in->read +802 # ++curr 803 40/inc-EAX 804 eb/jump $skip-chars-not-matching-in-slice:loop/disp8 805 $skip-chars-not-matching-in-slice:end: diff --git a/html/subx/apps/assort.subx.html b/html/subx/apps/assort.subx.html new file mode 100644 index 00000000..8db118f3 --- /dev/null +++ b/html/subx/apps/assort.subx.html @@ -0,0 +1,1217 @@ + + + + +Mu - subx/apps/assort.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/apps/assort.subx +
+   1 # Read a series of segments from stdin and concatenate segments with the same
+   2 # name on stdout.
+   3 #
+   4 # Segments are emitted in order of first encounter.
+   5 #
+   6 # Drop lines that are all comments. They could get misleading after assortment
+   7 # because we don't know if they refer to the line above or the line below.
+   8 #
+   9 # To run (from the subx/ directory):
+  10 #   $ ./subx translate *.subx apps/assort.subx -o apps/assort
+  11 #   $ cat x
+  12 #   == code
+  13 #   abc
+  14 #   == code
+  15 #   def
+  16 #   $ cat x  |./subx run apps/assort
+  17 #   == code
+  18 #   abc
+  19 #   def
+  20 
+  21 == code
+  22 #   instruction                     effective address                                                   register    displacement    immediate
+  23 # . op          subop               mod             rm32          base        index         scale       r32
+  24 # . 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
+  25 
+  26 Entry:  # run tests if necessary, convert stdin if not
+  27 
+  28     # for debugging: run a single test
+  29 #?     e8/call test-get-or-insert-segment/disp32
+  30 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+  31 #?     eb/jump  $main:end/disp8
+  32 
+  33     # . prolog
+  34     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  35     # - if argc > 1 and argv[1] == "test", then return run_tests()
+  36     # . argc > 1
+  37     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
+  38     7e/jump-if-lesser-or-equal  $run-main/disp8
+  39     # . argv[1] == "test"
+  40     # . . push args
+  41     68/push  "test"/imm32
+  42     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+  43     # . . call
+  44     e8/call  kernel-string-equal?/disp32
+  45     # . . discard args
+  46     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  47     # . check result
+  48     3d/compare-EAX-and  1/imm32
+  49     75/jump-if-not-equal  $run-main/disp8
+  50     # . run-tests()
+  51     e8/call  run-tests/disp32
+  52     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+  53     eb/jump  $main:end/disp8
+  54 $run-main:
+  55     # - otherwise convert stdin
+  56     # var ed/EAX : exit-descriptor
+  57     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+  58     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
+  59     # configure ed to really exit()
+  60     # . ed->target = 0
+  61     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+  62     # return convert(Stdin, 1/stdout, 2/stderr, ed)
+  63     # . . push args
+  64     50/push-EAX/ed
+  65     68/push  Stderr/imm32
+  66     68/push  Stdout/imm32
+  67     68/push  Stdin/imm32
+  68     # . . call
+  69     e8/call  convert/disp32
+  70     # . . discard args
+  71     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+  72     # . syscall(exit, 0)
+  73     bb/copy-to-EBX  0/imm32
+  74 $main:end:
+  75     b8/copy-to-EAX  1/imm32/exit
+  76     cd/syscall  0x80/imm8
+  77 
+  78 # data structure:
+  79 #   row: pair of (address array byte) and (address stream byte)
+  80 #   table: (address stream row)
+  81 
+  82 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+  83     # pseudocode:
+  84     #   var table : (address stream) = new-stream(10 rows, 8 bytes each)
+  85     #   read-segments(in, table)
+  86     #   write-segments(out, table)
+  87     #
+  88     # . prolog
+  89     55/push-EBP
+  90     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  91     # . save registers
+  92     51/push-ECX
+  93     # var table/ECX : (address stream byte) = stream(10 * 8)
+  94     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x50/imm32        # subtract from ESP
+  95     68/push  0x50/imm32/length
+  96     68/push  0/imm32/read
+  97     68/push  0/imm32/write
+  98     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+  99     # clear-stream(table)
+ 100     # . . push args
+ 101     51/push-ECX
+ 102     # . . call
+ 103     e8/call  clear-stream/disp32
+ 104     # . . discard args
+ 105     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 106 $convert:read:
+ 107     # read-segments(in, table)
+ 108     # . . push args
+ 109     51/push-ECX
+ 110     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 111     # . . call
+ 112     e8/call  read-segments/disp32
+ 113     # . . discard args
+ 114     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 115 $convert:write:
+ 116     # write-segments(out, table)
+ 117     # . . push args
+ 118     51/push-ECX
+ 119     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 120     # . . call
+ 121     e8/call  write-segments/disp32
+ 122     # . . discard args
+ 123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 124 $convert:end:
+ 125     # . reclaim locals
+ 126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x5c/imm32        # add to ESP
+ 127     # . restore registers
+ 128     59/pop-to-ECX
+ 129     # . epilog
+ 130     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 131     5d/pop-to-EBP
+ 132     c3/return
+ 133 
+ 134 #? test-convert:
+ 135 #?     # . prolog
+ 136 #?     55/push-EBP
+ 137 #?     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 138 #?     # setup
+ 139 #?     # . clear-stream(_test-input-stream)
+ 140 #?     # . . push args
+ 141 #?     68/push  _test-input-stream/imm32
+ 142 #?     # . . call
+ 143 #?     e8/call  clear-stream/disp32
+ 144 #?     # . . discard args
+ 145 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 146 #?     # . clear-stream(_test-input-buffered-file+4)
+ 147 #?     # . . push args
+ 148 #?     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 149 #?     05/add-to-EAX  4/imm32
+ 150 #?     50/push-EAX
+ 151 #?     # . . call
+ 152 #?     e8/call  clear-stream/disp32
+ 153 #?     # . . discard args
+ 154 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 155 #?     # . clear-stream(_test-output-stream)
+ 156 #?     # . . push args
+ 157 #?     68/push  _test-output-stream/imm32
+ 158 #?     # . . call
+ 159 #?     e8/call  clear-stream/disp32
+ 160 #?     # . . discard args
+ 161 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 162 #?     # . clear-stream(_test-output-buffered-file+4)
+ 163 #?     # . . push args
+ 164 #?     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 165 #?     05/add-to-EAX  4/imm32
+ 166 #?     50/push-EAX
+ 167 #?     # . . call
+ 168 #?     e8/call  clear-stream/disp32
+ 169 #?     # . . discard args
+ 170 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 171 #?     # initialize input (meta comments in parens)
+ 172 #?     #   # comment 1
+ 173 #?     #     # comment 2 indented
+ 174 #?     #   == code  (new segment)
+ 175 #?     #   # comment 3 inside a segment
+ 176 #?     #   1
+ 177 #?     #                         (empty line)
+ 178 #?     #   2 3 # comment 4 inline with other contents
+ 179 #?     #   == data  (new segment)
+ 180 #?     #   4 5/imm32
+ 181 #?     #   == code  (existing segment but non-contiguous with previous iteration)
+ 182 #?     #   6 7
+ 183 #?     #   8 9  (multiple lines)
+ 184 #?     #   == code  (existing segment contiguous with previous iteration)
+ 185 #?     #   10 11
+ 186 #?     # . write(_test-input-stream, "# comment 1")
+ 187 #?     # . . push args
+ 188 #?     68/push  "# comment 1"/imm32
+ 189 #?     68/push  _test-input-stream/imm32
+ 190 #?     # . . call
+ 191 #?     e8/call  write/disp32
+ 192 #?     # . . discard args
+ 193 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 194 #?     # . write(_test-input-stream, "\n")
+ 195 #?     # . . push args
+ 196 #?     68/push  Newline/imm32
+ 197 #?     68/push  _test-input-stream/imm32
+ 198 #?     # . . call
+ 199 #?     e8/call  write/disp32
+ 200 #?     # . . discard args
+ 201 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 202 #?     # . write(_test-input-stream, "  # comment 2 indented")
+ 203 #?     # . . push args
+ 204 #?     68/push  "  # comment 2 indented"/imm32
+ 205 #?     68/push  _test-input-stream/imm32
+ 206 #?     # . . call
+ 207 #?     e8/call  write/disp32
+ 208 #?     # . . discard args
+ 209 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 210 #?     # . write(_test-input-stream, "\n")
+ 211 #?     # . . push args
+ 212 #?     68/push  Newline/imm32
+ 213 #?     68/push  _test-input-stream/imm32
+ 214 #?     # . . call
+ 215 #?     e8/call  write/disp32
+ 216 #?     # . . discard args
+ 217 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 218 #?     # . write(_test-input-stream, "== code")
+ 219 #?     # . . push args
+ 220 #?     68/push  "== code"/imm32
+ 221 #?     68/push  _test-input-stream/imm32
+ 222 #?     # . . call
+ 223 #?     e8/call  write/disp32
+ 224 #?     # . . discard args
+ 225 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 226 #?     # . write(_test-input-stream, "\n")
+ 227 #?     # . . push args
+ 228 #?     68/push  Newline/imm32
+ 229 #?     68/push  _test-input-stream/imm32
+ 230 #?     # . . call
+ 231 #?     e8/call  write/disp32
+ 232 #?     # . . discard args
+ 233 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 234 #?     # . write(_test-input-stream, "# comment 3 inside a segment")
+ 235 #?     # . . push args
+ 236 #?     68/push  "# comment 3 inside a segment"/imm32
+ 237 #?     68/push  _test-input-stream/imm32
+ 238 #?     # . . call
+ 239 #?     e8/call  write/disp32
+ 240 #?     # . . discard args
+ 241 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 242 #?     # . write(_test-input-stream, "\n")
+ 243 #?     # . . push args
+ 244 #?     68/push  Newline/imm32
+ 245 #?     68/push  _test-input-stream/imm32
+ 246 #?     # . . call
+ 247 #?     e8/call  write/disp32
+ 248 #?     # . . discard args
+ 249 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 250 #?     # . write(_test-input-stream, "1")
+ 251 #?     # . . push args
+ 252 #?     68/push  "1"/imm32
+ 253 #?     68/push  _test-input-stream/imm32
+ 254 #?     # . . call
+ 255 #?     e8/call  write/disp32
+ 256 #?     # . . discard args
+ 257 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 258 #?     # . write(_test-input-stream, "\n")
+ 259 #?     # . . push args
+ 260 #?     68/push  Newline/imm32
+ 261 #?     68/push  _test-input-stream/imm32
+ 262 #?     # . . call
+ 263 #?     e8/call  write/disp32
+ 264 #?     # . . discard args
+ 265 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 266 #?     # . write(_test-input-stream, "\n")  # empty line
+ 267 #?     # . . push args
+ 268 #?     68/push  Newline/imm32
+ 269 #?     68/push  _test-input-stream/imm32
+ 270 #?     # . . call
+ 271 #?     e8/call  write/disp32
+ 272 #?     # . . discard args
+ 273 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 274 #?     # . write(_test-input-stream, "2 3 # comment 4 inline with other comments")
+ 275 #?     # . . push args
+ 276 #?     68/push  "2 3 # comment 4 inline with other comments"/imm32
+ 277 #?     68/push  _test-input-stream/imm32
+ 278 #?     # . . call
+ 279 #?     e8/call  write/disp32
+ 280 #?     # . . discard args
+ 281 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 282 #?     # . write(_test-input-stream, "\n")
+ 283 #?     # . . push args
+ 284 #?     68/push  Newline/imm32
+ 285 #?     68/push  _test-input-stream/imm32
+ 286 #?     # . . call
+ 287 #?     e8/call  write/disp32
+ 288 #?     # . . discard args
+ 289 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 290 #?     # . write(_test-input-stream, "== data")
+ 291 #?     # . . push args
+ 292 #?     68/push  "== data"/imm32
+ 293 #?     68/push  _test-input-stream/imm32
+ 294 #?     # . . call
+ 295 #?     e8/call  write/disp32
+ 296 #?     # . . discard args
+ 297 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 298 #?     # . write(_test-input-stream, "\n")
+ 299 #?     # . . push args
+ 300 #?     68/push  Newline/imm32
+ 301 #?     68/push  _test-input-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 #?     # . write(_test-input-stream, "4 5/imm32")
+ 307 #?     # . . push args
+ 308 #?     68/push  "4 5/imm32"/imm32
+ 309 #?     68/push  _test-input-stream/imm32
+ 310 #?     # . . call
+ 311 #?     e8/call  write/disp32
+ 312 #?     # . . discard args
+ 313 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 314 #?     # . write(_test-input-stream, "\n")
+ 315 #?     # . . push args
+ 316 #?     68/push  Newline/imm32
+ 317 #?     68/push  _test-input-stream/imm32
+ 318 #?     # . . call
+ 319 #?     e8/call  write/disp32
+ 320 #?     # . . discard args
+ 321 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 322 #?     # . write(_test-input-stream, "== code")
+ 323 #?     # . . push args
+ 324 #?     68/push  "== code"/imm32
+ 325 #?     68/push  _test-input-stream/imm32
+ 326 #?     # . . call
+ 327 #?     e8/call  write/disp32
+ 328 #?     # . . discard args
+ 329 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 330 #?     # . write(_test-input-stream, "\n")
+ 331 #?     # . . push args
+ 332 #?     68/push  Newline/imm32
+ 333 #?     68/push  _test-input-stream/imm32
+ 334 #?     # . . call
+ 335 #?     e8/call  write/disp32
+ 336 #?     # . . discard args
+ 337 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 338 #?     # . write(_test-input-stream, "6 7")
+ 339 #?     # . . push args
+ 340 #?     68/push  "6 7"/imm32
+ 341 #?     68/push  _test-input-stream/imm32
+ 342 #?     # . . call
+ 343 #?     e8/call  write/disp32
+ 344 #?     # . . discard args
+ 345 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 346 #?     # . write(_test-input-stream, "\n")
+ 347 #?     # . . push args
+ 348 #?     68/push  Newline/imm32
+ 349 #?     68/push  _test-input-stream/imm32
+ 350 #?     # . . call
+ 351 #?     e8/call  write/disp32
+ 352 #?     # . . discard args
+ 353 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 354 #?     # . write(_test-input-stream, "8 9")
+ 355 #?     # . . push args
+ 356 #?     68/push  "6 7"/imm32
+ 357 #?     68/push  _test-input-stream/imm32
+ 358 #?     # . . call
+ 359 #?     e8/call  write/disp32
+ 360 #?     # . . discard args
+ 361 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 362 #?     # . write(_test-input-stream, "\n")
+ 363 #?     # . . push args
+ 364 #?     68/push  Newline/imm32
+ 365 #?     68/push  _test-input-stream/imm32
+ 366 #?     # . . call
+ 367 #?     e8/call  write/disp32
+ 368 #?     # . . discard args
+ 369 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 370 #?     # . write(_test-input-stream, "== code")
+ 371 #?     # . . push args
+ 372 #?     68/push  "== code"/imm32
+ 373 #?     68/push  _test-input-stream/imm32
+ 374 #?     # . . call
+ 375 #?     e8/call  write/disp32
+ 376 #?     # . . discard args
+ 377 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 378 #?     # . write(_test-input-stream, "\n")
+ 379 #?     # . . push args
+ 380 #?     68/push  Newline/imm32
+ 381 #?     68/push  _test-input-stream/imm32
+ 382 #?     # . . call
+ 383 #?     e8/call  write/disp32
+ 384 #?     # . . discard args
+ 385 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 386 #?     # . write(_test-input-stream, "10 11")
+ 387 #?     # . . push args
+ 388 #?     68/push  "10 11"/imm32
+ 389 #?     68/push  _test-input-stream/imm32
+ 390 #?     # . . call
+ 391 #?     e8/call  write/disp32
+ 392 #?     # . . discard args
+ 393 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 394 #?     # . write(_test-input-stream, "\n")
+ 395 #?     # . . push args
+ 396 #?     68/push  Newline/imm32
+ 397 #?     68/push  _test-input-stream/imm32
+ 398 #?     # . . call
+ 399 #?     e8/call  write/disp32
+ 400 #?     # . . discard args
+ 401 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 402 #?     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 403 #?     # . . push args
+ 404 #?     68/push  _test-output-buffered-file/imm32
+ 405 #?     68/push  _test-input-buffered-file/imm32
+ 406 #?     # . . call
+ 407 #?     e8/call  convert/disp32
+ 408 #?     # . . discard args
+ 409 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 410 #?     # check output
+ 411 #?     #   == code
+ 412 #?     #   1
+ 413 #?     #   2 3 # comment 4 inline with other contents
+ 414 #?     #   6 7
+ 415 #?     #   8 9
+ 416 #?     #   10 11
+ 417 #?     #   == data
+ 418 #?     #   4 5/imm32
+ 419 +-- 34 lines: #? #?     # debug print ------------------------------------------------------------------------------------------------------------------------
+ 453 #?     # . flush(_test-output-buffered-file)
+ 454 #?     # . . push args
+ 455 #?     68/push  _test-output-buffered-file/imm32
+ 456 #?     # . . call
+ 457 #?     e8/call  flush/disp32
+ 458 #?     # . . discard args
+ 459 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 460 #?     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
+ 461 #?     # . . push args
+ 462 #?     68/push  "F - test-convert-code-and-data-segments/0"/imm32
+ 463 #?     68/push  "== code"/imm32
+ 464 #?     68/push  _test-output-stream/imm32
+ 465 #?     # . . call
+ 466 #?     e8/call  check-next-stream-line-equal/disp32
+ 467 #?     # . . discard args
+ 468 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 469 #?     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
+ 470 #?     # . . push args
+ 471 #?     68/push  "F - test-convert-code-and-data-segments/1"/imm32
+ 472 #?     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
+ 473 #?     68/push  _test-output-stream/imm32
+ 474 #?     # . . call
+ 475 #?     e8/call  check-next-stream-line-equal/disp32
+ 476 #?     # . . discard args
+ 477 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 478 #?     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
+ 479 #?     # . . push args
+ 480 #?     68/push  "F - test-convert-code-and-data-segments/2"/imm32
+ 481 #?     68/push  "2 3 # comment 4 inline with other contents"/imm32
+ 482 #?     68/push  _test-output-stream/imm32
+ 483 #?     # . . call
+ 484 #?     e8/call  check-next-stream-line-equal/disp32
+ 485 #?     # . . discard args
+ 486 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 487 #?     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
+ 488 #?     # . . push args
+ 489 #?     68/push  "F - test-convert-code-and-data-segments/3"/imm32
+ 490 #?     68/push  "6 7"/imm32
+ 491 #?     68/push  _test-output-stream/imm32
+ 492 #?     # . . call
+ 493 #?     e8/call  check-next-stream-line-equal/disp32
+ 494 #?     # . . discard args
+ 495 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 496 #?     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
+ 497 #?     # . . push args
+ 498 #?     68/push  "F - test-convert-code-and-data-segments/4"/imm32
+ 499 #?     68/push  "8 9"/imm32
+ 500 #?     68/push  _test-output-stream/imm32
+ 501 #?     # . . call
+ 502 #?     e8/call  check-next-stream-line-equal/disp32
+ 503 #?     # . . discard args
+ 504 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 505 #?     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
+ 506 #?     # . . push args
+ 507 #?     68/push  "F - test-convert-code-and-data-segments/5"/imm32
+ 508 #?     68/push  "10 11"/imm32
+ 509 #?     68/push  _test-output-stream/imm32
+ 510 #?     # . . call
+ 511 #?     e8/call  check-next-stream-line-equal/disp32
+ 512 #?     # . . discard args
+ 513 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 514 #?     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
+ 515 #?     # . . push args
+ 516 #?     68/push  "F - test-convert-code-and-data-segments/6"/imm32
+ 517 #?     68/push  "== data"/imm32
+ 518 #?     68/push  _test-output-stream/imm32
+ 519 #?     # . . call
+ 520 #?     e8/call  check-next-stream-line-equal/disp32
+ 521 #?     # . . discard args
+ 522 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 523 #?     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
+ 524 #?     # . . push args
+ 525 #?     68/push  "F - test-convert-code-and-data-segments/4"/imm32
+ 526 #?     68/push  "4 5/imm32"/imm32
+ 527 #?     68/push  _test-output-stream/imm32
+ 528 #?     # . . call
+ 529 #?     e8/call  check-next-stream-line-equal/disp32
+ 530 #?     # . . discard args
+ 531 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 532 #?     # . epilog
+ 533 #?     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 534 #?     5d/pop-to-EBP
+ 535 #?     c3/return
+ 536 
+ 537 read-segments:  # in : (address buffered-file), table : (address stream row)
+ 538     # pseudocode:
+ 539     #   var curr-segment = null
+ 540     #   var line = new-stream(512, 1)
+ 541     #   while true
+ 542     #     clear-stream(line)
+ 543     #     read-line(in, line)
+ 544     #     if (line->write == 0) break             # end of file
+ 545     #     var word-slice = next-word(line)
+ 546     #     if slice-empty?(word-slice)             # whitespace
+ 547     #       continue
+ 548     #     if slice-starts-with?(word-slice, "#")  # comment
+ 549     #       continue
+ 550     #     if (slice-equal?(word-slice, "=="))
+ 551     #       var segment-name = next-word(line)
+ 552     #       curr-segment = get-or-insert-segment(table, segment-name, N)
+ 553     #     else
+ 554     #       write-stream-data(curr-segment, line)
+ 555     #
+ 556     # . prolog
+ 557     55/push-EBP
+ 558     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 559     # . save registers
+ 560     51/push-ECX
+ 561     # var line/ECX : (address stream byte) = stream(512)
+ 562     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
+ 563     68/push  0x200/imm32/length
+ 564     68/push  0/imm32/read
+ 565     68/push  0/imm32/write
+ 566     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 567     # var word-slice/EDX = {0, 0}
+ 568     68/push  0/imm32/end
+ 569     68/push  0/imm32/curr
+ 570     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 571     # var curr-segment/EBX = null
+ 572     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+ 573 $read-segments:loop:
+ 574     # clear-stream(line)
+ 575     # . . push args
+ 576     51/push-ECX
+ 577     # . . call
+ 578     e8/call  clear-stream/disp32
+ 579     # . . discard args
+ 580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 581     # read-line(in, line)
+ 582     # . . push args
+ 583     51/push-ECX
+ 584     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 585     # . . call
+ 586     e8/call  read-line/disp32
+ 587     # . . discard args
+ 588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 589 $read-segments:check0:
+ 590     # if (line->write == 0) break
+ 591     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
+ 592     0f 84/jump-if-equal  $read-segments:break/disp32
+ 593 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+ 627     # next-word(line, word-slice)
+ 628     # . . push args
+ 629     52/push-EDX
+ 630     51/push-ECX
+ 631     # . . call
+ 632     e8/call  next-word/disp32
+ 633     # . . discard args
+ 634     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 635 $read-segments:check1:
+ 636     # if (slice-empty?(word-slice)) continue
+ 637     # . EAX = slice-empty?(word-slice)
+ 638     # . . push args
+ 639     52/push-EDX
+ 640     # . . call
+ 641     e8/call  slice-empty?/disp32
+ 642     # . . discard args
+ 643     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 644     # . if (EAX != 0) continue
+ 645     3d/compare-EAX-and  0/imm32
+ 646     0f 85/jump-if-not-equal  $read-segments:loop/disp32
+ 647 $read-segments:check-for-comment:
+ 648     # if (slice-starts-with?(word-slice, "#"))
+ 649     # . start/EDX = word-slice->start
+ 650     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+ 651     # . c/EAX = *start
+ 652     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 653     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
+ 654     # . if (EAX == '#') continue
+ 655     3d/compare-EAX-and  0x23/imm32/hash
+ 656     74/jump-if-equal  $read-segments:loop/disp8
+ 657 $read-segments:check-for-segment-header:
+ 658 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+ 708     # if (slice-equal?(word-slice, "=="))
+ 709     #   segment-name = next-word(line)
+ 710     #   curr-segment = get-or-insert(table, segment-name)
+ 711     # . EAX = slice-equal?(word-slice, "==")
+ 712     # . . push args
+ 713     68/push  "=="/imm32
+ 714     52/push-EDX
+ 715     # . . call
+ 716     e8/call  slice-equal?/disp32
+ 717     # . . discard args
+ 718     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 719     # . if (EAX == 0) goto check3
+ 720     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
+ 721     0f 84/jump-if-equal  $read-segments:regular-line/disp32
+ 722     # . next-word(line, segment-name)
+ 723     # . . push args
+ 724     52/push-EDX
+ 725     51/push-ECX
+ 726     # . . call
+ 727     e8/call  next-word/disp32
+ 728     # . . discard args
+ 729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 730 +-- 50 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
+ 780     # . EAX = get-or-insert-segment(table, segment-name, N)
+ 781     # . . push args
+ 782     68/push  0x1000/imm32/segment-size/4KB
+ 783     52/push-EDX
+ 784     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 785     # . . call
+ 786     e8/call  get-or-insert-segment/disp32
+ 787     # . . discard args
+ 788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 789     # . curr-segment = EAX
+ 790     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+ 791     # . continue
+ 792     e9/jump  $read-segments:loop/disp32
+ 793 $read-segments:regular-line:
+ 794     # write-stream-data(curr-segment, line)
+ 795     # . . push args
+ 796     51/push-ECX
+ 797     53/push-EBX
+ 798     # . . call
+ 799     e8/call  write-stream-data/disp32
+ 800     # . . discard args
+ 801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 802     # loop
+ 803     e9/jump  $read-segments:loop/disp32
+ 804 $read-segments:break:
+ 805 $read-segments:end:
+ 806     # . reclaim locals
+ 807     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
+ 808     # . restore registers
+ 809     59/pop-to-ECX
+ 810     # . epilog
+ 811     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 812     5d/pop-to-EBP
+ 813     c3/return
+ 814 
+ 815 write-segments:  # out : (address buffered-file), table : (address stream row)
+ 816     # pseudocode:
+ 817     #   name, stream = table[0]
+ 818     #   var i = 0
+ 819     #   while i < table.length
+ 820     #     name = table[i].name
+ 821     #     if (name == null) break
+ 822     #     write-buffered(out, "== ")
+ 823     #     write-buffered(out, name)
+ 824     #     write-buffered(out, "\n")
+ 825     #     stream = table[i].stream
+ 826     #     write-stream-data(out, stream)
+ 827     #     ++i
+ 828     #   flush(out)
+ 829     #
+ 830     # . prolog
+ 831     55/push-EBP
+ 832     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 833     # . save registers
+ 834 $write-segments:end:
+ 835     # . reclaim locals
+ 836     # . restore registers
+ 837     # . epilog
+ 838     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 839     5d/pop-to-EBP
+ 840     c3/return
+ 841 
+ 842 ## helpers
+ 843 
+ 844 # TODO: pass in an allocation descriptor
+ 845 get-or-insert-segment:  # table : (address stream row), s : (address slice), n : int -> EAX : (address stream)
+ 846     # pseudocode:
+ 847     #   curr = table->data
+ 848     #   max = &table->data[table->write]
+ 849     #   while curr < max
+ 850     #     if slice-equal?(s, *curr)
+ 851     #       return *(curr+4)
+ 852     #     curr += 8
+ 853     #   if table->write < table->length
+ 854     #     *max = slice-to-string(Heap, s)
+ 855     #     result = new-stream(Heap, n, 1)
+ 856     #     *(max+4) = result
+ 857     #     table->write += 8
+ 858     #     return result
+ 859     #   return 0
+ 860     #
+ 861     # . prolog
+ 862     55/push-EBP
+ 863     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 864     # . save registers
+ 865     51/push-ECX
+ 866     52/push-EDX
+ 867     56/push-ESI
+ 868     # ESI = table
+ 869     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 870     # curr/ECX = table->data
+ 871     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
+ 872     # max/EDX = table->data + table->write
+ 873     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+ 874     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
+ 875 $get-or-insert-segment:search-loop:
+ 876     # if (curr >= max) break
+ 877     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
+ 878     7d/jump-if-greater-or-equal  $get-or-insert-segment:not-found/disp8
+ 879     # if (slice-equal?(s, *curr)) return *(curr+4)
+ 880     # . EAX = slice-equal?(s, *curr)
+ 881     # . . push args
+ 882     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+ 883     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 884     # . . call
+ 885     e8/call  slice-equal?/disp32
+ 886     # . . discard args
+ 887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 888     # . if (EAX != 0) return EAX = *(curr+4)
+ 889     3d/compare-EAX-and  0/imm32
+ 890     74/jump-if-equal  $get-or-insert-segment:mismatch/disp8
+ 891     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+ 892     eb/jump  $get-or-insert-segment:end/disp8
+ 893 $get-or-insert-segment:mismatch:
+ 894     # curr += 8
+ 895     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
+ 896     # loop
+ 897     eb/jump  $get-or-insert-segment:search-loop/disp8
+ 898 $get-or-insert-segment:not-found:
+ 899     # result/EAX = 0
+ 900     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 901     # if (table->write >= table->length) abort
+ 902     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+ 903     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
+ 904     7d/jump-if-greater-or-equal  $get-or-insert-segment:abort/disp8
+ 905     # *max = slice-to-string(Heap, s)
+ 906     # . EAX = slice-to-string(Heap, s)
+ 907     # . . push args
+ 908     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 909     68/push  Heap/imm32
+ 910     # . . call
+ 911     e8/call  slice-to-string/disp32
+ 912     # . . discard args
+ 913     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 914     # . *max = EAX <= writes to 0x0a003873
+ 915     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
+ 916     # result/EAX = new-stream(Heap, n, 1)
+ 917     # . . push args
+ 918     68/push  1/imm32
+ 919     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+ 920     68/push  Heap/imm32
+ 921     # . . call
+ 922     e8/call  new-stream/disp32
+ 923     # . . discard args
+ 924     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 925     # *(max+4) = result
+ 926     89/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDX+4)
+ 927     # table->write += 8
+ 928     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               8/imm32           # add to *ESI
+ 929 $get-or-insert-segment:end:
+ 930     # . restore registers
+ 931     5e/pop-to-ESI
+ 932     5a/pop-to-EDX
+ 933     59/pop-to-ECX
+ 934     # . epilog
+ 935     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 936     5d/pop-to-EBP
+ 937     c3/return
+ 938 
+ 939 $get-or-insert-segment:abort:
+ 940     # . _write(2/stderr, error)
+ 941     # . . push args
+ 942     68/push  "get-or-insert-segment: too many segments"/imm32
+ 943     68/push  2/imm32/stderr
+ 944     # . . call
+ 945     e8/call  _write/disp32
+ 946     # . . discard args
+ 947     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 948     # . syscall(exit, 1)
+ 949     bb/copy-to-EBX  1/imm32
+ 950     b8/copy-to-EAX  1/imm32/exit
+ 951     cd/syscall  0x80/imm8
+ 952     # never gets here
+ 953 
+ 954 test-get-or-insert-segment:
+ 955     # . prolog
+ 956     55/push-EBP
+ 957     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 958     # var table/ECX : (address stream byte) = stream(2 * 8)
+ 959     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
+ 960     68/push  0x10/imm32/length
+ 961     68/push  0/imm32/read
+ 962     68/push  0/imm32/write
+ 963     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 964     # EDX : (address slice) = "code"
+ 965     68/push  _test-code-segment-end/imm32/end
+ 966     68/push  _test-code-segment/imm32/start
+ 967     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 968 $test-get-or-insert-segment:first-call:
+ 969     # - start with an empty table, insert one segment, verify that it was inserted
+ 970     # segment/EAX = get-or-insert-segment(table, "code" slice, 10)
+ 971     # . . push args
+ 972     68/push  0xa/imm32/segment-length
+ 973     52/push-EDX
+ 974     51/push-ECX
+ 975     # . . call
+ 976     e8/call  get-or-insert-segment/disp32
+ 977     # . . discard args
+ 978     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 979     # save segment
+ 980     50/push-EAX
+ 981     # if (segment != 0) goto next check
+ 982     3d/compare-EAX-and  0/imm32
+ 983     75/jump-if-not-equal  $test-get-or-insert-segment:check1/disp8
+ 984     # fail test
+ 985     # . _write(2/stderr, msg)
+ 986     # . . push args
+ 987     68/push  "F - test-get-or-insert-segment/0"/imm32
+ 988     68/push  2/imm32/stderr
+ 989     # . . call
+ 990     e8/call  _write/disp32
+ 991     # . . discard args
+ 992     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 993     # . _write(2/stderr, Newline)
+ 994     # . . push args
+ 995     68/push  Newline/imm32
+ 996     68/push  2/imm32/stderr
+ 997     # . . call
+ 998     e8/call  _write/disp32
+ 999     # . . discard args
+1000     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1001     # . increment Num-test-failures
+1002     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Num-test-failures/disp32          # increment *Num-test-failures
+1003     e9/jump  $test-get-or-insert-segment:end/disp32
+1004 $test-get-or-insert-segment:check1:
+1005     # check-ints-equal(segment->length, 10, msg)
+1006     # . . push args
+1007     68/push  "F - test-get-or-insert-segment/1"/imm32
+1008     68/push  0xa/imm32/segment-length
+1009     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
+1010     # . . call
+1011     e8/call  check-ints-equal/disp32
+1012     # . . discard args
+1013     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1014 $test-get-or-insert-segment:check2:
+1015     # check-ints-equal(table->write, rowsize = 8, msg)
+1016     # . . push args
+1017     68/push  "F - test-get-or-insert-segment/2"/imm32
+1018     68/push  8/imm32/row-size
+1019     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+1020     # . . call
+1021     e8/call  check-ints-equal/disp32
+1022     # . . discard args
+1023     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1024     # EAX = string-equal?(*table->data, "code")
+1025     # . . push args
+1026     68/push  "code"/imm32
+1027     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
+1028     # . . call
+1029     e8/call  string-equal?/disp32
+1030     # . . discard args
+1031     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1032     # check-ints-equal(EAX, 1, msg)
+1033     # . . push args
+1034     68/push  "F - test-get-or-insert-segment/3"/imm32
+1035     68/push  1/imm32
+1036     50/push-EAX
+1037     # . . call
+1038     e8/call  check-ints-equal/disp32
+1039     # . . discard args
+1040     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1041 $test-get-or-insert-segment:check3:
+1042     # stream/EAX = *(table->data+4)
+1043     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(ECX+16) to EAX
+1044     # check-ints-equal(stream->length, 10, msg)
+1045     # . . push args
+1046     68/push  "F - test-get-or-insert-segment/4"/imm32
+1047     68/push  0xa/imm32/segment-size
+1048     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
+1049     # . . call
+1050     e8/call  check-ints-equal/disp32
+1051     # . . discard args
+1052     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1053 $test-get-or-insert-segment:second-call:
+1054     # - insert the same segment name again, verify that it was reused
+1055     # segment2/EAX = get-or-insert-segment(table, "code" slice, 8)
+1056     # . . push args
+1057     68/push  8/imm32/segment-length
+1058     52/push-EDX
+1059     51/push-ECX
+1060     # . . call
+1061     e8/call  get-or-insert-segment/disp32
+1062     # . . discard args
+1063     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1064     # restore old segment1
+1065     5a/pop-to-EDX
+1066     # check-ints-equal(segment2/EAX, segment1/EDX, msg)
+1067     # . . push args
+1068     68/push  "F - test-get-or-insert-segment/5"/imm32
+1069     52/push-EDX
+1070     50/push-EAX
+1071     # . . call
+1072     e8/call  check-ints-equal/disp32
+1073     # . . discard args
+1074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1075     # no change to table size
+1076     # . check-ints-equal(table->write, rowsize = 8, msg)
+1077     # . . push args
+1078     68/push  "F - test-get-or-insert-segment/6"/imm32
+1079     68/push  8/imm32/row-size
+1080     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+1081     # . . call
+1082     e8/call  check-ints-equal/disp32
+1083     # . . discard args
+1084     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1085 $test-get-or-insert-segment:third-call:
+1086     # - insert a new segment name, verify that it was inserted
+1087     # EDX : (address slice) = "data"
+1088     c7          0/subop/copy        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               _test-data-segment/imm32  # copy to *EDX
+1089     c7          0/subop/copy        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         _test-data-segment-end/imm32  # copy to *(EDX+4)
+1090     # segment2/EAX = get-or-insert-segment(table, "data" slice, 8)
+1091     # . . push args
+1092     68/push  8/imm32/segment-length
+1093     52/push-EDX
+1094     51/push-ECX
+1095     # . . call
+1096     e8/call  get-or-insert-segment/disp32
+1097     # . . discard args
+1098     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1099     # table gets a new row
+1100     # . check-ints-equal(table->write, 2 rows = 16, msg)
+1101     # . . push args
+1102     68/push  "F - test-get-or-insert-segment/7"/imm32
+1103     68/push  0x10/imm32/two-rows
+1104     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+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 $test-get-or-insert-segment:end:
+1110     # . epilog
+1111     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1112     5d/pop-to-EBP
+1113     c3/return
+1114 
+1115 # write an entire stream's contents to a buffered-file
+1116 # ways to do this:
+1117 #   - construct a 'maximal slice' and pass it to write-slice
+1118 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
+1119 # we'll go with the first way for now
+1120 write-stream-data:  # f : (address buffered-file), s : (address stream) -> <void>
+1121     # . prolog
+1122     55/push-EBP
+1123     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1124     # . save registers
+1125     50/push-EAX
+1126     51/push-ECX
+1127     56/push-ESI
+1128     # ESI = s
+1129     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+1130     # var slice/ECX = {s->data, s->data + s->write}
+1131     # . push s->data + s->write
+1132     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+1133     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
+1134     50/push-EAX
+1135     # . push s->data
+1136     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
+1137     50/push-EAX
+1138     # . ECX = ESP
+1139     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1140     # write-slice(f, slice)
+1141     # . . push args
+1142     51/push-ECX
+1143     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1144     # . . call
+1145     e8/call  write-slice/disp32
+1146     # . . discard args
+1147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1148 $write-stream-data:end:
+1149     # . restore locals
+1150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1151     # . restore registers
+1152     5e/pop-to-ESI
+1153     59/pop-to-ECX
+1154     58/pop-to-EAX
+1155     # . epilog
+1156     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1157     5d/pop-to-EBP
+1158     c3/return
+1159 
+1160 test-write-stream-data:
+1161     # . prolog
+1162     55/push-EBP
+1163     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1164     # setup
+1165     # . clear-stream(_test-stream)
+1166     # . . push args
+1167     68/push  _test-stream/imm32
+1168     # . . call
+1169     e8/call  clear-stream/disp32
+1170     # . . discard args
+1171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1172     # . clear-stream(_test-buffered-file+4)
+1173     # . . push args
+1174     b8/copy-to-EAX  _test-buffered-file/imm32
+1175     05/add-to-EAX  4/imm32
+1176     50/push-EAX
+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-tmp-stream)
+1182     # . . push args
+1183     68/push  _test-tmp-stream/imm32
+1184     # . . call
+1185     e8/call  clear-stream/disp32
+1186     # . . discard args
+1187     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1188     # initialize input
+1189     # . write(_test-tmp-stream, "abcd")
+1190     # . . push args
+1191     68/push  "abcd"/imm32
+1192     68/push  _test-tmp-stream/imm32
+1193     # . . call
+1194     e8/call  write/disp32
+1195     # . . discard args
+1196     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1197     # write-stream-data(_test-buffered-file, _test-tmp-stream)
+1198     # . . push args
+1199     68/push  _test-tmp-stream/imm32
+1200     68/push  _test-buffered-file/imm32
+1201     # . . call
+1202     e8/call  write-stream-data/disp32
+1203     # . . discard args
+1204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1205     # check that the write happened as expected
+1206     # . flush(_test-buffered-file)
+1207     # . . push args
+1208     68/push  _test-buffered-file/imm32
+1209     # . . call
+1210     e8/call  flush/disp32
+1211     # . . discard args
+1212     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1213     # . check-stream-equal(_test-stream, "abcd", msg)
+1214     # . . push args
+1215     68/push  "F - test-write-stream-data"/imm32
+1216     68/push  "abcd"/imm32
+1217     68/push  _test-stream/imm32
+1218     # . . call
+1219     e8/call  check-stream-equal/disp32
+1220     # . . discard args
+1221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1222     # . epilog
+1223     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1224     5d/pop-to-EBP
+1225     c3/return
+1226 
+1227 == data
+1228 
+1229 _test-input-stream:
+1230     # current write index
+1231     0/imm32
+1232     # current read index
+1233     0/imm32
+1234     # length
+1235     0x80/imm32
+1236     # data
+1237     00 00 00 00 00 00 00 00  # 8 bytes
+1238     00 00 00 00 00 00 00 00  # 8 bytes
+1239     00 00 00 00 00 00 00 00  # 8 bytes
+1240     00 00 00 00 00 00 00 00  # 8 bytes
+1241     00 00 00 00 00 00 00 00  # 8 bytes
+1242     00 00 00 00 00 00 00 00  # 8 bytes
+1243     00 00 00 00 00 00 00 00  # 8 bytes
+1244     00 00 00 00 00 00 00 00  # 8 bytes
+1245     00 00 00 00 00 00 00 00  # 8 bytes
+1246     00 00 00 00 00 00 00 00  # 8 bytes
+1247     00 00 00 00 00 00 00 00  # 8 bytes
+1248     00 00 00 00 00 00 00 00  # 8 bytes
+1249     00 00 00 00 00 00 00 00  # 8 bytes
+1250     00 00 00 00 00 00 00 00  # 8 bytes
+1251     00 00 00 00 00 00 00 00  # 8 bytes
+1252     00 00 00 00 00 00 00 00  # 8 bytes
+1253 
+1254 # a test buffered file for _test-input-stream
+1255 _test-input-buffered-file:
+1256     # file descriptor or (address stream)
+1257     _test-input-stream/imm32
+1258     # current write index
+1259     0/imm32
+1260     # current read index
+1261     0/imm32
+1262     # length
+1263     6/imm32
+1264     # data
+1265     00 00 00 00 00 00  # 6 bytes
+1266 
+1267 _test-output-stream:
+1268     # current write index
+1269     0/imm32
+1270     # current read index
+1271     0/imm32
+1272     # length
+1273     0x80/imm32
+1274     # data
+1275     00 00 00 00 00 00 00 00  # 8 bytes
+1276     00 00 00 00 00 00 00 00  # 8 bytes
+1277     00 00 00 00 00 00 00 00  # 8 bytes
+1278     00 00 00 00 00 00 00 00  # 8 bytes
+1279     00 00 00 00 00 00 00 00  # 8 bytes
+1280     00 00 00 00 00 00 00 00  # 8 bytes
+1281     00 00 00 00 00 00 00 00  # 8 bytes
+1282     00 00 00 00 00 00 00 00  # 8 bytes
+1283     00 00 00 00 00 00 00 00  # 8 bytes
+1284     00 00 00 00 00 00 00 00  # 8 bytes
+1285     00 00 00 00 00 00 00 00  # 8 bytes
+1286     00 00 00 00 00 00 00 00  # 8 bytes
+1287     00 00 00 00 00 00 00 00  # 8 bytes
+1288     00 00 00 00 00 00 00 00  # 8 bytes
+1289     00 00 00 00 00 00 00 00  # 8 bytes
+1290     00 00 00 00 00 00 00 00  # 8 bytes
+1291 
+1292 # a test buffered file for _test-output-stream
+1293 _test-output-buffered-file:
+1294     # file descriptor or (address stream)
+1295     _test-output-stream/imm32
+1296     # current write index
+1297     0/imm32
+1298     # current read index
+1299     0/imm32
+1300     # length
+1301     6/imm32
+1302     # data
+1303     00 00 00 00 00 00  # 6 bytes
+1304 
+1305 _test-code-segment:
+1306   63/c 6f/o 64/d 65/e
+1307 _test-code-segment-end:
+1308 
+1309 _test-data-segment:
+1310   64/d 61/a 74/t 61/a
+1311 _test-data-segment-end:
+1312 
+1313 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/apps/crenshaw2-1.subx.html b/html/subx/apps/crenshaw2-1.subx.html index b950dcfb..c40057e3 100644 --- a/html/subx/apps/crenshaw2-1.subx.html +++ b/html/subx/apps/crenshaw2-1.subx.html @@ -96,7 +96,7 @@ if ('onhashchange' in window) { 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 + 36 #? e8/call test-get-num-aborts-on-non-digit-in-Look/disp32 37 #? eb/jump $main:end/disp8 38 39 # . prolog @@ -114,7 +114,7 @@ if ('onhashchange' in window) { 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 + 54 3d/compare-EAX-and 1/imm32 55 75/jump-if-not-equal $run-main/disp8 56 # . run-tests() 57 e8/call run-tests/disp32 @@ -293,7 +293,7 @@ if ('onhashchange' in window) { 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 +233 3d/compare-EAX-and 0/imm32 234 75/jump-if-not-equal $get-num:main/disp8 235 # . expected(ed, err, "integer") 236 # . . push args @@ -657,11 +657,18 @@ if ('onhashchange' in window) { 594 # current read index 595 0/imm32 596 # length -597 8/imm32 +597 0x40/imm32 598 # data 599 00 00 00 00 00 00 00 00 # 8 bytes -600 -601 # . . vim:nowrap:textwidth=0 +600 00 00 00 00 00 00 00 00 # 8 bytes +601 00 00 00 00 00 00 00 00 # 8 bytes +602 00 00 00 00 00 00 00 00 # 8 bytes +603 00 00 00 00 00 00 00 00 # 8 bytes +604 00 00 00 00 00 00 00 00 # 8 bytes +605 00 00 00 00 00 00 00 00 # 8 bytes +606 00 00 00 00 00 00 00 00 # 8 bytes +607 +608 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/crenshaw2-1b.subx.html b/html/subx/apps/crenshaw2-1b.subx.html index 932e7ed5..5aa5f873 100644 --- a/html/subx/apps/crenshaw2-1b.subx.html +++ b/html/subx/apps/crenshaw2-1b.subx.html @@ -114,7 +114,7 @@ if ('onhashchange' in window) { 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 + 54 3d/compare-EAX-and 1/imm32 55 75/jump-if-not-equal $run-main/disp8 56 # . run-tests() 57 e8/call run-tests/disp32 @@ -157,7 +157,7 @@ if ('onhashchange' in window) { 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 + 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 @@ -292,13 +292,13 @@ if ('onhashchange' in window) { 229 # - if (is-digit?(Look)) expected(ed, err, "integer") 230 # . EAX = is-digit?(Look) 231 # . . push args -232 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +232 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look 233 # . . call -234 e8/call is-digit?/disp32 +234 e8/call is-digit?/disp32 235 # . . discard args 236 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 237 # . if (EAX == 0) -238 3d/compare-EAX 0/imm32 +238 3d/compare-EAX-and 0/imm32 239 75/jump-if-not-equal $get-num:main/disp8 240 # . expected(ed, err, "integer") 241 # . . push args @@ -343,7 +343,7 @@ if ('onhashchange' in window) { 280 $get-num:loop-stage2: 281 # out->data[out->write] = LSB(Look) 282 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 -283 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX +283 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX 284 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX 285 # ++out->write 286 41/increment-ECX @@ -351,19 +351,19 @@ if ('onhashchange' in window) { 288 # . . push args 289 56/push-ESI 290 # . . call -291 e8/call get-char/disp32 +291 e8/call get-char/disp32 292 # . . discard args 293 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 294 # if (is-digit?(Look)) loop 295 # . EAX = is-digit?(Look) 296 # . . push args -297 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look +297 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look 298 # . . call -299 e8/call is-digit?/disp32 +299 e8/call is-digit?/disp32 300 # . . discard args 301 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 302 # . if (EAX != 0) loop -303 3d/compare-EAX 0/imm32 +303 3d/compare-EAX-and 0/imm32 304 0f 85/jump-if-not-equal $get-num:loop/disp32 305 $get-num:loop-end: 306 # persist necessary variables from registers @@ -405,14 +405,14 @@ if ('onhashchange' in window) { 342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 343 # . clear-stream(_test-output-stream) 344 # . . push args -345 68/push _test-output-stream/imm32 +345 68/push _test-output-stream/imm32 346 # . . call 347 e8/call clear-stream/disp32 348 # . . discard args 349 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 350 # . clear-stream(_test-error-stream) 351 # . . push args -352 68/push _test-error-stream/imm32 +352 68/push _test-error-stream/imm32 353 # . . call 354 e8/call clear-stream/disp32 355 # . . discard args @@ -443,14 +443,14 @@ if ('onhashchange' in window) { 380 # . . push args 381 68/push _test-buffered-file/imm32 382 # . . call -383 e8/call get-char/disp32 +383 e8/call get-char/disp32 384 # . . discard args 385 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 386 # get-num(in, out, err, ed) 387 # . . push args 388 50/push-EAX/ed -389 68/push _test-error-stream/imm32 -390 68/push _test-output-stream/imm32 +389 68/push _test-error-stream/imm32 +390 68/push _test-output-stream/imm32 391 68/push _test-buffered-file/imm32 392 # . . call 393 e8/call get-num/disp32 @@ -461,7 +461,7 @@ if ('onhashchange' in window) { 398 # . . push args 399 68/push "F - test-get-num-reads-single-digit"/imm32 400 68/push 0x33/imm32 -401 b8/copy-to-EAX _test-output-stream/imm32 +401 b8/copy-to-EAX _test-output-stream/imm32 402 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) 403 # . . call 404 e8/call check-ints-equal/disp32 @@ -496,14 +496,14 @@ if ('onhashchange' in window) { 433 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 434 # . clear-stream(_test-output-stream) 435 # . . push args -436 68/push _test-output-stream/imm32 +436 68/push _test-output-stream/imm32 437 # . . call 438 e8/call clear-stream/disp32 439 # . . discard args 440 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 441 # . clear-stream(_test-error-stream) 442 # . . push args -443 68/push _test-error-stream/imm32 +443 68/push _test-error-stream/imm32 444 # . . call 445 e8/call clear-stream/disp32 446 # . . discard args @@ -533,8 +533,8 @@ if ('onhashchange' in window) { 470 # get-num(in, out, err, ed) 471 # . . push args 472 50/push-EAX/ed -473 68/push _test-error-stream/imm32 -474 68/push _test-output-stream/imm32 +473 68/push _test-error-stream/imm32 +474 68/push _test-output-stream/imm32 475 68/push _test-buffered-file/imm32 476 # . . call 477 e8/call get-num/disp32 @@ -581,14 +581,14 @@ if ('onhashchange' in window) { 518 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 519 # . clear-stream(_test-output-stream) 520 # . . push args -521 68/push _test-output-stream/imm32 +521 68/push _test-output-stream/imm32 522 # . . call 523 e8/call clear-stream/disp32 524 # . . discard args 525 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 526 # . clear-stream(_test-error-stream) 527 # . . push args -528 68/push _test-error-stream/imm32 +528 68/push _test-error-stream/imm32 529 # . . call 530 e8/call clear-stream/disp32 531 # . . discard args @@ -619,14 +619,14 @@ if ('onhashchange' in window) { 556 # . . push args 557 68/push _test-buffered-file/imm32 558 # . . call -559 e8/call get-char/disp32 +559 e8/call get-char/disp32 560 # . . discard args 561 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 562 # get-num(in, out, err, ed) 563 # . . push args 564 50/push-EAX/ed -565 68/push _test-error-stream/imm32 -566 68/push _test-output-stream/imm32 +565 68/push _test-error-stream/imm32 +566 68/push _test-output-stream/imm32 567 68/push _test-buffered-file/imm32 568 # . . call 569 e8/call get-num/disp32 @@ -637,7 +637,7 @@ if ('onhashchange' in window) { 574 # . . push args 575 68/push "F - test-get-num-reads-multiple-digits"/imm32 576 68/push 0x36353433/imm32 -577 b8/copy-to-EAX _test-output-stream/imm32 +577 b8/copy-to-EAX _test-output-stream/imm32 578 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) 579 # . . call 580 e8/call check-ints-equal/disp32 @@ -672,14 +672,14 @@ if ('onhashchange' in window) { 609 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 610 # . clear-stream(_test-output-stream) 611 # . . push args -612 68/push _test-output-stream/imm32 +612 68/push _test-output-stream/imm32 613 # . . call 614 e8/call clear-stream/disp32 615 # . . discard args 616 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 617 # . clear-stream(_test-error-stream) 618 # . . push args -619 68/push _test-error-stream/imm32 +619 68/push _test-error-stream/imm32 620 # . . call 621 e8/call clear-stream/disp32 622 # . . discard args @@ -710,14 +710,14 @@ if ('onhashchange' in window) { 647 # . . push args 648 68/push _test-buffered-file/imm32 649 # . . call -650 e8/call get-char/disp32 +650 e8/call get-char/disp32 651 # . . discard args 652 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 653 # get-num(in, out, err, ed) 654 # . . push args 655 50/push-EAX/ed -656 68/push _test-error-stream/imm32 -657 68/push _test-output-stream/imm32 +656 68/push _test-error-stream/imm32 +657 68/push _test-output-stream/imm32 658 68/push _test-buffered-file/imm32 659 # . . call 660 e8/call get-num/disp32 @@ -728,7 +728,7 @@ if ('onhashchange' in window) { 665 # . . push args 666 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 667 68/push 0x36353433/imm32 -668 b8/copy-to-EAX _test-output-stream/imm32 +668 b8/copy-to-EAX _test-output-stream/imm32 669 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) 670 # . . call 671 e8/call check-ints-equal/disp32 @@ -785,80 +785,89 @@ if ('onhashchange' in window) { 722 # . . call 723 e8/call stop/disp32 724 # should never get past this point -725 # . epilog -726 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -727 5d/pop-to-EBP -728 c3/return -729 -730 # read a byte from 'f', and save it in 'Look' -731 get-char: # f : (address buffered-file) -> <void> -732 # . prolog -733 55/push-EBP -734 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -735 # . save registers -736 50/push-EAX -737 # read-byte(f) -738 # . . push args -739 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) -740 # . . call -741 e8/call read-byte/disp32 -742 # . . discard args -743 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -744 # save EAX to Look -745 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look -746 # . restore registers -747 58/pop-to-EAX -748 # . epilog -749 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -750 5d/pop-to-EBP -751 c3/return -752 -753 is-digit?: # c : int -> EAX : boolean -754 # . prolog -755 55/push-EBP -756 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -757 # EAX = false -758 b8/copy-to-EAX 0/imm32 -759 # if (c < '0') return false -760 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) -761 7c/jump-if-lesser $is-digit?:end/disp8 -762 # if (c > '9') return false -763 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) -764 7f/jump-if-greater $is-digit?:end/disp8 -765 # otherwise return true -766 b8/copy-to-EAX 1/imm32 -767 $is-digit?:end: -768 # . epilog -769 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -770 5d/pop-to-EBP -771 c3/return -772 -773 == data +725 $expected:dead-end: +726 # . epilog +727 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +728 5d/pop-to-EBP +729 c3/return +730 +731 # read a byte from 'f', and save it in 'Look' +732 get-char: # f : (address buffered-file) -> <void> +733 # . prolog +734 55/push-EBP +735 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +736 # . save registers +737 50/push-EAX +738 # read-byte(f) +739 # . . push args +740 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +741 # . . call +742 e8/call read-byte/disp32 +743 # . . discard args +744 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +745 # save EAX to Look +746 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look +747 $get-char:end: +748 # . restore registers +749 58/pop-to-EAX +750 # . epilog +751 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +752 5d/pop-to-EBP +753 c3/return +754 +755 is-digit?: # c : int -> EAX : boolean +756 # . prolog +757 55/push-EBP +758 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +759 # EAX = false +760 b8/copy-to-EAX 0/imm32 +761 # if (c < '0') return false +762 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x30/imm32 # compare *(EBP+8) +763 7c/jump-if-lesser $is-digit?:end/disp8 +764 # if (c > '9') return false +765 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x39/imm32 # compare *(EBP+8) +766 7f/jump-if-greater $is-digit?:end/disp8 +767 # otherwise return true +768 b8/copy-to-EAX 1/imm32 +769 $is-digit?:end: +770 # . epilog +771 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +772 5d/pop-to-EBP +773 c3/return 774 -775 Look: # (char with some extra padding) -776 0/imm32 -777 -778 _test-output-stream: -779 # current write index -780 0/imm32 -781 # current read index +775 == data +776 +777 Look: # (char with some extra padding) +778 0/imm32 +779 +780 _test-output-stream: +781 # current write index 782 0/imm32 -783 # length -784 8/imm32 -785 # data -786 00 00 00 00 00 00 00 00 # 8 bytes -787 -788 _test-error-stream: -789 # current write index -790 0/imm32 -791 # current read index +783 # current read index +784 0/imm32 +785 # length +786 8/imm32 +787 # data +788 00 00 00 00 00 00 00 00 # 8 bytes +789 +790 _test-error-stream: +791 # current write index 792 0/imm32 -793 # length -794 8/imm32 -795 # data -796 00 00 00 00 00 00 00 00 # 8 bytes -797 -798 # . . vim:nowrap:textwidth=0 +793 # current read index +794 0/imm32 +795 # length +796 0x40/imm32 +797 # data +798 00 00 00 00 00 00 00 00 # 8 bytes +799 00 00 00 00 00 00 00 00 # 8 bytes +800 00 00 00 00 00 00 00 00 # 8 bytes +801 00 00 00 00 00 00 00 00 # 8 bytes +802 00 00 00 00 00 00 00 00 # 8 bytes +803 00 00 00 00 00 00 00 00 # 8 bytes +804 00 00 00 00 00 00 00 00 # 8 bytes +805 00 00 00 00 00 00 00 00 # 8 bytes +806 +807 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/factorial.subx.html b/html/subx/apps/factorial.subx.html index c265aabf..86eb6b65 100644 --- a/html/subx/apps/factorial.subx.html +++ b/html/subx/apps/factorial.subx.html @@ -101,7 +101,7 @@ if ('onhashchange' in window) { 39 # . . discard args 40 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 41 # . check result - 42 3d/compare-EAX 1/imm32 + 42 3d/compare-EAX-and 1/imm32 43 75/jump-if-not-equal $run-main/disp8 44 # . run-tests() 45 e8/call run-tests/disp32 diff --git a/html/subx/apps/handle.subx.html b/html/subx/apps/handle.subx.html index 6595c56c..2dac1676 100644 --- a/html/subx/apps/handle.subx.html +++ b/html/subx/apps/handle.subx.html @@ -112,7 +112,7 @@ if ('onhashchange' in window) { 51 # out->address = EAX 52 89/copy 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDX+4) 53 # if (EAX == 0) out->alloc_id = 0, return - 54 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0/imm32 # compare EAX + 54 3d/compare-EAX-and 0/imm32 55 75/jump-if-not-equal $new:continue/disp8 56 c7 0/subop/copy 0/mod/indirect 2/rm32/EDX . . . . . 0/imm32 # copy to *EDX 57 eb/jump $new:end/disp8 diff --git a/html/subx/apps/hex.subx.html b/html/subx/apps/hex.subx.html index 125478ec..6a61283c 100644 --- a/html/subx/apps/hex.subx.html +++ b/html/subx/apps/hex.subx.html @@ -83,7 +83,7 @@ if ('onhashchange' in window) { 20 Entry: # run tests if necessary, convert stdin if not 21 22 #? # for debugging: run a single test - 23 #? e8/call test-skip-until-newline/disp32 + 23 #? e8/call test-convert-next-octet-aborts-on-single-hex-byte/disp32 24 #? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX 25 #? eb/jump $main:end/disp8 26 @@ -102,7 +102,7 @@ if ('onhashchange' in window) { 39 # . . discard args 40 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 41 # . check result - 42 3d/compare-EAX 1/imm32 + 42 3d/compare-EAX-and 1/imm32 43 75/jump-if-not-equal $run-main/disp8 44 # . run-tests() 45 e8/call run-tests/disp32 @@ -157,7 +157,7 @@ if ('onhashchange' in window) { 94 # . . discard first 2 args 95 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 96 # if (EAX == Eof) break - 97 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xffffffff/imm32/Eof # compare EAX + 97 3d/compare-EAX-and 0xffffffff/imm32/Eof 98 74/jump-if-equal $convert:loop-end/disp8 99 # write-byte(out, AL) 100 # . . push args @@ -217,7 +217,7 @@ if ('onhashchange' in window) { 154 # . . discard args 155 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 156 # if (EAX == Eof) return - 157 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xffffffff/imm32/Eof # compare EAX + 157 3d/compare-EAX-and 0xffffffff/imm32/Eof 158 74/jump-if-equal $convert-next-octet:end/disp8 159 # EAX = from-hex-char(EAX) 160 e8/call from-hex-char/disp32 @@ -233,7 +233,7 @@ if ('onhashchange' in window) { 170 # . . discard args 171 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP 172 # if (EAX == Eof) error(ed, err, "partial byte found.") - 173 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xffffffff/imm32/Eof # compare EAX + 173 3d/compare-EAX-and 0xffffffff/imm32/Eof 174 75/jump-if-not-equal $convert-next-octet:convert/disp8 175 # . error-byte(ed, err, msg, '.') # reusing error-byte to avoid creating _yet_ another helper 176 # . . push args @@ -290,7 +290,7 @@ if ('onhashchange' in window) { 227 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 228 # . clear-stream(_test-error-buffered-file+4) 229 # . . push args - 230 b8/copy-to-EAX _test-error-buffered-file/imm32 + 230 b8/copy-to-EAX _test-error-buffered-file/imm32 231 05/add-to-EAX 4/imm32 232 50/push-EAX 233 # . . call @@ -321,7 +321,7 @@ if ('onhashchange' in window) { 258 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) 259 # . . push args 260 51/push-ECX/ed - 261 68/push _test-error-buffered-file/imm32 + 261 68/push _test-error-buffered-file/imm32 262 68/push _test-buffered-file/imm32 263 # . . call 264 e8/call convert-next-octet/disp32 @@ -392,7 +392,7 @@ if ('onhashchange' in window) { 329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 330 # . clear-stream(_test-error-buffered-file+4) 331 # . . push args - 332 b8/copy-to-EAX _test-error-buffered-file/imm32 + 332 b8/copy-to-EAX _test-error-buffered-file/imm32 333 05/add-to-EAX 4/imm32 334 50/push-EAX 335 # . . call @@ -415,7 +415,7 @@ if ('onhashchange' in window) { 352 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) 353 # . . push args 354 51/push-ECX/ed - 355 68/push _test-error-buffered-file/imm32 + 355 68/push _test-error-buffered-file/imm32 356 68/push _test-buffered-file/imm32 357 # . . call 358 e8/call convert-next-octet/disp32 @@ -486,7 +486,7 @@ if ('onhashchange' in window) { 423 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 424 # . clear-stream(_test-error-buffered-file+4) 425 # . . push args - 426 b8/copy-to-EAX _test-error-buffered-file/imm32 + 426 b8/copy-to-EAX _test-error-buffered-file/imm32 427 05/add-to-EAX 4/imm32 428 50/push-EAX 429 # . . call @@ -517,7 +517,7 @@ if ('onhashchange' in window) { 454 # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) 455 # . . push args 456 51/push-ECX/ed - 457 68/push _test-error-buffered-file/imm32 + 457 68/push _test-error-buffered-file/imm32 458 68/push _test-buffered-file/imm32 459 # . . call 460 e8/call convert-next-octet/disp32 @@ -592,13 +592,13 @@ if ('onhashchange' in window) { 529 75/jump-if-not-equal $scan-next-byte:end/disp8 530 $scan-next-byte:check1: 531 # if (EAX == ' ') continue - 532 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0x20/imm32 # compare EAX + 532 3d/compare-EAX-and 0x20/imm32/space 533 74/jump-if-equal $scan-next-byte:loop/disp8 534 # if (EAX == '\t') continue - 535 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0x9/imm32 # compare EAX + 535 3d/compare-EAX-and 9/imm32/tab 536 74/jump-if-equal $scan-next-byte:loop/disp8 537 # if (EAX == '\n') continue - 538 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xa/imm32 # compare EAX + 538 3d/compare-EAX-and 0xa/imm32/newline 539 74/jump-if-equal $scan-next-byte:loop/disp8 540 $scan-next-byte:check2: 541 # if (EAX == '#') skip-until-newline(in) @@ -659,7 +659,7 @@ if ('onhashchange' in window) { 596 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 597 # . clear-stream(_test-error-buffered-file+4) 598 # . . push args - 599 b8/copy-to-EAX _test-error-buffered-file/imm32 + 599 b8/copy-to-EAX _test-error-buffered-file/imm32 600 05/add-to-EAX 4/imm32 601 50/push-EAX 602 # . . call @@ -690,7 +690,7 @@ if ('onhashchange' in window) { 627 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) 628 # . . push args 629 51/push-ECX/ed - 630 68/push _test-error-buffered-file/imm32 + 630 68/push _test-error-buffered-file/imm32 631 68/push _test-buffered-file/imm32 632 # . . call 633 e8/call scan-next-byte/disp32 @@ -761,7 +761,7 @@ if ('onhashchange' in window) { 698 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 699 # . clear-stream(_test-error-buffered-file+4) 700 # . . push args - 701 b8/copy-to-EAX _test-error-buffered-file/imm32 + 701 b8/copy-to-EAX _test-error-buffered-file/imm32 702 05/add-to-EAX 4/imm32 703 50/push-EAX 704 # . . call @@ -792,7 +792,7 @@ if ('onhashchange' in window) { 729 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) 730 # . . push args 731 51/push-ECX/ed - 732 68/push _test-error-buffered-file/imm32 + 732 68/push _test-error-buffered-file/imm32 733 68/push _test-buffered-file/imm32 734 # . . call 735 e8/call scan-next-byte/disp32 @@ -863,7 +863,7 @@ if ('onhashchange' in window) { 800 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 801 # . clear-stream(_test-error-buffered-file+4) 802 # . . push args - 803 b8/copy-to-EAX _test-error-buffered-file/imm32 + 803 b8/copy-to-EAX _test-error-buffered-file/imm32 804 05/add-to-EAX 4/imm32 805 50/push-EAX 806 # . . call @@ -910,7 +910,7 @@ if ('onhashchange' in window) { 847 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) 848 # . . push args 849 51/push-ECX/ed - 850 68/push _test-error-buffered-file/imm32 + 850 68/push _test-error-buffered-file/imm32 851 68/push _test-buffered-file/imm32 852 # . . call 853 e8/call scan-next-byte/disp32 @@ -981,7 +981,7 @@ if ('onhashchange' in window) { 918 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 919 # . clear-stream(_test-error-buffered-file+4) 920 # . . push args - 921 b8/copy-to-EAX _test-error-buffered-file/imm32 + 921 b8/copy-to-EAX _test-error-buffered-file/imm32 922 05/add-to-EAX 4/imm32 923 50/push-EAX 924 # . . call @@ -1028,7 +1028,7 @@ if ('onhashchange' in window) { 965 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) 966 # . . push args 967 51/push-ECX/ed - 968 68/push _test-error-buffered-file/imm32 + 968 68/push _test-error-buffered-file/imm32 969 68/push _test-buffered-file/imm32 970 # . . call 971 e8/call scan-next-byte/disp32 @@ -1099,7 +1099,7 @@ if ('onhashchange' in window) { 1036 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1037 # . clear-stream(_test-error-buffered-file+4) 1038 # . . push args -1039 b8/copy-to-EAX _test-error-buffered-file/imm32 +1039 b8/copy-to-EAX _test-error-buffered-file/imm32 1040 05/add-to-EAX 4/imm32 1041 50/push-EAX 1042 # . . call @@ -1146,7 +1146,7 @@ if ('onhashchange' in window) { 1083 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) 1084 # . . push args 1085 51/push-ECX/ed -1086 68/push _test-error-buffered-file/imm32 +1086 68/push _test-error-buffered-file/imm32 1087 68/push _test-buffered-file/imm32 1088 # . . call 1089 e8/call scan-next-byte/disp32 @@ -1217,7 +1217,7 @@ if ('onhashchange' in window) { 1154 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1155 # . clear-stream(_test-error-buffered-file+4) 1156 # . . push args -1157 b8/copy-to-EAX _test-error-buffered-file/imm32 +1157 b8/copy-to-EAX _test-error-buffered-file/imm32 1158 05/add-to-EAX 4/imm32 1159 50/push-EAX 1160 # . . call @@ -1248,7 +1248,7 @@ if ('onhashchange' in window) { 1185 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) 1186 # . . push args 1187 51/push-ECX/ed -1188 68/push _test-error-buffered-file/imm32 +1188 68/push _test-error-buffered-file/imm32 1189 68/push _test-buffered-file/imm32 1190 # . . call 1191 e8/call scan-next-byte/disp32 @@ -1319,7 +1319,7 @@ if ('onhashchange' in window) { 1256 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1257 # . clear-stream(_test-error-buffered-file+4) 1258 # . . push args -1259 b8/copy-to-EAX _test-error-buffered-file/imm32 +1259 b8/copy-to-EAX _test-error-buffered-file/imm32 1260 05/add-to-EAX 4/imm32 1261 50/push-EAX 1262 # . . call @@ -1342,7 +1342,7 @@ if ('onhashchange' in window) { 1279 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) 1280 # . . push args 1281 51/push-ECX/ed -1282 68/push _test-error-buffered-file/imm32 +1282 68/push _test-error-buffered-file/imm32 1283 68/push _test-buffered-file/imm32 1284 # . . call 1285 e8/call scan-next-byte/disp32 @@ -1413,7 +1413,7 @@ if ('onhashchange' in window) { 1350 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1351 # . clear-stream(_test-error-buffered-file+4) 1352 # . . push args -1353 b8/copy-to-EAX _test-error-buffered-file/imm32 +1353 b8/copy-to-EAX _test-error-buffered-file/imm32 1354 05/add-to-EAX 4/imm32 1355 50/push-EAX 1356 # . . call @@ -1444,7 +1444,7 @@ if ('onhashchange' in window) { 1381 # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) 1382 # . . push args 1383 51/push-ECX/ed -1384 68/push _test-error-buffered-file/imm32 +1384 68/push _test-error-buffered-file/imm32 1385 68/push _test-buffered-file/imm32 1386 # . . call 1387 e8/call scan-next-byte/disp32 @@ -1494,10 +1494,10 @@ if ('onhashchange' in window) { 1431 # . . discard args 1432 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1433 # . if (EAX == Eof) break -1434 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xffffffff/imm32/Eof # compare EAX +1434 3d/compare-EAX-and 0xffffffff/imm32/Eof 1435 74/jump-if-equal $skip-until-newline:end/disp8 1436 # . if (EAX != 0xa/newline) loop -1437 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xa/imm32 # compare EAX +1437 3d/compare-EAX-and 0xa/imm32/newline 1438 75/jump-if-not-equal $skip-until-newline:loop/disp8 1439 $skip-until-newline:end: 1440 # . restore registers @@ -1579,24 +1579,39 @@ if ('onhashchange' in window) { 1516 # current read index 1517 0/imm32 1518 # length -1519 8/imm32 +1519 0x80/imm32 1520 # data 1521 00 00 00 00 00 00 00 00 # 8 bytes -1522 -1523 # a test buffered file for _test-error-stream -1524 _test-error-buffered-file: -1525 # file descriptor or (address stream) -1526 _test-error-stream/imm32 -1527 # current write index -1528 0/imm32 -1529 # current read index -1530 0/imm32 -1531 # length -1532 6/imm32 -1533 # data -1534 00 00 00 00 00 00 # 6 bytes -1535 -1536 # . . vim:nowrap:textwidth=0 +1522 00 00 00 00 00 00 00 00 # 8 bytes +1523 00 00 00 00 00 00 00 00 # 8 bytes +1524 00 00 00 00 00 00 00 00 # 8 bytes +1525 00 00 00 00 00 00 00 00 # 8 bytes +1526 00 00 00 00 00 00 00 00 # 8 bytes +1527 00 00 00 00 00 00 00 00 # 8 bytes +1528 00 00 00 00 00 00 00 00 # 8 bytes +1529 00 00 00 00 00 00 00 00 # 8 bytes +1530 00 00 00 00 00 00 00 00 # 8 bytes +1531 00 00 00 00 00 00 00 00 # 8 bytes +1532 00 00 00 00 00 00 00 00 # 8 bytes +1533 00 00 00 00 00 00 00 00 # 8 bytes +1534 00 00 00 00 00 00 00 00 # 8 bytes +1535 00 00 00 00 00 00 00 00 # 8 bytes +1536 00 00 00 00 00 00 00 00 # 8 bytes +1537 +1538 # a test buffered file for _test-error-stream +1539 _test-error-buffered-file: +1540 # file descriptor or (address stream) +1541 _test-error-stream/imm32 +1542 # current write index +1543 0/imm32 +1544 # current read index +1545 0/imm32 +1546 # length +1547 6/imm32 +1548 # data +1549 00 00 00 00 00 00 # 6 bytes +1550 +1551 # . . vim:nowrap:textwidth=0 diff --git a/html/subx/apps/pack.subx.html b/html/subx/apps/pack.subx.html index 0802babc..90884600 100644 --- a/html/subx/apps/pack.subx.html +++ b/html/subx/apps/pack.subx.html @@ -63,8 +63,8 @@ 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.
+   2 # of whitespace-separated ascii hex bytes on stdout. Label definitions and
+   3 # uses are left untouched.
    4 #
    5 # To run (from the subx/ directory):
    6 #   $ ./subx translate *.subx apps/pack.subx -o apps/pack
@@ -76,541 +76,541 @@ if ('onhashchange' in window) {
   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
+  15 
+  16 == code
+  17 #   instruction                     effective address                                                   register    displacement    immediate
+  18 # . op          subop               mod             rm32          base        index         scale       r32
+  19 # . 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
+  20 
+  21 Entry:  # run tests if necessary, convert stdin if not
   22 
-  23 Entry:  # run tests if necessary, convert stdin if not
-  24 
-  25     # for debugging: run a single test
-  26 #?     e8/call test-convert-in-data-segment/disp32
-  27 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
-  28 #?     eb/jump  $main:end/disp8
-  29 
-  30     # . prolog
-  31     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-  32     # - if argc > 1 and argv[1] == "test", then return run_tests()
-  33     # . argc > 1
-  34     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
-  35     7e/jump-if-lesser-or-equal  $run-main/disp8
-  36     # . argv[1] == "test"
-  37     # . . push args
-  38     68/push  "test"/imm32
-  39     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-  40     # . . call
-  41     e8/call  kernel-string-equal?/disp32
-  42     # . . discard args
-  43     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-  44     # . check result
-  45     3d/compare-EAX  1/imm32
-  46     75/jump-if-not-equal  $run-main/disp8
-  47     # . run-tests()
-  48     e8/call  run-tests/disp32
-  49     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
-  50     eb/jump  $main:end/disp8
-  51 $run-main:
-  52     # - otherwise convert stdin
-  53     # var ed/EAX : exit-descriptor
-  54     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
-  55     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
-  56     # configure ed to really exit()
-  57     # . ed->target = 0
-  58     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
-  59     # return convert(Stdin, 1/stdout, 2/stderr, ed)
-  60     # . . push args
-  61     50/push-EAX/ed
-  62     68/push  Stderr/imm32
-  63     68/push  Stdout/imm32
-  64     68/push  Stdin/imm32
-  65     # . . call
-  66     e8/call  convert/disp32
-  67     # . . discard args
-  68     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-  69     # . syscall(exit, 0)
-  70     bb/copy-to-EBX  0/imm32
-  71 $main:end:
-  72     b8/copy-to-EAX  1/imm32/exit
-  73     cd/syscall  0x80/imm8
-  74 
-  75 # - big picture
-  76 # We'll operate on each line/instruction in isolation. That way we only need to
-  77 # allocate memory for converting a single instruction.
-  78 #
-  79 # To pack an entire file, convert every segment in it
-  80 # To convert a code segment, convert every instruction (line) until the next segment header
-  81 # To convert a non-data segment, convert every word until the next segment header
-  82 #
-  83 # primary state: line
-  84 #   stream of 512 bytes; abort if it ever overflows
-  85 
-  86 # conceptual hierarchy within a line:
-  87 #   line = words separated by ' ', maybe followed by comment starting with '#'
-  88 #   word = name until '/', then 0 or more metadata separated by '/'
-  89 #
-  90 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
-  91 #   next-token(stream, delim char) -> slice (start, end pointers)
-  92 #   next-token-from-slice(start, end, delim char) -> slice
-  93 #   slice-equal?(slice, string)
-  94 
-  95 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
-  96     # pseudocode:
-  97     #   var line = new-stream(512, 1)
-  98     #   var in-code? = false
-  99     #   while true
- 100     #     clear-stream(line)
- 101     #     read-line(in, line)
- 102     #     if (line->write == 0) break             # end of file
- 103     #     var word-slice = next-word(line)
- 104     #     if slice-empty?(word-slice)             # whitespace
- 105     #       write-stream-buffered(out, line)
- 106     #     else if (slice-equal?(word-slice, "=="))
- 107     #       word-slice = next-word(line)
- 108     #       in-code? = slice-equal?(word-slice, "code")
- 109     #       write-stream-buffered(out, line)
- 110     #     else if (in-code?)
- 111     #       rewind-stream(line)
- 112     #       convert-instruction(line, out)
- 113     #     else
- 114     #       rewind-stream(line)
- 115     #       convert-data(line, out)
- 116     #   flush(out)
- 117     #
- 118     # . prolog
- 119     55/push-EBP
- 120     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 121     # . save registers
- 122     50/push-EAX
- 123     51/push-ECX
- 124     52/push-EDX
- 125     53/push-EBX
- 126     # var line/ECX : (address stream byte) = stream(512)
- 127     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
- 128     68/push  0x200/imm32/length
- 129     68/push  0/imm32/read
- 130     68/push  0/imm32/write
- 131     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 132     # var word-slice/EDX = {0, 0}
- 133     68/push  0/imm32/end
- 134     68/push  0/imm32/curr
- 135     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
- 136     # var in-code?/EBX = false
- 137     68/push  0/imm32/false
- 138     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
- 139 $convert:loop:
- 140     # clear-stream(line)
- 141     # . . push args
- 142     51/push-ECX
- 143     # . . call
- 144     e8/call  clear-stream/disp32
- 145     # . . discard args
- 146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 147     # read-line(in, line)
- 148     # . . push args
- 149     51/push-ECX
- 150     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 151     # . . call
- 152     e8/call  read-line/disp32
- 153     # . . discard args
- 154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 155 $convert:check0:
- 156     # if (line->write == 0) break
- 157     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
- 158     0f 84/jump-if-equal  $convert:break/disp32
- 159 +-- 34 lines: #?     # dump current line -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 193     # next-word(line, word-slice)
- 194     # . . push args
- 195     52/push-EDX
- 196     51/push-ECX
- 197     # . . call
- 198     e8/call  next-word/disp32
- 199     # . . discard args
- 200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 201 $convert:check1:
- 202     # if (slice-empty?(word-slice)) write-stream-buffered(out, line)
- 203     # . EAX = slice-empty?(word-slice)
- 204     # . . push args
- 205     52/push-EDX
- 206     # . . call
- 207     e8/call  slice-empty?/disp32
- 208     # . . discard args
- 209     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 210     # . if (EAX != 0) write-stream-buffered(out, line)
- 211     3d/compare-EAX  0/imm32
- 212     0f 85/jump-if-not-equal  $convert:pass-through/disp32
- 213 $convert:check2:
- 214 +-- 50 lines: #?     # dump current word -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 264     # if (slice-equal?(word-slice, "=="))
- 265     #   word-slice = next-word(line)
- 266     #   in-code? = slice-equal?(word-slice, "code")
- 267     #   write-stream-buffered(out, line)
- 268     # . EAX = slice-equal?(word-slice, "==")
- 269     # . . push args
- 270     68/push  "=="/imm32
- 271     52/push-EDX
- 272     # . . call
- 273     e8/call  slice-equal?/disp32
- 274     # . . discard args
- 275     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 276     # . if (EAX == 0) goto check3
- 277     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
- 278     0f 84/jump-if-equal  $convert:check3/disp32
- 279     # . next-word(line, word-slice)
- 280     # . . push args
- 281     52/push-EDX
- 282     51/push-ECX
- 283     # . . call
- 284     e8/call  next-word/disp32
- 285     # . . discard args
- 286     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 287 +-- 50 lines: #?     # dump segment name -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 337     # . in-code? = slice-equal?(word-slice, "code")
- 338     # . . push args
- 339     68/push  "code"/imm32
- 340     52/push-EDX
- 341     # . . call
- 342     e8/call  slice-equal?/disp32
- 343     # . . discard args
- 344     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 345     # . . in-code? = EAX
- 346     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
- 347     # . goto pass-through
- 348     eb/jump  $convert:pass-through/disp8
- 349 $convert:check3:
- 350     # else rewind-stream(line)
- 351     # . rewind-stream(line)
- 352     # . . push args
- 353     51/push-ECX
- 354     # . . call
- 355     e8/call  rewind-stream/disp32
- 356     # . . discard args
- 357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 358     # if (in-code? != 0) convert-instruction(line, out)
- 359     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
- 360     74/jump-if-equal  $convert:data/disp8
- 361 $convert:code:
- 362     # . convert-instruction(line, out)
- 363     # . . push args
- 364     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 365     51/push-ECX
- 366     # . . call
- 367     e8/call  convert-instruction/disp32
- 368     # . . discard args
- 369     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 370     # . loop
- 371     e9/jump  $convert:loop/disp32
- 372 $convert:data:
- 373     # else convert-data(line, out)
- 374     # . . push args
- 375     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 376     51/push-ECX
- 377     # . . call
- 378     e8/call  convert-data/disp32
- 379     # . . discard args
- 380     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 381     # . loop
- 382     e9/jump  $convert:loop/disp32
- 383 $convert:pass-through:
- 384     # write-stream-buffered(out, line)
- 385     # . . push args
- 386     51/push-ECX
- 387     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 388     # . . call
- 389     e8/call  write-stream-buffered/disp32
- 390     # . . discard args
- 391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 392     # . loop
- 393     e9/jump  $convert:loop/disp32
- 394 $convert:break:
- 395     # flush(out)
- 396     # . . push args
- 397     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 398     # . . call
- 399     e8/call  flush/disp32
- 400     # . . discard args
- 401     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 402 $convert:end:
- 403     # . reclaim locals
- 404     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x218/imm32       # add to ESP
- 405     # . restore registers
- 406     5b/pop-to-EBX
- 407     5a/pop-to-EDX
- 408     59/pop-to-ECX
- 409     58/pop-to-EAX
- 410     # . epilog
- 411     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 412     5d/pop-to-EBP
- 413     c3/return
- 414 
- 415 test-convert-passes-empty-lines-through:
- 416     # if a line is empty, pass it along unchanged
- 417     # . prolog
- 418     55/push-EBP
- 419     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 420     # setup
- 421     # . clear-stream(_test-input-stream)
- 422     # . . push args
- 423     68/push  _test-input-stream/imm32
- 424     # . . call
- 425     e8/call  clear-stream/disp32
- 426     # . . discard args
- 427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 428     # . clear-stream(_test-input-buffered-file+4)
- 429     # . . push args
- 430     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 431     05/add-to-EAX  4/imm32
- 432     50/push-EAX
- 433     # . . call
- 434     e8/call  clear-stream/disp32
- 435     # . . discard args
- 436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 437     # . clear-stream(_test-output-stream)
- 438     # . . push args
- 439     68/push  _test-output-stream/imm32
- 440     # . . call
- 441     e8/call  clear-stream/disp32
- 442     # . . discard args
- 443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 444     # . clear-stream(_test-output-buffered-file+4)
- 445     # . . push args
- 446     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 447     05/add-to-EAX  4/imm32
- 448     50/push-EAX
- 449     # . . call
- 450     e8/call  clear-stream/disp32
- 451     # . . discard args
- 452     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 453     # write nothing to input
- 454     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 455     # . . push args
- 456     68/push  _test-output-buffered-file/imm32
- 457     68/push  _test-input-buffered-file/imm32
- 458     # . . call
- 459     e8/call  convert/disp32
- 460     # . . discard args
- 461     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 462     # check that the line just passed through
- 463     # . flush(_test-output-buffered-file)
- 464     # . . push args
- 465     68/push  _test-output-buffered-file/imm32
- 466     # . . call
- 467     e8/call  flush/disp32
- 468     # . . discard args
- 469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 470     # . check-stream-equal(_test-output-stream, "", msg)
- 471     # . . push args
- 472     68/push  "F - test-convert-passes-empty-lines-through"/imm32
- 473     68/push  ""/imm32
- 474     68/push  _test-output-stream/imm32
- 475     # . . call
- 476     e8/call  check-stream-equal/disp32
- 477     # . . discard args
- 478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 479     # . epilog
- 480     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 481     5d/pop-to-EBP
- 482     c3/return
- 483 
- 484 test-convert-passes-lines-with-just-whitespace-through:
- 485     # if a line is empty, pass it along unchanged
- 486     # . prolog
- 487     55/push-EBP
- 488     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 489     # setup
- 490     # . clear-stream(_test-input-stream)
- 491     # . . push args
- 492     68/push  _test-input-stream/imm32
- 493     # . . call
- 494     e8/call  clear-stream/disp32
- 495     # . . discard args
- 496     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 497     # . clear-stream(_test-input-buffered-file+4)
- 498     # . . push args
- 499     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 500     05/add-to-EAX  4/imm32
- 501     50/push-EAX
- 502     # . . call
- 503     e8/call  clear-stream/disp32
- 504     # . . discard args
- 505     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 506     # . clear-stream(_test-output-stream)
- 507     # . . push args
- 508     68/push  _test-output-stream/imm32
- 509     # . . call
- 510     e8/call  clear-stream/disp32
- 511     # . . discard args
- 512     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 513     # . clear-stream(_test-output-buffered-file+4)
- 514     # . . push args
- 515     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 516     05/add-to-EAX  4/imm32
- 517     50/push-EAX
- 518     # . . call
- 519     e8/call  clear-stream/disp32
- 520     # . . discard args
- 521     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 522     # initialize input
- 523     # . write(_test-input-stream, "    ")
- 524     # . . push args
- 525     68/push  "    "/imm32
- 526     68/push  _test-input-stream/imm32
- 527     # . . call
- 528     e8/call  write/disp32
- 529     # . . discard args
- 530     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 531     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 532     # . . push args
- 533     68/push  _test-output-buffered-file/imm32
- 534     68/push  _test-input-buffered-file/imm32
- 535     # . . call
- 536     e8/call  convert/disp32
- 537     # . . discard args
- 538     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 539     # check that the line just passed through
- 540     # . flush(_test-output-buffered-file)
- 541     # . . push args
- 542     68/push  _test-output-buffered-file/imm32
- 543     # . . call
- 544     e8/call  flush/disp32
- 545     # . . discard args
- 546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 547     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
- 548     # . . push args
- 549     68/push  "F - test-convert-passes-with-just-whitespace-through"/imm32
- 550     68/push  "    "/imm32
- 551     68/push  _test-output-stream/imm32
- 552     # . . call
- 553     e8/call  check-next-stream-line-equal/disp32
- 554     # . . discard args
- 555     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 556     # . epilog
- 557     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 558     5d/pop-to-EBP
- 559     c3/return
- 560 
- 561 test-convert-passes-segment-headers-through:
- 562     # if a line starts with '==', pass it along unchanged
- 563     # . prolog
- 564     55/push-EBP
- 565     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 566     # setup
- 567     # . clear-stream(_test-input-stream)
- 568     # . . push args
- 569     68/push  _test-input-stream/imm32
- 570     # . . call
- 571     e8/call  clear-stream/disp32
- 572     # . . discard args
- 573     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 574     # . clear-stream(_test-input-buffered-file+4)
- 575     # . . push args
- 576     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 577     05/add-to-EAX  4/imm32
- 578     50/push-EAX
- 579     # . . call
- 580     e8/call  clear-stream/disp32
- 581     # . . discard args
- 582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 583     # . clear-stream(_test-output-stream)
- 584     # . . push args
- 585     68/push  _test-output-stream/imm32
- 586     # . . call
- 587     e8/call  clear-stream/disp32
- 588     # . . discard args
- 589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 590     # . clear-stream(_test-output-buffered-file+4)
- 591     # . . push args
- 592     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 593     05/add-to-EAX  4/imm32
- 594     50/push-EAX
- 595     # . . call
- 596     e8/call  clear-stream/disp32
- 597     # . . discard args
- 598     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 599     # initialize input
- 600     # . write(_test-input-stream, "== abcd")
- 601     # . . push args
- 602     68/push  "== abcd"/imm32
- 603     68/push  _test-input-stream/imm32
- 604     # . . call
- 605     e8/call  write/disp32
- 606     # . . discard args
- 607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 608     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 609     # . . push args
- 610     68/push  _test-output-buffered-file/imm32
- 611     68/push  _test-input-buffered-file/imm32
- 612     # . . call
- 613     e8/call  convert/disp32
- 614     # . . discard args
- 615     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 616     # check that the line just passed through
- 617     # . flush(_test-output-buffered-file)
- 618     # . . push args
- 619     68/push  _test-output-buffered-file/imm32
- 620     # . . call
- 621     e8/call  flush/disp32
- 622     # . . discard args
- 623     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 624     # . check-stream-equal(_test-output-stream, "== abcd", msg)
- 625     # . . push args
- 626     68/push  "F - test-convert-passes-segment-headers-through"/imm32
- 627     68/push  "== abcd"/imm32
- 628     68/push  _test-output-stream/imm32
- 629     # . . call
- 630     e8/call  check-stream-equal/disp32
- 631     # . . discard args
- 632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 633     # . epilog
- 634     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 635     5d/pop-to-EBP
- 636     c3/return
- 637 
- 638 test-convert-in-data-segment:
- 639     # correctly process lines in the data segment
- 640     # . prolog
- 641     55/push-EBP
- 642     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 643     # setup
- 644     # . clear-stream(_test-input-stream)
- 645     # . . push args
- 646     68/push  _test-input-stream/imm32
- 647     # . . call
- 648     e8/call  clear-stream/disp32
- 649     # . . discard args
- 650     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 651     # . clear-stream(_test-input-buffered-file+4)
- 652     # . . push args
- 653     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 654     05/add-to-EAX  4/imm32
- 655     50/push-EAX
- 656     # . . call
- 657     e8/call  clear-stream/disp32
- 658     # . . discard args
- 659     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 660     # . clear-stream(_test-output-stream)
- 661     # . . push args
- 662     68/push  _test-output-stream/imm32
- 663     # . . call
- 664     e8/call  clear-stream/disp32
- 665     # . . discard args
- 666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 667     # . clear-stream(_test-output-buffered-file+4)
- 668     # . . push args
- 669     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 670     05/add-to-EAX  4/imm32
- 671     50/push-EAX
- 672     # . . call
- 673     e8/call  clear-stream/disp32
- 674     # . . discard args
- 675     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 676     # initialize input
+  23     # for debugging: run a single test
+  24 #?     e8/call test-emit-non-number-with-all-hex-digits-and-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     # . prolog
+  29     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  30     # - if argc > 1 and argv[1] == "test", then return run_tests()
+  31     # . argc > 1
+  32     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
+  33     7e/jump-if-lesser-or-equal  $run-main/disp8
+  34     # . argv[1] == "test"
+  35     # . . push args
+  36     68/push  "test"/imm32
+  37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+  38     # . . call
+  39     e8/call  kernel-string-equal?/disp32
+  40     # . . discard args
+  41     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  42     # . check result
+  43     3d/compare-EAX-and  1/imm32
+  44     75/jump-if-not-equal  $run-main/disp8
+  45     # . run-tests()
+  46     e8/call  run-tests/disp32
+  47     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+  48     eb/jump  $main:end/disp8
+  49 $run-main:
+  50     # - otherwise convert stdin
+  51     # var ed/EAX : exit-descriptor
+  52     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+  53     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
+  54     # configure ed to really exit()
+  55     # . ed->target = 0
+  56     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+  57     # return convert(Stdin, 1/stdout, 2/stderr, ed)
+  58     # . . push args
+  59     50/push-EAX/ed
+  60     68/push  Stderr/imm32
+  61     68/push  Stdout/imm32
+  62     68/push  Stdin/imm32
+  63     # . . call
+  64     e8/call  convert/disp32
+  65     # . . discard args
+  66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+  67     # . syscall(exit, 0)
+  68     bb/copy-to-EBX  0/imm32
+  69 $main:end:
+  70     b8/copy-to-EAX  1/imm32/exit
+  71     cd/syscall  0x80/imm8
+  72 
+  73 # - big picture
+  74 # We'll operate on each line/instruction in isolation. That way we only need to
+  75 # allocate memory for converting a single instruction.
+  76 #
+  77 # To pack an entire file, convert every segment in it
+  78 # To convert a code segment, convert every instruction (line) until the next segment header
+  79 # To convert a non-data segment, convert every word until the next segment header
+  80 #
+  81 # primary state: line
+  82 #   stream of 512 bytes; abort if it ever overflows
+  83 
+  84 # conceptual hierarchy within a line:
+  85 #   line = words separated by ' ', maybe followed by comment starting with '#'
+  86 #   word = datum until '/', then 0 or more metadata separated by '/'
+  87 #
+  88 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
+  89 #   next-token(stream, delim char) -> slice (start, end pointers)
+  90 #   next-token-from-slice(start, end, delim char) -> slice
+  91 #   slice-equal?(slice, string)
+  92 
+  93 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+  94     # pseudocode:
+  95     #   var line = new-stream(512, 1)
+  96     #   var in-code? = false
+  97     #   while true
+  98     #     clear-stream(line)
+  99     #     read-line(in, line)
+ 100     #     if (line->write == 0) break             # end of file
+ 101     #     var word-slice = next-word(line)
+ 102     #     if slice-empty?(word-slice)             # whitespace
+ 103     #       write-stream-data(out, line)
+ 104     #     else if (slice-equal?(word-slice, "=="))
+ 105     #       word-slice = next-word(line)
+ 106     #       in-code? = slice-equal?(word-slice, "code")
+ 107     #       write-stream-data(out, line)
+ 108     #     else if (in-code?)
+ 109     #       rewind-stream(line)
+ 110     #       convert-instruction(line, out)
+ 111     #     else
+ 112     #       rewind-stream(line)
+ 113     #       convert-data(line, out)
+ 114     #   flush(out)
+ 115     #
+ 116     # . prolog
+ 117     55/push-EBP
+ 118     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 119     # . save registers
+ 120     50/push-EAX
+ 121     51/push-ECX
+ 122     52/push-EDX
+ 123     53/push-EBX
+ 124     # var line/ECX : (address stream byte) = stream(512)
+ 125     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
+ 126     68/push  0x200/imm32/length
+ 127     68/push  0/imm32/read
+ 128     68/push  0/imm32/write
+ 129     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 130     # var word-slice/EDX = {0, 0}
+ 131     68/push  0/imm32/end
+ 132     68/push  0/imm32/curr
+ 133     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 134     # var in-code?/EBX = false
+ 135     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+ 136 $convert:loop:
+ 137     # clear-stream(line)
+ 138     # . . push args
+ 139     51/push-ECX
+ 140     # . . call
+ 141     e8/call  clear-stream/disp32
+ 142     # . . discard args
+ 143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 144     # read-line(in, line)
+ 145     # . . push args
+ 146     51/push-ECX
+ 147     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 148     # . . call
+ 149     e8/call  read-line/disp32
+ 150     # . . discard args
+ 151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 152 $convert:check0:
+ 153     # if (line->write == 0) break
+ 154     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
+ 155     0f 84/jump-if-equal  $convert:break/disp32
+ 156 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+ 190     # next-word(line, word-slice)
+ 191     # . . push args
+ 192     52/push-EDX
+ 193     51/push-ECX
+ 194     # . . call
+ 195     e8/call  next-word/disp32
+ 196     # . . discard args
+ 197     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 198 $convert:check1:
+ 199     # if (slice-empty?(word-slice)) write-stream-data(out, line)
+ 200     # . EAX = slice-empty?(word-slice)
+ 201     # . . push args
+ 202     52/push-EDX
+ 203     # . . call
+ 204     e8/call  slice-empty?/disp32
+ 205     # . . discard args
+ 206     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 207     # . if (EAX != 0) write-stream-data(out, line)
+ 208     3d/compare-EAX-and  0/imm32
+ 209     0f 85/jump-if-not-equal  $convert:pass-through/disp32
+ 210 $convert:check2:
+ 211 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+ 261     # if (slice-equal?(word-slice, "=="))
+ 262     #   word-slice = next-word(line)
+ 263     #   in-code? = slice-equal?(word-slice, "code")
+ 264     #   write-stream-data(out, line)
+ 265     # . EAX = slice-equal?(word-slice, "==")
+ 266     # . . push args
+ 267     68/push  "=="/imm32
+ 268     52/push-EDX
+ 269     # . . call
+ 270     e8/call  slice-equal?/disp32
+ 271     # . . discard args
+ 272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 273     # . if (EAX == 0) goto check3
+ 274     3d/compare-EAX-and  0/imm32
+ 275     0f 84/jump-if-equal  $convert:check3/disp32
+ 276     # . next-word(line, word-slice)
+ 277     # . . push args
+ 278     52/push-EDX
+ 279     51/push-ECX
+ 280     # . . call
+ 281     e8/call  next-word/disp32
+ 282     # . . discard args
+ 283     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 284 +-- 50 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
+ 334     # . in-code? = slice-equal?(word-slice, "code")
+ 335     # . . push args
+ 336     68/push  "code"/imm32
+ 337     52/push-EDX
+ 338     # . . call
+ 339     e8/call  slice-equal?/disp32
+ 340     # . . discard args
+ 341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 342     # . . in-code? = EAX
+ 343     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+ 344     # . goto pass-through
+ 345     eb/jump  $convert:pass-through/disp8
+ 346 $convert:check3:
+ 347     # else rewind-stream(line)
+ 348     # . rewind-stream(line)
+ 349     # . . push args
+ 350     51/push-ECX
+ 351     # . . call
+ 352     e8/call  rewind-stream/disp32
+ 353     # . . discard args
+ 354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 355     # if (in-code? != 0) convert-instruction(line, out)
+ 356     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
+ 357     74/jump-if-equal  $convert:data/disp8
+ 358 $convert:code:
+ 359     # . convert-instruction(line, out)
+ 360     # . . push args
+ 361     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 362     51/push-ECX
+ 363     # . . call
+ 364     e8/call  convert-instruction/disp32
+ 365     # . . discard args
+ 366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 367     # . loop
+ 368     e9/jump  $convert:loop/disp32
+ 369 $convert:data:
+ 370     # else convert-data(line, out)
+ 371     # . . push args
+ 372     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 373     51/push-ECX
+ 374     # . . call
+ 375     e8/call  convert-data/disp32
+ 376     # . . discard args
+ 377     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 378     # . loop
+ 379     e9/jump  $convert:loop/disp32
+ 380 $convert:pass-through:
+ 381     # write-stream-data(out, line)
+ 382     # . . push args
+ 383     51/push-ECX
+ 384     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 385     # . . call
+ 386     e8/call  write-stream-data/disp32
+ 387     # . . discard args
+ 388     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 389     # . loop
+ 390     e9/jump  $convert:loop/disp32
+ 391 $convert:break:
+ 392     # flush(out)
+ 393     # . . push args
+ 394     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 395     # . . call
+ 396     e8/call  flush/disp32
+ 397     # . . discard args
+ 398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 399 $convert:end:
+ 400     # . reclaim locals
+ 401     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
+ 402     # . restore registers
+ 403     5b/pop-to-EBX
+ 404     5a/pop-to-EDX
+ 405     59/pop-to-ECX
+ 406     58/pop-to-EAX
+ 407     # . epilog
+ 408     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 409     5d/pop-to-EBP
+ 410     c3/return
+ 411 
+ 412 test-convert-passes-empty-lines-through:
+ 413     # if a line is empty, pass it along unchanged
+ 414     # . prolog
+ 415     55/push-EBP
+ 416     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 417     # setup
+ 418     # . clear-stream(_test-input-stream)
+ 419     # . . push args
+ 420     68/push  _test-input-stream/imm32
+ 421     # . . call
+ 422     e8/call  clear-stream/disp32
+ 423     # . . discard args
+ 424     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 425     # . clear-stream(_test-input-buffered-file+4)
+ 426     # . . push args
+ 427     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 428     05/add-to-EAX  4/imm32
+ 429     50/push-EAX
+ 430     # . . call
+ 431     e8/call  clear-stream/disp32
+ 432     # . . discard args
+ 433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 434     # . clear-stream(_test-output-stream)
+ 435     # . . push args
+ 436     68/push  _test-output-stream/imm32
+ 437     # . . call
+ 438     e8/call  clear-stream/disp32
+ 439     # . . discard args
+ 440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 441     # . clear-stream(_test-output-buffered-file+4)
+ 442     # . . push args
+ 443     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 444     05/add-to-EAX  4/imm32
+ 445     50/push-EAX
+ 446     # . . call
+ 447     e8/call  clear-stream/disp32
+ 448     # . . discard args
+ 449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 450     # write nothing to input
+ 451     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 452     # . . push args
+ 453     68/push  _test-output-buffered-file/imm32
+ 454     68/push  _test-input-buffered-file/imm32
+ 455     # . . call
+ 456     e8/call  convert/disp32
+ 457     # . . discard args
+ 458     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 459     # check that the line just passed through
+ 460     # . flush(_test-output-buffered-file)
+ 461     # . . push args
+ 462     68/push  _test-output-buffered-file/imm32
+ 463     # . . call
+ 464     e8/call  flush/disp32
+ 465     # . . discard args
+ 466     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 467     # . check-stream-equal(_test-output-stream, "", msg)
+ 468     # . . push args
+ 469     68/push  "F - test-convert-passes-empty-lines-through"/imm32
+ 470     68/push  ""/imm32
+ 471     68/push  _test-output-stream/imm32
+ 472     # . . call
+ 473     e8/call  check-stream-equal/disp32
+ 474     # . . discard args
+ 475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 476     # . epilog
+ 477     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 478     5d/pop-to-EBP
+ 479     c3/return
+ 480 
+ 481 test-convert-passes-lines-with-just-whitespace-through:
+ 482     # if a line is empty, pass it along unchanged
+ 483     # . prolog
+ 484     55/push-EBP
+ 485     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 486     # setup
+ 487     # . clear-stream(_test-input-stream)
+ 488     # . . push args
+ 489     68/push  _test-input-stream/imm32
+ 490     # . . call
+ 491     e8/call  clear-stream/disp32
+ 492     # . . discard args
+ 493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 494     # . clear-stream(_test-input-buffered-file+4)
+ 495     # . . push args
+ 496     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 497     05/add-to-EAX  4/imm32
+ 498     50/push-EAX
+ 499     # . . call
+ 500     e8/call  clear-stream/disp32
+ 501     # . . discard args
+ 502     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 503     # . clear-stream(_test-output-stream)
+ 504     # . . push args
+ 505     68/push  _test-output-stream/imm32
+ 506     # . . call
+ 507     e8/call  clear-stream/disp32
+ 508     # . . discard args
+ 509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 510     # . clear-stream(_test-output-buffered-file+4)
+ 511     # . . push args
+ 512     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 513     05/add-to-EAX  4/imm32
+ 514     50/push-EAX
+ 515     # . . call
+ 516     e8/call  clear-stream/disp32
+ 517     # . . discard args
+ 518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 519     # initialize input
+ 520     # . write(_test-input-stream, "    ")
+ 521     # . . push args
+ 522     68/push  "    "/imm32
+ 523     68/push  _test-input-stream/imm32
+ 524     # . . call
+ 525     e8/call  write/disp32
+ 526     # . . discard args
+ 527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 528     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 529     # . . push args
+ 530     68/push  _test-output-buffered-file/imm32
+ 531     68/push  _test-input-buffered-file/imm32
+ 532     # . . call
+ 533     e8/call  convert/disp32
+ 534     # . . discard args
+ 535     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 536     # check that the line just passed through
+ 537     # . flush(_test-output-buffered-file)
+ 538     # . . push args
+ 539     68/push  _test-output-buffered-file/imm32
+ 540     # . . call
+ 541     e8/call  flush/disp32
+ 542     # . . discard args
+ 543     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 544     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
+ 545     # . . push args
+ 546     68/push  "F - test-convert-passes-with-just-whitespace-through"/imm32
+ 547     68/push  "    "/imm32
+ 548     68/push  _test-output-stream/imm32
+ 549     # . . call
+ 550     e8/call  check-next-stream-line-equal/disp32
+ 551     # . . discard args
+ 552     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 553     # . epilog
+ 554     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 555     5d/pop-to-EBP
+ 556     c3/return
+ 557 
+ 558 test-convert-passes-segment-headers-through:
+ 559     # if a line starts with '==', pass it along unchanged
+ 560     # . prolog
+ 561     55/push-EBP
+ 562     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 563     # setup
+ 564     # . clear-stream(_test-input-stream)
+ 565     # . . push args
+ 566     68/push  _test-input-stream/imm32
+ 567     # . . call
+ 568     e8/call  clear-stream/disp32
+ 569     # . . discard args
+ 570     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 571     # . clear-stream(_test-input-buffered-file+4)
+ 572     # . . push args
+ 573     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 574     05/add-to-EAX  4/imm32
+ 575     50/push-EAX
+ 576     # . . call
+ 577     e8/call  clear-stream/disp32
+ 578     # . . discard args
+ 579     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 580     # . clear-stream(_test-output-stream)
+ 581     # . . push args
+ 582     68/push  _test-output-stream/imm32
+ 583     # . . call
+ 584     e8/call  clear-stream/disp32
+ 585     # . . discard args
+ 586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 587     # . clear-stream(_test-output-buffered-file+4)
+ 588     # . . push args
+ 589     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 590     05/add-to-EAX  4/imm32
+ 591     50/push-EAX
+ 592     # . . call
+ 593     e8/call  clear-stream/disp32
+ 594     # . . discard args
+ 595     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 596     # initialize input
+ 597     # . write(_test-input-stream, "== abcd")
+ 598     # . . push args
+ 599     68/push  "== abcd"/imm32
+ 600     68/push  _test-input-stream/imm32
+ 601     # . . call
+ 602     e8/call  write/disp32
+ 603     # . . discard args
+ 604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 605     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 606     # . . push args
+ 607     68/push  _test-output-buffered-file/imm32
+ 608     68/push  _test-input-buffered-file/imm32
+ 609     # . . call
+ 610     e8/call  convert/disp32
+ 611     # . . discard args
+ 612     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 613     # check that the line just passed through
+ 614     # . flush(_test-output-buffered-file)
+ 615     # . . push args
+ 616     68/push  _test-output-buffered-file/imm32
+ 617     # . . call
+ 618     e8/call  flush/disp32
+ 619     # . . discard args
+ 620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 621     # . check-stream-equal(_test-output-stream, "== abcd", msg)
+ 622     # . . push args
+ 623     68/push  "F - test-convert-passes-segment-headers-through"/imm32
+ 624     68/push  "== abcd"/imm32
+ 625     68/push  _test-output-stream/imm32
+ 626     # . . call
+ 627     e8/call  check-stream-equal/disp32
+ 628     # . . discard args
+ 629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 630     # . epilog
+ 631     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 632     5d/pop-to-EBP
+ 633     c3/return
+ 634 
+ 635 test-convert-in-data-segment:
+ 636     # correctly process lines in the data segment
+ 637     # . prolog
+ 638     55/push-EBP
+ 639     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 640     # setup
+ 641     # . clear-stream(_test-input-stream)
+ 642     # . . push args
+ 643     68/push  _test-input-stream/imm32
+ 644     # . . call
+ 645     e8/call  clear-stream/disp32
+ 646     # . . discard args
+ 647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 648     # . clear-stream(_test-input-buffered-file+4)
+ 649     # . . push args
+ 650     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 651     05/add-to-EAX  4/imm32
+ 652     50/push-EAX
+ 653     # . . call
+ 654     e8/call  clear-stream/disp32
+ 655     # . . discard args
+ 656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 657     # . clear-stream(_test-output-stream)
+ 658     # . . push args
+ 659     68/push  _test-output-stream/imm32
+ 660     # . . call
+ 661     e8/call  clear-stream/disp32
+ 662     # . . discard args
+ 663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 664     # . clear-stream(_test-output-buffered-file+4)
+ 665     # . . push args
+ 666     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 667     05/add-to-EAX  4/imm32
+ 668     50/push-EAX
+ 669     # . . call
+ 670     e8/call  clear-stream/disp32
+ 671     # . . discard args
+ 672     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 673     # initialize input
+ 674     #   == code
+ 675     #   == data
+ 676     #   3 4/imm32
  677     # . write(_test-input-stream, "== code")
  678     # . . push args
  679     68/push  "== code"/imm32
- 680     68/push  _test-input-stream/imm32
+ 680     68/push  _test-input-stream/imm32
  681     # . . call
  682     e8/call  write/disp32
  683     # . . discard args
@@ -618,7 +618,7 @@ if ('onhashchange' in window) {
  685     # . write(_test-input-stream, "\n")
  686     # . . push args
  687     68/push  Newline/imm32
- 688     68/push  _test-input-stream/imm32
+ 688     68/push  _test-input-stream/imm32
  689     # . . call
  690     e8/call  write/disp32
  691     # . . discard args
@@ -626,7 +626,7 @@ if ('onhashchange' in window) {
  693     # . write(_test-input-stream, "== data")
  694     # . . push args
  695     68/push  "== data"/imm32
- 696     68/push  _test-input-stream/imm32
+ 696     68/push  _test-input-stream/imm32
  697     # . . call
  698     e8/call  write/disp32
  699     # . . discard args
@@ -634,7 +634,7 @@ if ('onhashchange' in window) {
  701     # . write(_test-input-stream, "\n")
  702     # . . push args
  703     68/push  Newline/imm32
- 704     68/push  _test-input-stream/imm32
+ 704     68/push  _test-input-stream/imm32
  705     # . . call
  706     e8/call  write/disp32
  707     # . . discard args
@@ -642,7 +642,7 @@ if ('onhashchange' in window) {
  709     # . write(_test-input-stream, "3 4/imm32")
  710     # . . push args
  711     68/push  "3 4/imm32"/imm32
- 712     68/push  _test-input-stream/imm32
+ 712     68/push  _test-input-stream/imm32
  713     # . . call
  714     e8/call  write/disp32
  715     # . . discard args
@@ -650,51 +650,51 @@ if ('onhashchange' in window) {
  717     # . write(_test-input-stream, "\n")
  718     # . . push args
  719     68/push  Newline/imm32
- 720     68/push  _test-input-stream/imm32
+ 720     68/push  _test-input-stream/imm32
  721     # . . call
  722     e8/call  write/disp32
  723     # . . discard args
  724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  725     # convert(_test-input-buffered-file, _test-output-buffered-file)
  726     # . . push args
- 727     68/push  _test-output-buffered-file/imm32
- 728     68/push  _test-input-buffered-file/imm32
+ 727     68/push  _test-output-buffered-file/imm32
+ 728     68/push  _test-input-buffered-file/imm32
  729     # . . call
- 730     e8/call  convert/disp32
+ 730     e8/call  convert/disp32
  731     # . . discard args
  732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  733     # check output
- 734 +-- 34 lines: #?     # debug print -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 734 +-- 34 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
  768     # . flush(_test-output-buffered-file)
  769     # . . push args
- 770     68/push  _test-output-buffered-file/imm32
+ 770     68/push  _test-output-buffered-file/imm32
  771     # . . call
  772     e8/call  flush/disp32
  773     # . . discard args
  774     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
  775     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
  776     # . . push args
- 777     68/push  "F - test-convert-in-data-segment/0"/imm32
+ 777     68/push  "F - test-convert-in-data-segment/0"/imm32
  778     68/push  "== code"/imm32
- 779     68/push  _test-output-stream/imm32
+ 779     68/push  _test-output-stream/imm32
  780     # . . call
  781     e8/call  check-next-stream-line-equal/disp32
  782     # . . discard args
  783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
  784     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
  785     # . . push args
- 786     68/push  "F - test-convert-in-data-segment/1"/imm32
+ 786     68/push  "F - test-convert-in-data-segment/1"/imm32
  787     68/push  "== data"/imm32
- 788     68/push  _test-output-stream/imm32
+ 788     68/push  _test-output-stream/imm32
  789     # . . call
  790     e8/call  check-next-stream-line-equal/disp32
  791     # . . discard args
  792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
  793     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
  794     # . . push args
- 795     68/push  "F - test-convert-in-data-segment/2"/imm32
+ 795     68/push  "F - test-convert-in-data-segment/2"/imm32
  796     68/push  "03 04 00 00 00 "/imm32
- 797     68/push  _test-output-stream/imm32
+ 797     68/push  _test-output-stream/imm32
  798     # . . call
  799     e8/call  check-next-stream-line-equal/disp32
  800     # . . discard args
@@ -704,433 +704,368 @@ if ('onhashchange' in window) {
  804     5d/pop-to-EBP
  805     c3/return
  806 
- 807 convert-data:  # line : (address stream byte), out : (address buffered-file) -> <void>
- 808     # pseudocode:
- 809     #   while true
- 810     #     word-slice = next-word(line)
- 811     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
- 812     #       break  # skip emitting some whitespace
- 813     #     if slice-starts-with?(word-slice, "#")      # comment
- 814     #       write-stream-buffered(out, line)
- 815     #       break
- 816     #     if slice-ends-with?(word-slice, ":")        # label
- 817     #       write-stream-buffered(out, line)
- 818     #       break
- 819     #     if has-metadata?(word-slice, "imm32")
- 820     #       emit(out, word-slice, 4)
- 821     #     # disp32 is not permitted in data segments, and anything else is only a byte long
- 822     #     else
- 823     #       emit(out, word-slice, 1)
- 824     #
- 825     # . prolog
- 826     55/push-EBP
- 827     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 828     # . save registers
- 829     50/push-EAX
- 830     51/push-ECX
- 831     52/push-EDX
- 832     # var word-slice/ECX = {0, 0}
- 833     68/push  0/imm32/end
- 834     68/push  0/imm32/start
- 835     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 836 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 862 $convert-data:loop:
- 863     # next-word(line, word-slice)
- 864     # . . push args
- 865     51/push-ECX
- 866     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 867     # . . call
- 868     e8/call  next-word/disp32
- 869     # . . discard args
- 870     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 871 +-- 26 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 897 $convert-data:check0:
- 898     # if (slice-empty?(word-slice)) break
- 899     # . EAX = slice-empty?(word-slice)
+ 807 test-convert-code-and-data-segments:
+ 808     # correctly process lines in both code and data segments
+ 809     # . prolog
+ 810     55/push-EBP
+ 811     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 812     # setup
+ 813     # . clear-stream(_test-input-stream)
+ 814     # . . push args
+ 815     68/push  _test-input-stream/imm32
+ 816     # . . call
+ 817     e8/call  clear-stream/disp32
+ 818     # . . discard args
+ 819     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 820     # . clear-stream(_test-input-buffered-file+4)
+ 821     # . . push args
+ 822     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 823     05/add-to-EAX  4/imm32
+ 824     50/push-EAX
+ 825     # . . call
+ 826     e8/call  clear-stream/disp32
+ 827     # . . discard args
+ 828     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 829     # . clear-stream(_test-output-stream)
+ 830     # . . push args
+ 831     68/push  _test-output-stream/imm32
+ 832     # . . call
+ 833     e8/call  clear-stream/disp32
+ 834     # . . discard args
+ 835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 836     # . clear-stream(_test-output-buffered-file+4)
+ 837     # . . push args
+ 838     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 839     05/add-to-EAX  4/imm32
+ 840     50/push-EAX
+ 841     # . . call
+ 842     e8/call  clear-stream/disp32
+ 843     # . . discard args
+ 844     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 845     # initialize input
+ 846     #   == code
+ 847     #   e8/call 20/disp32
+ 848     #   68/push 0x20/imm8
+ 849     #   == data
+ 850     #   3 4/imm32
+ 851     # . write(_test-input-stream, "== code")
+ 852     # . . push args
+ 853     68/push  "== code"/imm32
+ 854     68/push  _test-input-stream/imm32
+ 855     # . . call
+ 856     e8/call  write/disp32
+ 857     # . . discard args
+ 858     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 859     # . write(_test-input-stream, "\n")
+ 860     # . . push args
+ 861     68/push  Newline/imm32
+ 862     68/push  _test-input-stream/imm32
+ 863     # . . call
+ 864     e8/call  write/disp32
+ 865     # . . discard args
+ 866     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 867     # . write(_test-input-stream, "e8/call 20/disp32")
+ 868     # . . push args
+ 869     68/push  "e8/call 20/disp32"/imm32
+ 870     68/push  _test-input-stream/imm32
+ 871     # . . call
+ 872     e8/call  write/disp32
+ 873     # . . discard args
+ 874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 875     # . write(_test-input-stream, "\n")
+ 876     # . . push args
+ 877     68/push  Newline/imm32
+ 878     68/push  _test-input-stream/imm32
+ 879     # . . call
+ 880     e8/call  write/disp32
+ 881     # . . discard args
+ 882     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 883     # . write(_test-input-stream, "68/push 0x20/imm8")
+ 884     # . . push args
+ 885     68/push  "68/push 0x20/imm8"/imm32
+ 886     68/push  _test-input-stream/imm32
+ 887     # . . call
+ 888     e8/call  write/disp32
+ 889     # . . discard args
+ 890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 891     # . write(_test-input-stream, "\n")
+ 892     # . . push args
+ 893     68/push  Newline/imm32
+ 894     68/push  _test-input-stream/imm32
+ 895     # . . call
+ 896     e8/call  write/disp32
+ 897     # . . discard args
+ 898     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 899     # . write(_test-input-stream, "== data")
  900     # . . push args
- 901     51/push-ECX
- 902     # . . call
- 903     e8/call  slice-empty?/disp32
- 904     # . . discard args
- 905     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 906     # . if (EAX != 0) pass through
- 907     3d/compare-EAX  0/imm32
- 908     75/jump-if-not-equal  $convert-data:break/disp8
- 909 $convert-data:check1:
- 910     # if (slice-starts-with?(word-slice, "#")) write-stream-buffered(out, line)
- 911     # . start/EDX = word-slice->start
- 912     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
- 913     # . c/EAX = *start
- 914     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 915     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
- 916     # . if (EAX == '#') pass through
- 917     3d/compare-with-EAX  0x23/imm32/hash
- 918     74/jump-if-equal  $convert-data:pass-line-through/disp8
- 919 $convert-data:check2:
- 920     # if (slice-ends-with?(word-slice, ":")) write-stream-buffered(out, line)
- 921     # . end/EDX = word-slice->end
- 922     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
- 923     # . c/EAX = *(end-1)
- 924     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 925     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
- 926     # . if (EAX == ':') pass through
- 927     3d/compare-with-EAX  0x3a/imm32/colon
- 928     74/jump-if-equal  $convert-data:pass-line-through/disp8
- 929 $convert-data:check3:
- 930     # if (has-metadata?(word-slice, "imm32"))
- 931     # . EAX = has-metadata?(ECX, "c")
+ 901     68/push  "== data"/imm32
+ 902     68/push  _test-input-stream/imm32
+ 903     # . . call
+ 904     e8/call  write/disp32
+ 905     # . . discard args
+ 906     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 907     # . write(_test-input-stream, "\n")
+ 908     # . . push args
+ 909     68/push  Newline/imm32
+ 910     68/push  _test-input-stream/imm32
+ 911     # . . call
+ 912     e8/call  write/disp32
+ 913     # . . discard args
+ 914     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 915     # . write(_test-input-stream, "3 4/imm32")
+ 916     # . . push args
+ 917     68/push  "3 4/imm32"/imm32
+ 918     68/push  _test-input-stream/imm32
+ 919     # . . call
+ 920     e8/call  write/disp32
+ 921     # . . discard args
+ 922     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 923     # . write(_test-input-stream, "\n")
+ 924     # . . push args
+ 925     68/push  Newline/imm32
+ 926     68/push  _test-input-stream/imm32
+ 927     # . . call
+ 928     e8/call  write/disp32
+ 929     # . . discard args
+ 930     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 931     # convert(_test-input-buffered-file, _test-output-buffered-file)
  932     # . . push args
- 933     68/push  "imm32"/imm32
- 934     51/push-ECX
+ 933     68/push  _test-output-buffered-file/imm32
+ 934     68/push  _test-input-buffered-file/imm32
  935     # . . call
- 936     e8/call  has-metadata?/disp32
+ 936     e8/call  convert/disp32
  937     # . . discard args
- 938     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
- 939     # . if (EAX == 0) process as a single byte
- 940     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
- 941     74/jump-if-equal  $convert-data:single-byte/disp8
- 942 $convert-data:imm32:
- 943     # emit(out, word-slice, 4)
- 944     # . . push args
- 945     68/push  4/imm32
- 946     51/push-ECX
- 947     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 948     # . . call
- 949     e8/call  emit/disp32
- 950     # . . discard args
- 951     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 952     e9/jump  $convert-data:loop/disp32
- 953 $convert-data:single-byte:
- 954     # emit(out, word-slice, 1)
- 955     # . . push args
- 956     68/push  1/imm32
- 957     51/push-ECX
- 958     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 959     # . . call
- 960     e8/call  emit/disp32
- 961     # . . discard args
- 962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 963     e9/jump  $convert-data:loop/disp32
- 964 $convert-data:pass-line-through:
- 965     # write-stream-buffered(out, line)
- 966     # . . push args
- 967     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 968     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 969     # . . call
- 970     e8/call  write-stream-buffered/disp32
- 971     # . . discard args
- 972     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 973     # break
- 974 $convert-data:break:
- 975 $convert-data:end:
- 976     # . reclaim locals
- 977     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 978     # . restore registers
- 979     5a/pop-to-EDX
- 980     59/pop-to-ECX
- 981     58/pop-to-EAX
- 982     # . epilog
- 983     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 984     5d/pop-to-EBP
- 985     c3/return
- 986 
- 987 test-convert-data-passes-comments-through:
- 988     # if a line starts with '#', pass it along unchanged
- 989     # . prolog
- 990     55/push-EBP
- 991     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 992     # setup
- 993     # . clear-stream(_test-input-stream)
- 994     # . . push args
- 995     68/push  _test-input-stream/imm32
- 996     # . . call
- 997     e8/call  clear-stream/disp32
- 998     # . . discard args
- 999     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1000     # . clear-stream(_test-output-stream)
-1001     # . . push args
-1002     68/push  _test-output-stream/imm32
-1003     # . . call
-1004     e8/call  clear-stream/disp32
-1005     # . . discard args
-1006     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1007     # . clear-stream(_test-output-buffered-file+4)
-1008     # . . push args
-1009     b8/copy-to-EAX  _test-buffered-file/imm32
-1010     05/add-to-EAX  4/imm32
-1011     50/push-EAX
-1012     # . . call
-1013     e8/call  clear-stream/disp32
-1014     # . . discard args
-1015     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1016     # initialize input
-1017     # . write(_test-input-stream, "# abcd")
-1018     # . . push args
-1019     68/push  "# abcd"/imm32
-1020     68/push  _test-input-stream/imm32
-1021     # . . call
-1022     e8/call  write/disp32
-1023     # . . discard args
-1024     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1025     # convert-data(_test-input-stream, _test-output-buffered-file)
-1026     # . . push args
-1027     68/push  _test-output-buffered-file/imm32
-1028     68/push  _test-input-stream/imm32
-1029     # . . call
-1030     e8/call  convert-data/disp32
-1031     # . . discard args
-1032     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1033     # check that the line just passed through
-1034     # . flush(_test-output-buffered-file)
-1035     # . . push args
-1036     68/push  _test-output-buffered-file/imm32
-1037     # . . call
-1038     e8/call  flush/disp32
-1039     # . . discard args
-1040     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1041     # . check-stream-equal(_test-output-stream, "# abcd", msg)
-1042     # . . push args
-1043     68/push  "F - test-convert-data-passes-comments-through"/imm32
-1044     68/push  "# abcd"/imm32
-1045     68/push  _test-output-stream/imm32
-1046     # . . call
-1047     e8/call  check-stream-equal/disp32
-1048     # . . discard args
-1049     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1050     # . epilog
-1051     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1052     5d/pop-to-EBP
-1053     c3/return
-1054 
-1055 test-convert-data-passes-labels-through:
-1056     # if the first word ends with ':', pass along the entire line unchanged
-1057     # . prolog
-1058     55/push-EBP
-1059     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1060     # setup
-1061     # . clear-stream(_test-input-stream)
-1062     # . . push args
-1063     68/push  _test-input-stream/imm32
-1064     # . . call
-1065     e8/call  clear-stream/disp32
-1066     # . . discard args
-1067     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1068     # . clear-stream(_test-output-stream)
-1069     # . . push args
-1070     68/push  _test-output-stream/imm32
-1071     # . . call
-1072     e8/call  clear-stream/disp32
-1073     # . . discard args
-1074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1075     # . clear-stream(_test-output-buffered-file+4)
-1076     # . . push args
-1077     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1078     05/add-to-EAX  4/imm32
-1079     50/push-EAX
-1080     # . . call
-1081     e8/call  clear-stream/disp32
-1082     # . . discard args
-1083     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1084     # initialize input
-1085     # . write(_test-input-stream, "ab: # cd")
-1086     # . . push args
-1087     68/push  "ab: # cd"/imm32
-1088     68/push  _test-input-stream/imm32
-1089     # . . call
-1090     e8/call  write/disp32
-1091     # . . discard args
-1092     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1093     # convert-data(_test-input-stream, _test-output-buffered-file)
-1094     # . . push args
-1095     68/push  _test-output-buffered-file/imm32
-1096     68/push  _test-input-stream/imm32
-1097     # . . call
-1098     e8/call  convert-data/disp32
-1099     # . . discard args
-1100     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1101     # check that the line just passed through
-1102     # . flush(_test-output-buffered-file)
-1103     # . . push args
-1104     68/push  _test-output-buffered-file/imm32
+ 938     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 939     # check output
+ 940     #   == code
+ 941     #   e8 20 00 00 00  # e8/call 20/disp32
+ 942     #   68 20  # 68/push 0x20/imm8
+ 943     #   == data
+ 944     #   03 04 00 00 00
+ 945 +-- 34 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
+ 979     # . flush(_test-output-buffered-file)
+ 980     # . . push args
+ 981     68/push  _test-output-buffered-file/imm32
+ 982     # . . call
+ 983     e8/call  flush/disp32
+ 984     # . . discard args
+ 985     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 986     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
+ 987     # . . push args
+ 988     68/push  "F - test-convert-code-and-data-segments/0"/imm32
+ 989     68/push  "== code"/imm32
+ 990     68/push  _test-output-stream/imm32
+ 991     # . . call
+ 992     e8/call  check-next-stream-line-equal/disp32
+ 993     # . . discard args
+ 994     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 995     # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
+ 996     # . . push args
+ 997     68/push  "F - test-convert-code-and-data-segments/1"/imm32
+ 998     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
+ 999     68/push  _test-output-stream/imm32
+1000     # . . call
+1001     e8/call  check-next-stream-line-equal/disp32
+1002     # . . discard args
+1003     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1004     # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
+1005     # . . push args
+1006     68/push  "F - test-convert-code-and-data-segments/2"/imm32
+1007     68/push  "68 20  # 68/push 0x20/imm8"/imm32
+1008     68/push  _test-output-stream/imm32
+1009     # . . call
+1010     e8/call  check-next-stream-line-equal/disp32
+1011     # . . discard args
+1012     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1013     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
+1014     # . . push args
+1015     68/push  "F - test-convert-code-and-data-segments/3"/imm32
+1016     68/push  "== data"/imm32
+1017     68/push  _test-output-stream/imm32
+1018     # . . call
+1019     e8/call  check-next-stream-line-equal/disp32
+1020     # . . discard args
+1021     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1022     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
+1023     # . . push args
+1024     68/push  "F - test-convert-code-and-data-segments/4"/imm32
+1025     68/push  "03 04 00 00 00 "/imm32
+1026     68/push  _test-output-stream/imm32
+1027     # . . call
+1028     e8/call  check-next-stream-line-equal/disp32
+1029     # . . discard args
+1030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1031     # . epilog
+1032     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1033     5d/pop-to-EBP
+1034     c3/return
+1035 
+1036 convert-data:  # line : (address stream byte), out : (address buffered-file) -> <void>
+1037     # pseudocode:
+1038     #   var word-slice = {0, 0}
+1039     #   while true
+1040     #     word-slice = next-word(line)
+1041     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
+1042     #       break  # skip emitting some whitespace
+1043     #     if slice-starts-with?(word-slice, "#")      # comment
+1044     #       write-slice(out, word-slice)
+1045     #       break
+1046     #     if slice-ends-with?(word-slice, ":")        # label
+1047     #       write-stream-data(out, line)
+1048     #       break
+1049     #     if has-metadata?(word-slice, "imm32")
+1050     #       emit(out, word-slice, 4)
+1051     #     # disp32 is not permitted in data segments, and anything else is only a byte long
+1052     #     else
+1053     #       emit(out, word-slice, 1)
+1054     #
+1055     # . prolog
+1056     55/push-EBP
+1057     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1058     # . save registers
+1059     50/push-EAX
+1060     51/push-ECX
+1061     52/push-EDX
+1062     # var word-slice/ECX = {0, 0}
+1063     68/push  0/imm32/end
+1064     68/push  0/imm32/start
+1065     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1066 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+1100 $convert-data:loop:
+1101     # next-word(line, word-slice)
+1102     # . . push args
+1103     51/push-ECX
+1104     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 1105     # . . call
-1106     e8/call  flush/disp32
+1106     e8/call  next-word/disp32
 1107     # . . discard args
-1108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1109     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
-1110     # . . push args
-1111     68/push  "F - test-convert-data-passes-labels-through"/imm32
-1112     68/push  "ab: # cd"/imm32
-1113     68/push  _test-output-stream/imm32
-1114     # . . call
-1115     e8/call  check-stream-equal/disp32
-1116     # . . discard args
-1117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1118     # . epilog
-1119     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1120     5d/pop-to-EBP
-1121     c3/return
-1122 
-1123 test-convert-data-passes-names-through:
-1124     # If a word is a valid name, just emit it unchanged.
-1125     # Later phases will deal with it.
-1126     # . prolog
-1127     55/push-EBP
-1128     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1129     # setup
-1130     # . clear-stream(_test-input-stream)
-1131     # . . push args
-1132     68/push  _test-input-stream/imm32
-1133     # . . call
-1134     e8/call  clear-stream/disp32
-1135     # . . discard args
-1136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1137     # . clear-stream(_test-output-stream)
-1138     # . . push args
-1139     68/push  _test-output-stream/imm32
-1140     # . . call
-1141     e8/call  clear-stream/disp32
-1142     # . . discard args
-1143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1144     # . clear-stream(_test-output-buffered-file+4)
-1145     # . . push args
-1146     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1147     05/add-to-EAX  4/imm32
-1148     50/push-EAX
-1149     # . . call
-1150     e8/call  clear-stream/disp32
-1151     # . . discard args
-1152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1153     # initialize input
-1154     # . write(_test-input-stream, "abcd/imm32")
-1155     # . . push args
-1156     68/push  "abcd/imm32"/imm32
-1157     68/push  _test-input-stream/imm32
-1158     # . . call
-1159     e8/call  write/disp32
-1160     # . . discard args
-1161     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1162     # convert-data(_test-input-stream, _test-output-buffered-file)
-1163     # . . push args
-1164     68/push  _test-output-buffered-file/imm32
-1165     68/push  _test-input-stream/imm32
-1166     # . . call
-1167     e8/call  convert-data/disp32
-1168     # . . discard args
-1169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1170     # check that the line just passed through
-1171     # . flush(_test-output-buffered-file)
-1172     # . . push args
-1173     68/push  _test-output-buffered-file/imm32
-1174     # . . call
-1175     e8/call  flush/disp32
-1176     # . . discard args
-1177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1178     # . check-stream-equal(_test-output-stream, "abcd/imm32", msg)
-1179     # . . push args
-1180     68/push  "F - test-convert-data-passes-names-through"/imm32
-1181     68/push  "abcd/imm32 "/imm32
-1182     68/push  _test-output-stream/imm32
-1183     # . . call
-1184     e8/call  check-stream-equal/disp32
-1185     # . . discard args
-1186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1187     # . epilog
-1188     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1189     5d/pop-to-EBP
-1190     c3/return
-1191 
-1192 test-convert-data-handles-imm32:
-1193     # If a word has the /imm32 metadata, emit it in 4 bytes.
-1194     # . prolog
-1195     55/push-EBP
-1196     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1197     # setup
-1198     # . clear-stream(_test-input-stream)
-1199     # . . push args
-1200     68/push  _test-input-stream/imm32
-1201     # . . call
-1202     e8/call  clear-stream/disp32
-1203     # . . discard args
-1204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1205     # . clear-stream(_test-output-stream)
-1206     # . . push args
-1207     68/push  _test-output-stream/imm32
-1208     # . . call
-1209     e8/call  clear-stream/disp32
-1210     # . . discard args
-1211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1212     # . clear-stream(_test-output-buffered-file+4)
-1213     # . . push args
-1214     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1215     05/add-to-EAX  4/imm32
-1216     50/push-EAX
-1217     # . . call
-1218     e8/call  clear-stream/disp32
-1219     # . . discard args
-1220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1221     # initialize input
-1222     # . write(_test-input-stream, "30/imm32")
-1223     # . . push args
-1224     68/push  "30/imm32"/imm32
-1225     68/push  _test-input-stream/imm32
-1226     # . . call
-1227     e8/call  write/disp32
-1228     # . . discard args
-1229     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1230     # convert-data(_test-input-stream, _test-output-buffered-file)
-1231     # . . push args
-1232     68/push  _test-output-buffered-file/imm32
-1233     68/push  _test-input-stream/imm32
-1234     # . . call
-1235     e8/call  convert-data/disp32
-1236     # . . discard args
-1237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1238     # check that 4 bytes were written
-1239     # . flush(_test-output-buffered-file)
-1240     # . . push args
-1241     68/push  _test-output-buffered-file/imm32
-1242     # . . call
-1243     e8/call  flush/disp32
-1244     # . . discard args
-1245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1246     # . check-stream-equal(_test-output-stream, "30 00 00 00 ", msg)
-1247     # . . push args
-1248     68/push  "F - test-convert-data-handles-imm32"/imm32
-1249     68/push  "30 00 00 00 "/imm32
-1250     68/push  _test-output-stream/imm32
-1251     # . . call
-1252     e8/call  check-stream-equal/disp32
-1253     # . . discard args
-1254     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1255     # . epilog
-1256     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1257     5d/pop-to-EBP
-1258     c3/return
-1259 
-1260 test-convert-data-handles-single-byte:
-1261     # Any metadata but /imm32 will emit a single byte.
-1262     # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
+1108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1109 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+1159 $convert-data:check0:
+1160     # if (slice-empty?(word-slice)) break
+1161     # . EAX = slice-empty?(word-slice)
+1162     # . . push args
+1163     51/push-ECX
+1164     # . . call
+1165     e8/call  slice-empty?/disp32
+1166     # . . discard args
+1167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1168     # . if (EAX != 0) break
+1169     3d/compare-EAX-and  0/imm32
+1170     0f 85/jump-if-not-equal  $convert-data:break/disp32
+1171 $convert-data:check-for-comment:
+1172     # if (slice-starts-with?(word-slice, "#"))
+1173     # . start/EDX = word-slice->start
+1174     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+1175     # . c/EAX = *start
+1176     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+1177     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
+1178     # . if (EAX != '#') goto next check
+1179     3d/compare-EAX-and  0x23/imm32/hash
+1180     75/jump-if-not-equal  $convert-data:check-for-label/disp8
+1181 $convert-data:comment:
+1182     # write-slice(out, word-slice)
+1183     # . . push args
+1184     51/push-ECX
+1185     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1186     # . . call
+1187     e8/call  write-slice/disp32
+1188     # . . discard args
+1189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1190     # break
+1191     75/jump-if-not-equal  $convert-data:break/disp8
+1192 $convert-data:check-for-label:
+1193     # if (slice-ends-with?(word-slice, ":"))
+1194     # . end/EDX = word-slice->end
+1195     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
+1196     # . c/EAX = *(end-1)
+1197     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+1198     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
+1199     # . if (EAX != ':') goto next check
+1200     3d/compare-EAX-and  0x3a/imm32/colon
+1201     75/jump-if-not-equal  $convert-data:check-for-imm32/disp8
+1202 $convert-data:label:
+1203     # write-stream-data(out, line)
+1204     # . . push args
+1205     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1206     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1207     # . . call
+1208     e8/call  write-stream-data/disp32
+1209     # . . discard args
+1210     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1211     # break
+1212     75/jump-if-not-equal  $convert-data:break/disp8
+1213 $convert-data:check-for-imm32:
+1214     # if (has-metadata?(word-slice, "imm32"))
+1215     # . EAX = has-metadata?(ECX, "imm32")
+1216     # . . push args
+1217     68/push  "imm32"/imm32
+1218     51/push-ECX
+1219     # . . call
+1220     e8/call  has-metadata?/disp32
+1221     # . . discard args
+1222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1223     # . if (EAX == 0) process as a single byte
+1224     3d/compare-EAX-and  0/imm32
+1225     74/jump-if-equal  $convert-data:single-byte/disp8
+1226 $convert-data:imm32:
+1227     # emit(out, word-slice, 4)
+1228     # . . push args
+1229     68/push  4/imm32
+1230     51/push-ECX
+1231     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1232     # . . call
+1233     e8/call  emit/disp32
+1234     # . . discard args
+1235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1236     e9/jump  $convert-data:loop/disp32
+1237 $convert-data:single-byte:
+1238     # emit(out, word-slice, 1)
+1239     # . . push args
+1240     68/push  1/imm32
+1241     51/push-ECX
+1242     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1243     # . . call
+1244     e8/call  emit/disp32
+1245     # . . discard args
+1246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1247     e9/jump  $convert-data:loop/disp32
+1248 $convert-data:break:
+1249 $convert-data:end:
+1250     # . reclaim locals
+1251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1252     # . restore registers
+1253     5a/pop-to-EDX
+1254     59/pop-to-ECX
+1255     58/pop-to-EAX
+1256     # . epilog
+1257     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1258     5d/pop-to-EBP
+1259     c3/return
+1260 
+1261 test-convert-data-passes-comments-through:
+1262     # if a line starts with '#', pass it along unchanged
 1263     # . prolog
 1264     55/push-EBP
 1265     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 1266     # setup
 1267     # . clear-stream(_test-input-stream)
 1268     # . . push args
-1269     68/push  _test-input-stream/imm32
+1269     68/push  _test-input-stream/imm32
 1270     # . . call
 1271     e8/call  clear-stream/disp32
 1272     # . . discard args
 1273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 1274     # . clear-stream(_test-output-stream)
 1275     # . . push args
-1276     68/push  _test-output-stream/imm32
+1276     68/push  _test-output-stream/imm32
 1277     # . . call
 1278     e8/call  clear-stream/disp32
 1279     # . . discard args
 1280     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 1281     # . clear-stream(_test-output-buffered-file+4)
 1282     # . . push args
-1283     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1283     b8/copy-to-EAX  _test-output-buffered-file/imm32
 1284     05/add-to-EAX  4/imm32
 1285     50/push-EAX
 1286     # . . call
@@ -1138,2230 +1073,5137 @@ if ('onhashchange' in window) {
 1288     # . . discard args
 1289     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 1290     # initialize input
-1291     # . write(_test-input-stream, "30/imm16")
+1291     # . write(_test-input-stream, "# abcd")
 1292     # . . push args
-1293     68/push  "30/imm16"/imm32
-1294     68/push  _test-input-stream/imm32
+1293     68/push  "# abcd"/imm32
+1294     68/push  _test-input-stream/imm32
 1295     # . . call
 1296     e8/call  write/disp32
 1297     # . . discard args
 1298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 1299     # convert-data(_test-input-stream, _test-output-buffered-file)
 1300     # . . push args
-1301     68/push  _test-output-buffered-file/imm32
-1302     68/push  _test-input-stream/imm32
+1301     68/push  _test-output-buffered-file/imm32
+1302     68/push  _test-input-stream/imm32
 1303     # . . call
-1304     e8/call  convert-data/disp32
+1304     e8/call  convert-data/disp32
 1305     # . . discard args
 1306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1307     # check that a single byte was written (imm16 is not a valid operand type)
+1307     # check that the line just passed through
 1308     # . flush(_test-output-buffered-file)
 1309     # . . push args
-1310     68/push  _test-output-buffered-file/imm32
+1310     68/push  _test-output-buffered-file/imm32
 1311     # . . call
 1312     e8/call  flush/disp32
 1313     # . . discard args
 1314     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1315     # . check-stream-equal(_test-output-stream, "30 ", msg)
-1316     # . . push args
-1317     68/push  "F - test-convert-data-handles-single-byte"/imm32
-1318     68/push  "30 "/imm32
-1319     68/push  _test-output-stream/imm32
-1320     # . . call
-1321     e8/call  check-stream-equal/disp32
-1322     # . . discard args
-1323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1324     # . epilog
-1325     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1326     5d/pop-to-EBP
-1327     c3/return
-1328 
-1329 test-convert-data-multiple-bytes:
-1330     # Multiple single-byte words in input stream get processed one by one.
-1331     # . prolog
-1332     55/push-EBP
-1333     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1334     # setup
-1335     # . clear-stream(_test-input-stream)
-1336     # . . push args
-1337     68/push  _test-input-stream/imm32
-1338     # . . call
-1339     e8/call  clear-stream/disp32
-1340     # . . discard args
-1341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1342     # . clear-stream(_test-output-stream)
-1343     # . . push args
-1344     68/push  _test-output-stream/imm32
-1345     # . . call
-1346     e8/call  clear-stream/disp32
-1347     # . . discard args
-1348     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1349     # . clear-stream(_test-output-buffered-file+4)
+1315 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+1349     # . check-stream-equal(_test-output-stream, "# abcd", msg)
 1350     # . . push args
-1351     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1352     05/add-to-EAX  4/imm32
-1353     50/push-EAX
+1351     68/push  "F - test-convert-data-passes-comments-through"/imm32
+1352     68/push  "# abcd"/imm32
+1353     68/push  _test-output-stream/imm32
 1354     # . . call
-1355     e8/call  clear-stream/disp32
+1355     e8/call  check-stream-equal/disp32
 1356     # . . discard args
-1357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1358     # initialize input
-1359     # . write(_test-input-stream, "1 2")
-1360     # . . push args
-1361     68/push  "1 2"/imm32
-1362     68/push  _test-input-stream/imm32
-1363     # . . call
-1364     e8/call  write/disp32
-1365     # . . discard args
-1366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1367     # convert-data(_test-input-stream, _test-output-buffered-file)
-1368     # . . push args
-1369     68/push  _test-output-buffered-file/imm32
-1370     68/push  _test-input-stream/imm32
-1371     # . . call
-1372     e8/call  convert-data/disp32
-1373     # . . discard args
-1374     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1375     # check output
-1376     # . flush(_test-output-buffered-file)
+1357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1358     # . epilog
+1359     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1360     5d/pop-to-EBP
+1361     c3/return
+1362 
+1363 test-convert-data-passes-labels-through:
+1364     # if the first word ends with ':', pass along the entire line unchanged
+1365     # . prolog
+1366     55/push-EBP
+1367     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1368     # setup
+1369     # . clear-stream(_test-input-stream)
+1370     # . . push args
+1371     68/push  _test-input-stream/imm32
+1372     # . . call
+1373     e8/call  clear-stream/disp32
+1374     # . . discard args
+1375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1376     # . clear-stream(_test-output-stream)
 1377     # . . push args
-1378     68/push  _test-output-buffered-file/imm32
+1378     68/push  _test-output-stream/imm32
 1379     # . . call
-1380     e8/call  flush/disp32
+1380     e8/call  clear-stream/disp32
 1381     # . . discard args
 1382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1383     # . check-stream-equal(_test-output-stream, "01 02 ", msg)
+1383     # . clear-stream(_test-output-buffered-file+4)
 1384     # . . push args
-1385     68/push  "F - test-convert-data-multiple-bytes"/imm32
-1386     68/push  "01 02 "/imm32
-1387     68/push  _test-output-stream/imm32
+1385     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1386     05/add-to-EAX  4/imm32
+1387     50/push-EAX
 1388     # . . call
-1389     e8/call  check-stream-equal/disp32
+1389     e8/call  clear-stream/disp32
 1390     # . . discard args
-1391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1392     # . epilog
-1393     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1394     5d/pop-to-EBP
-1395     c3/return
-1396 
-1397 test-convert-data-byte-then-name:
-1398     # Single-byte word followed by valid name get processed one by one.
-1399     # . prolog
-1400     55/push-EBP
-1401     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1402     # setup
-1403     # . clear-stream(_test-input-stream)
-1404     # . . push args
-1405     68/push  _test-input-stream/imm32
-1406     # . . call
-1407     e8/call  clear-stream/disp32
-1408     # . . discard args
-1409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1410     # . clear-stream(_test-output-stream)
+1391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1392     # initialize input
+1393     # . write(_test-input-stream, "ab: # cd")
+1394     # . . push args
+1395     68/push  "ab: # cd"/imm32
+1396     68/push  _test-input-stream/imm32
+1397     # . . call
+1398     e8/call  write/disp32
+1399     # . . discard args
+1400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1401     # convert-data(_test-input-stream, _test-output-buffered-file)
+1402     # . . push args
+1403     68/push  _test-output-buffered-file/imm32
+1404     68/push  _test-input-stream/imm32
+1405     # . . call
+1406     e8/call  convert-data/disp32
+1407     # . . discard args
+1408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1409     # check that the line just passed through
+1410     # . flush(_test-output-buffered-file)
 1411     # . . push args
-1412     68/push  _test-output-stream/imm32
+1412     68/push  _test-output-buffered-file/imm32
 1413     # . . call
-1414     e8/call  clear-stream/disp32
+1414     e8/call  flush/disp32
 1415     # . . discard args
 1416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1417     # . clear-stream(_test-output-buffered-file+4)
+1417     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
 1418     # . . push args
-1419     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1420     05/add-to-EAX  4/imm32
-1421     50/push-EAX
+1419     68/push  "F - test-convert-data-passes-labels-through"/imm32
+1420     68/push  "ab: # cd"/imm32
+1421     68/push  _test-output-stream/imm32
 1422     # . . call
-1423     e8/call  clear-stream/disp32
+1423     e8/call  check-stream-equal/disp32
 1424     # . . discard args
-1425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1426     # initialize input
-1427     # . write(_test-input-stream, "30 abcd/o")
-1428     # . . push args
-1429     68/push  "30 abcd/o"/imm32
-1430     68/push  _test-input-stream/imm32
-1431     # . . call
-1432     e8/call  write/disp32
-1433     # . . discard args
-1434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1435     # convert-data(_test-input-stream, _test-output-buffered-file)
-1436     # . . push args
-1437     68/push  _test-output-buffered-file/imm32
-1438     68/push  _test-input-stream/imm32
-1439     # . . call
-1440     e8/call  convert-data/disp32
-1441     # . . discard args
-1442     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1443     # check output
-1444     # . flush(_test-output-buffered-file)
-1445     # . . push args
-1446     68/push  _test-output-buffered-file/imm32
-1447     # . . call
-1448     e8/call  flush/disp32
-1449     # . . discard args
-1450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1451     # . check-stream-equal(_test-output-stream, "30 abcd/o ", msg)
-1452     # . . push args
-1453     68/push  "F - test-convert-data-byte-then-name"/imm32
-1454     68/push  "30 abcd/o "/imm32
-1455     68/push  _test-output-stream/imm32
-1456     # . . call
-1457     e8/call  check-stream-equal/disp32
-1458     # . . discard args
-1459     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1460     # . epilog
-1461     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1462     5d/pop-to-EBP
-1463     c3/return
-1464 
-1465 test-convert-data-multiple-words:
-1466     # Multiple words in input stream get processed one by one.
-1467     # . prolog
-1468     55/push-EBP
-1469     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1470     # setup
-1471     # . clear-stream(_test-input-stream)
-1472     # . . push args
-1473     68/push  _test-input-stream/imm32
+1425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1426     # . epilog
+1427     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1428     5d/pop-to-EBP
+1429     c3/return
+1430 
+1431 test-convert-data-passes-names-through:
+1432     # If a word is a valid name, just emit it unchanged.
+1433     # Later phases will deal with it.
+1434     # . prolog
+1435     55/push-EBP
+1436     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1437     # setup
+1438     # . clear-stream(_test-input-stream)
+1439     # . . push args
+1440     68/push  _test-input-stream/imm32
+1441     # . . call
+1442     e8/call  clear-stream/disp32
+1443     # . . discard args
+1444     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1445     # . clear-stream(_test-output-stream)
+1446     # . . push args
+1447     68/push  _test-output-stream/imm32
+1448     # . . call
+1449     e8/call  clear-stream/disp32
+1450     # . . discard args
+1451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1452     # . clear-stream(_test-output-buffered-file+4)
+1453     # . . push args
+1454     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1455     05/add-to-EAX  4/imm32
+1456     50/push-EAX
+1457     # . . call
+1458     e8/call  clear-stream/disp32
+1459     # . . discard args
+1460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1461     # initialize input
+1462     # . write(_test-input-stream, "abcd/imm32")
+1463     # . . push args
+1464     68/push  "abcd/imm32"/imm32
+1465     68/push  _test-input-stream/imm32
+1466     # . . call
+1467     e8/call  write/disp32
+1468     # . . discard args
+1469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1470     # convert-data(_test-input-stream, _test-output-buffered-file)
+1471     # . . push args
+1472     68/push  _test-output-buffered-file/imm32
+1473     68/push  _test-input-stream/imm32
 1474     # . . call
-1475     e8/call  clear-stream/disp32
+1475     e8/call  convert-data/disp32
 1476     # . . discard args
-1477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1478     # . clear-stream(_test-output-stream)
-1479     # . . push args
-1480     68/push  _test-output-stream/imm32
-1481     # . . call
-1482     e8/call  clear-stream/disp32
-1483     # . . discard args
-1484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1485     # . clear-stream(_test-output-buffered-file+4)
-1486     # . . push args
-1487     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1488     05/add-to-EAX  4/imm32
-1489     50/push-EAX
-1490     # . . call
-1491     e8/call  clear-stream/disp32
-1492     # . . discard args
-1493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1494     # initialize input
-1495     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
-1496     # . . push args
-1497     68/push  "30 abcd/o 42e1/imm32"/imm32
-1498     68/push  _test-input-stream/imm32
-1499     # . . call
-1500     e8/call  write/disp32
-1501     # . . discard args
-1502     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1503     # convert-data(_test-input-stream, _test-output-buffered-file)
-1504     # . . push args
-1505     68/push  _test-output-buffered-file/imm32
-1506     68/push  _test-input-stream/imm32
-1507     # . . call
-1508     e8/call  convert-data/disp32
-1509     # . . discard args
-1510     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1511     # check output
-1512     # . flush(_test-output-buffered-file)
-1513     # . . push args
-1514     68/push  _test-output-buffered-file/imm32
-1515     # . . call
-1516     e8/call  flush/disp32
-1517     # . . discard args
-1518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1519 +-- 34 lines: # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-1553     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 ", msg)
-1554     # . . push args
-1555     68/push  "F - test-convert-data-multiple-words"/imm32
-1556     68/push  "30 abcd/o e1 42 00 00 "/imm32
-1557     68/push  _test-output-stream/imm32
-1558     # . . call
-1559     e8/call  check-stream-equal/disp32
-1560     # . . discard args
-1561     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1562     # . epilog
-1563     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1564     5d/pop-to-EBP
-1565     c3/return
-1566 
-1567 # pack an instruction, following the C++ version
-1568 #
-1569 # zero error handling at the moment (continuing to rely on the C++ version for that):
-1570 #   missing fields are always 0-filled
-1571 #   bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte.
-1572 #   may pick up any of duplicate operands in an instruction
-1573 #   silently drop extraneous operands
-1574 #   unceremoniously abort on non-numeric operands except disp or imm
-1575 #   opcodes must be lowercase and zero padded
-1576 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
-1577 convert-instruction:  # line : (address stream byte), out : (address buffered-file) -> <void>
-1578     # pseudocode:
-1579     #   # some early exits
-1580     #   var word-slice = next-word(line)
-1581     #   if slice-empty?(word-slice)
-1582     #     write-stream-buffered(out, line)
-1583     #     return
-1584     #   if slice-starts-with?(word-slice, "#")
-1585     #     write-stream-buffered(out, line)
-1586     #     return
-1587     #   if slice-ends-with?(word-slice, ":")
-1588     #     write-stream-buffered(out, line)
-1589     #     return
-1590     #   # really convert
-1591     #   emit-opcodes(line, out)
-1592     #   emit-modrm(line, out)
-1593     #   emit-sib(line, out)
-1594     #   emit-disp(line, out)
-1595     #   emit-imm(line, out)
-1596     #   emit-line-in-comment(line, out)
-1597     #
-1598     # . prolog
-1599     55/push-EBP
-1600     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1601     # . save registers
-1602     50/push-EAX
-1603     51/push-ECX
-1604     52/push-EDX
-1605     # var word-slice/ECX = {0, 0}
-1606     68/push  0/imm32/end
-1607     68/push  0/imm32/start
-1608     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1609     # next-word(line, word-slice)
-1610     # . . push args
-1611     51/push-ECX
-1612     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1613     # . . call
-1614     e8/call  next-word/disp32
-1615     # . . discard args
-1616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1617 $convert-instruction:check0:
-1618     # if (slice-empty?(word-slice)) break
-1619     # . EAX = slice-empty?(word-slice)
-1620     # . . push args
-1621     51/push-ECX
-1622     # . . call
-1623     e8/call  slice-empty?/disp32
-1624     # . . discard args
-1625     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1626     # . if (EAX != 0) pass through
-1627     3d/compare-EAX  0/imm32
-1628     75/jump-if-not-equal  $convert-instruction:pass-through/disp8
-1629 $convert-instruction:check1:
-1630     # if (slice-starts-with?(word-slice, "#")) write-stream-buffered(out, line)
-1631     # . start/EDX = word-slice->start
-1632     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
-1633     # . c/EAX = *start
-1634     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-1635     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
-1636     # . if (EAX == '#') pass through
-1637     3d/compare-with-EAX  0x23/imm32/hash
-1638     74/jump-if-equal  $convert-instruction:pass-through/disp8
-1639 $convert-instruction:check2:
-1640     # if (slice-ends-with?(word-slice, ":")) write-stream-buffered(out, line)
-1641     # . end/EDX = word-slice->end
-1642     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
-1643     # . c/EAX = *(end-1)
-1644     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-1645     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
-1646     # . if (EAX == ':') pass through
-1647     3d/compare-with-EAX  0x3a/imm32/colon
-1648     74/jump-if-equal  $convert-instruction:pass-through/disp8
-1649 $convert-instruction:pass-through:
-1650     # write-stream-buffered(out, line)
+1477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1478     # check that the line just passed through
+1479     # . flush(_test-output-buffered-file)
+1480     # . . push args
+1481     68/push  _test-output-buffered-file/imm32
+1482     # . . call
+1483     e8/call  flush/disp32
+1484     # . . discard args
+1485     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1486     # . check-stream-equal(_test-output-stream, "abcd/imm32", msg)
+1487     # . . push args
+1488     68/push  "F - test-convert-data-passes-names-through"/imm32
+1489     68/push  "abcd/imm32 "/imm32
+1490     68/push  _test-output-stream/imm32
+1491     # . . call
+1492     e8/call  check-stream-equal/disp32
+1493     # . . discard args
+1494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1495     # . epilog
+1496     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1497     5d/pop-to-EBP
+1498     c3/return
+1499 
+1500 test-convert-data-handles-imm32:
+1501     # If a word has the /imm32 metadata, emit it in 4 bytes.
+1502     # . prolog
+1503     55/push-EBP
+1504     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1505     # setup
+1506     # . clear-stream(_test-input-stream)
+1507     # . . push args
+1508     68/push  _test-input-stream/imm32
+1509     # . . call
+1510     e8/call  clear-stream/disp32
+1511     # . . discard args
+1512     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1513     # . clear-stream(_test-output-stream)
+1514     # . . push args
+1515     68/push  _test-output-stream/imm32
+1516     # . . call
+1517     e8/call  clear-stream/disp32
+1518     # . . discard args
+1519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1520     # . clear-stream(_test-output-buffered-file+4)
+1521     # . . push args
+1522     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1523     05/add-to-EAX  4/imm32
+1524     50/push-EAX
+1525     # . . call
+1526     e8/call  clear-stream/disp32
+1527     # . . discard args
+1528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1529     # initialize input
+1530     # . write(_test-input-stream, "30/imm32")
+1531     # . . push args
+1532     68/push  "30/imm32"/imm32
+1533     68/push  _test-input-stream/imm32
+1534     # . . call
+1535     e8/call  write/disp32
+1536     # . . discard args
+1537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1538     # convert-data(_test-input-stream, _test-output-buffered-file)
+1539     # . . push args
+1540     68/push  _test-output-buffered-file/imm32
+1541     68/push  _test-input-stream/imm32
+1542     # . . call
+1543     e8/call  convert-data/disp32
+1544     # . . discard args
+1545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1546     # check that 4 bytes were written
+1547     # . flush(_test-output-buffered-file)
+1548     # . . push args
+1549     68/push  _test-output-buffered-file/imm32
+1550     # . . call
+1551     e8/call  flush/disp32
+1552     # . . discard args
+1553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1554     # . check-stream-equal(_test-output-stream, "30 00 00 00 ", msg)
+1555     # . . push args
+1556     68/push  "F - test-convert-data-handles-imm32"/imm32
+1557     68/push  "30 00 00 00 "/imm32
+1558     68/push  _test-output-stream/imm32
+1559     # . . call
+1560     e8/call  check-stream-equal/disp32
+1561     # . . discard args
+1562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1563     # . epilog
+1564     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1565     5d/pop-to-EBP
+1566     c3/return
+1567 
+1568 test-convert-data-handles-single-byte:
+1569     # Any metadata but /imm32 will emit a single byte.
+1570     # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
+1571     # . prolog
+1572     55/push-EBP
+1573     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1574     # setup
+1575     # . clear-stream(_test-input-stream)
+1576     # . . push args
+1577     68/push  _test-input-stream/imm32
+1578     # . . call
+1579     e8/call  clear-stream/disp32
+1580     # . . discard args
+1581     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1582     # . clear-stream(_test-output-stream)
+1583     # . . push args
+1584     68/push  _test-output-stream/imm32
+1585     # . . call
+1586     e8/call  clear-stream/disp32
+1587     # . . discard args
+1588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1589     # . clear-stream(_test-output-buffered-file+4)
+1590     # . . push args
+1591     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1592     05/add-to-EAX  4/imm32
+1593     50/push-EAX
+1594     # . . call
+1595     e8/call  clear-stream/disp32
+1596     # . . discard args
+1597     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1598     # initialize input
+1599     # . write(_test-input-stream, "30/imm16")
+1600     # . . push args
+1601     68/push  "30/imm16"/imm32
+1602     68/push  _test-input-stream/imm32
+1603     # . . call
+1604     e8/call  write/disp32
+1605     # . . discard args
+1606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1607     # convert-data(_test-input-stream, _test-output-buffered-file)
+1608     # . . push args
+1609     68/push  _test-output-buffered-file/imm32
+1610     68/push  _test-input-stream/imm32
+1611     # . . call
+1612     e8/call  convert-data/disp32
+1613     # . . discard args
+1614     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1615     # check that a single byte was written (imm16 is not a valid operand type)
+1616     # . flush(_test-output-buffered-file)
+1617     # . . push args
+1618     68/push  _test-output-buffered-file/imm32
+1619     # . . call
+1620     e8/call  flush/disp32
+1621     # . . discard args
+1622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1623     # . check-stream-equal(_test-output-stream, "30 ", msg)
+1624     # . . push args
+1625     68/push  "F - test-convert-data-handles-single-byte"/imm32
+1626     68/push  "30 "/imm32
+1627     68/push  _test-output-stream/imm32
+1628     # . . call
+1629     e8/call  check-stream-equal/disp32
+1630     # . . discard args
+1631     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1632     # . epilog
+1633     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1634     5d/pop-to-EBP
+1635     c3/return
+1636 
+1637 test-convert-data-multiple-bytes:
+1638     # Multiple single-byte words in input stream get processed one by one.
+1639     # . prolog
+1640     55/push-EBP
+1641     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1642     # setup
+1643     # . clear-stream(_test-input-stream)
+1644     # . . push args
+1645     68/push  _test-input-stream/imm32
+1646     # . . call
+1647     e8/call  clear-stream/disp32
+1648     # . . discard args
+1649     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1650     # . clear-stream(_test-output-stream)
 1651     # . . push args
-1652     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1653     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-1654     # . . call
-1655     e8/call  write-stream-buffered/disp32
-1656     # . . discard args
-1657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1658     # return
-1659     eb/jump  $convert-instruction:end/disp8
-1660 $convert-instruction:really-convert:
-1661     # emit-opcodes(line, out)
-1662     # . . push args
-1663     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
-1664     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1665     # . . call
-1666     e8/call  emit-opcodes/disp32
-1667     # . . discard args
-1668     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1669     # emit-modrm(line, out)
-1670     # . . push args
-1671     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
-1672     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1673     # . . call
-1674     e8/call  emit-modrm/disp32
-1675     # . . discard args
-1676     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1677     # emit-sib(line, out)
-1678     # . . push args
-1679     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
-1680     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1681     # . . call
-1682     e8/call  emit-sib/disp32
-1683     # . . discard args
-1684     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1685     # emit-disp(line, out)
-1686     # . . push args
-1687     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
-1688     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1689     # . . call
-1690     e8/call  emit-disp/disp32
-1691     # . . discard args
-1692     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1693     # emit-imm(line, out)
-1694     # . . push args
-1695     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
-1696     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1697     # . . call
-1698     e8/call  emit-imm/disp32
-1699     # . . discard args
-1700     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1701     # emit-line-in-comment(line, out)
-1702     # . . push args
-1703     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
-1704     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1705     # . . call
-1706     e8/call  emit-line-in-comment/disp32
-1707     # . . discard args
-1708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1709 $convert-instruction:end:
-1710     # . restore registers
-1711     5a/pop-to-EDX
-1712     59/pop-to-ECX
-1713     58/pop-to-EAX
-1714     # . epilog
-1715     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1716     5d/pop-to-EBP
-1717     c3/return
-1718 
-1719 emit-opcodes:  # line : (address stream byte), out : (address buffered-file) -> <void>
-1720     # pseudocode:
-1721     #   rewind-stream(line)
-1722     #   var op1 = word-slice
-1723     #   write-slice(out, op1)
-1724     #   if slice-equal?(op1, "0f") or slice-equal?(op1, "f2") or slice-equal?(op1, "f3")
-1725     #     var op2 = next-word(line)
-1726     #     if slice-empty?(op2)
-1727     #       return
-1728     #     if slice-starts-with?(op2, "#")
-1729     #       return
-1730     #     write-slice(out, op2)
-1731     #     if slice-equal?(op1, "f2") or slice-equal?(op1, "f3")
-1732     #       if slice-equal?(op2, "0f")
-1733     #         var op3 = next-word(line)
-1734     #         if slice-empty?(op3)
-1735     #           return
-1736     #         if slice-starts-with?(op2, "#")
-1737     #           return
-1738     #         write-slice(out, op3)
-1739     #
-1740     # . prolog
-1741     55/push-EBP
-1742     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1743     # . save registers
-1744 $emit-opcodes:end:
-1745     # . restore registers
-1746     # . epilog
-1747     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1748     5d/pop-to-EBP
-1749     c3/return
-1750 
-1751 emit-modrm:  # line : (address stream byte), out : (address buffered-file) -> <void>
-1752     # pseudocode:
-1753     #   rewind-stream(line)
-1754     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
-1755     #   while true
-1756     #     word-slice = next-word(line)
-1757     #     if (empty(word-slice)) break
-1758     #     if (slice-starts-with?(word-slice, "#")) break
-1759     #     if (has-metadata?(word-slice, "mod"))
-1760     #       var mod = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1761     #       has-modrm? = true
-1762     #     else if (has-metadata?(word-slice, "rm32"))
-1763     #       var rm32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1764     #       has-modrm? = true
-1765     #     else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop"))
-1766     #       var r32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1767     #       has-modrm? = true
-1768     #   if has-modrm?
-1769     #     var modrm = mod & 0b11
-1770     #     modrm <<= 2
-1771     #     modrm |= r32 & 0b111
-1772     #     modrm <<= 3
-1773     #     modrm |= rm32 & 0b111
-1774     #     emit-hex(out, modrm, 1)
-1775     #
-1776     # . prolog
-1777     55/push-EBP
-1778     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1779     # . save registers
-1780 $emit-modrm:end:
-1781     # . restore registers
-1782     # . epilog
-1783     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1784     5d/pop-to-EBP
-1785     c3/return
-1786 
-1787 emit-sib:  # line : (address stream byte), out : (address buffered-file) -> <void>
-1788     # pseudocode:
-1789     #   var has-sib? = false, base = 0, index = 0, scale = 0
-1790     #   while true
-1791     #     word-slice = next-word(line)
-1792     #     if (empty(word-slice)) break
-1793     #     if (slice-starts-with?(word-slice, "#")) break
-1794     #     if (has-metadata?(word-slice, "base")
-1795     #       var base = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1796     #       has-sib? = true
-1797     #     else if (has-metadata?(word-slice, "index")
-1798     #       var index = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1799     #       has-sib? = true
-1800     #     else if (has-metadata?(word-slice, "scale")
-1801     #       var scale = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1802     #       has-sib? = true
-1803     #   if has-sib?
-1804     #     var sib = scale & 0b11
-1805     #     sib <<= 2
-1806     #     sib |= index & 0b111
-1807     #     sib <<= 3
-1808     #     sib |= base & 0b111
-1809     #     emit-hex(out, sib, 1)
-1810     #
-1811     # . prolog
-1812     55/push-EBP
-1813     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1814     # . save registers
-1815 $emit-sib:end:
-1816     # . restore registers
-1817     # . epilog
-1818     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1819     5d/pop-to-EBP
-1820     c3/return
-1821 
-1822 emit-disp:  # line : (address stream byte), out : (address buffered-file) -> <void>
-1823     # pseudocode:
-1824     #   rewind-stream(line)
-1825     #   while true
-1826     #     word-slice = next-word(line)
-1827     #     if (empty(word-slice)) break
-1828     #     if (slice-starts-with?(word-slice, "#")) break
-1829     #     if has-metadata?(word-slice, "disp8")
-1830     #       var disp = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1831     #       emit-hex(out, disp, 1)
-1832     #       break
-1833     #     else if has-metadata?(word-slice, "disp16")
-1834     #       var disp = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1835     #       emit-hex(out, disp, 2)
-1836     #       break
-1837     #     else if has-metadata?(word-slice, "disp32")
-1838     #       var disp = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1839     #       emit-hex(out, disp, 4)
-1840     #       break
-1841     #
-1842     # . prolog
-1843     55/push-EBP
-1844     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1845     # . save registers
-1846 $emit-disp:end:
-1847     # . restore registers
-1848     # . epilog
-1849     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1850     5d/pop-to-EBP
-1851     c3/return
-1852 
-1853 emit-imm:  # line : (address stream byte), out : (address buffered-file) -> <void>
-1854     # pseudocode:
-1855     #   rewind-stream(line)
-1856     #   while true
-1857     #     word-slice = next-word(line)
-1858     #     if (slice-starts-with?(word-slice, "#")) break
-1859     #     if (empty(word-slice)) break
-1860     #     if has-metadata?(word-slice, "imm8")
-1861     #       var imm = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1862     #       emit-hex(out, imm, 1)
-1863     #       break
-1864     #     if has-metadata?(word-slice, "imm16")
-1865     #       var imm = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1866     #       emit-hex(out, imm, 2)
-1867     #       break
-1868     #     else if has-metadata?(word-slice, "imm32")
-1869     #       var imm = parse-hex-int(next-token-from-slice(word-slice, "/"))
-1870     #       emit-hex(out, imm, 4)
-1871     #       break
-1872     #
-1873     # . prolog
-1874     55/push-EBP
-1875     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1876     # . save registers
-1877 $emit-imm:end:
-1878     # . restore registers
-1879     # . epilog
-1880     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1881     5d/pop-to-EBP
-1882     c3/return
-1883 
-1884 emit-line-in-comment:  # line : (address stream byte), out : (address buffered-file) -> <void>
-1885     # pseudocode:
-1886     #   write-buffered(out, "  # ")
-1887     #   write-stream-buffered(out, line)
-1888     #
-1889     # . prolog
-1890     55/push-EBP
-1891     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1892     # . save registers
-1893 $emit-line-in-comment:end:
-1894     # . restore registers
-1895     # . epilog
-1896     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1897     5d/pop-to-EBP
-1898     c3/return
-1899 
-1900 test-convert-instruction-passes-comments-through:
-1901     # if a line starts with '#', pass it along unchanged
-1902     # . prolog
-1903     55/push-EBP
-1904     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1905     # setup
-1906     # . clear-stream(_test-input-stream)
-1907     # . . push args
-1908     68/push  _test-input-stream/imm32
+1652     68/push  _test-output-stream/imm32
+1653     # . . call
+1654     e8/call  clear-stream/disp32
+1655     # . . discard args
+1656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1657     # . clear-stream(_test-output-buffered-file+4)
+1658     # . . push args
+1659     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1660     05/add-to-EAX  4/imm32
+1661     50/push-EAX
+1662     # . . call
+1663     e8/call  clear-stream/disp32
+1664     # . . discard args
+1665     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1666     # initialize input
+1667     # . write(_test-input-stream, "1 2")
+1668     # . . push args
+1669     68/push  "1 2"/imm32
+1670     68/push  _test-input-stream/imm32
+1671     # . . call
+1672     e8/call  write/disp32
+1673     # . . discard args
+1674     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1675     # convert-data(_test-input-stream, _test-output-buffered-file)
+1676     # . . push args
+1677     68/push  _test-output-buffered-file/imm32
+1678     68/push  _test-input-stream/imm32
+1679     # . . call
+1680     e8/call  convert-data/disp32
+1681     # . . discard args
+1682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1683     # check output
+1684     # . flush(_test-output-buffered-file)
+1685     # . . push args
+1686     68/push  _test-output-buffered-file/imm32
+1687     # . . call
+1688     e8/call  flush/disp32
+1689     # . . discard args
+1690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1691     # . check-stream-equal(_test-output-stream, "01 02 ", msg)
+1692     # . . push args
+1693     68/push  "F - test-convert-data-multiple-bytes"/imm32
+1694     68/push  "01 02 "/imm32
+1695     68/push  _test-output-stream/imm32
+1696     # . . call
+1697     e8/call  check-stream-equal/disp32
+1698     # . . discard args
+1699     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1700     # . epilog
+1701     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1702     5d/pop-to-EBP
+1703     c3/return
+1704 
+1705 test-convert-data-byte-then-name:
+1706     # Single-byte word followed by valid name get processed one by one.
+1707     # . prolog
+1708     55/push-EBP
+1709     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1710     # setup
+1711     # . clear-stream(_test-input-stream)
+1712     # . . push args
+1713     68/push  _test-input-stream/imm32
+1714     # . . call
+1715     e8/call  clear-stream/disp32
+1716     # . . discard args
+1717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1718     # . clear-stream(_test-output-stream)
+1719     # . . push args
+1720     68/push  _test-output-stream/imm32
+1721     # . . call
+1722     e8/call  clear-stream/disp32
+1723     # . . discard args
+1724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1725     # . clear-stream(_test-output-buffered-file+4)
+1726     # . . push args
+1727     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1728     05/add-to-EAX  4/imm32
+1729     50/push-EAX
+1730     # . . call
+1731     e8/call  clear-stream/disp32
+1732     # . . discard args
+1733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1734     # initialize input
+1735     # . write(_test-input-stream, "30 abcd/o")
+1736     # . . push args
+1737     68/push  "30 abcd/o"/imm32
+1738     68/push  _test-input-stream/imm32
+1739     # . . call
+1740     e8/call  write/disp32
+1741     # . . discard args
+1742     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1743     # convert-data(_test-input-stream, _test-output-buffered-file)
+1744     # . . push args
+1745     68/push  _test-output-buffered-file/imm32
+1746     68/push  _test-input-stream/imm32
+1747     # . . call
+1748     e8/call  convert-data/disp32
+1749     # . . discard args
+1750     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1751     # check output
+1752     # . flush(_test-output-buffered-file)
+1753     # . . push args
+1754     68/push  _test-output-buffered-file/imm32
+1755     # . . call
+1756     e8/call  flush/disp32
+1757     # . . discard args
+1758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1759     # . check-stream-equal(_test-output-stream, "30 abcd/o ", msg)
+1760     # . . push args
+1761     68/push  "F - test-convert-data-byte-then-name"/imm32
+1762     68/push  "30 abcd/o "/imm32
+1763     68/push  _test-output-stream/imm32
+1764     # . . call
+1765     e8/call  check-stream-equal/disp32
+1766     # . . discard args
+1767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1768     # . epilog
+1769     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1770     5d/pop-to-EBP
+1771     c3/return
+1772 
+1773 test-convert-data-multiple-words:
+1774     # Multiple words in input stream get processed one by one.
+1775     # . prolog
+1776     55/push-EBP
+1777     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1778     # setup
+1779     # . clear-stream(_test-input-stream)
+1780     # . . push args
+1781     68/push  _test-input-stream/imm32
+1782     # . . call
+1783     e8/call  clear-stream/disp32
+1784     # . . discard args
+1785     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1786     # . clear-stream(_test-output-stream)
+1787     # . . push args
+1788     68/push  _test-output-stream/imm32
+1789     # . . call
+1790     e8/call  clear-stream/disp32
+1791     # . . discard args
+1792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1793     # . clear-stream(_test-output-buffered-file+4)
+1794     # . . push args
+1795     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1796     05/add-to-EAX  4/imm32
+1797     50/push-EAX
+1798     # . . call
+1799     e8/call  clear-stream/disp32
+1800     # . . discard args
+1801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1802     # initialize input
+1803     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
+1804     # . . push args
+1805     68/push  "30 abcd/o 42e1/imm32"/imm32
+1806     68/push  _test-input-stream/imm32
+1807     # . . call
+1808     e8/call  write/disp32
+1809     # . . discard args
+1810     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1811     # convert-data(_test-input-stream, _test-output-buffered-file)
+1812     # . . push args
+1813     68/push  _test-output-buffered-file/imm32
+1814     68/push  _test-input-stream/imm32
+1815     # . . call
+1816     e8/call  convert-data/disp32
+1817     # . . discard args
+1818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1819     # check output
+1820     # . flush(_test-output-buffered-file)
+1821     # . . push args
+1822     68/push  _test-output-buffered-file/imm32
+1823     # . . call
+1824     e8/call  flush/disp32
+1825     # . . discard args
+1826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1827 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+1861     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 ", msg)
+1862     # . . push args
+1863     68/push  "F - test-convert-data-multiple-words"/imm32
+1864     68/push  "30 abcd/o e1 42 00 00 "/imm32
+1865     68/push  _test-output-stream/imm32
+1866     # . . call
+1867     e8/call  check-stream-equal/disp32
+1868     # . . discard args
+1869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1870     # . epilog
+1871     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1872     5d/pop-to-EBP
+1873     c3/return
+1874 
+1875 test-convert-data-trailing-comment:
+1876     # Trailing comments in data segment get appropriately ignored.
+1877     # . prolog
+1878     55/push-EBP
+1879     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1880     # setup
+1881     # . clear-stream(_test-input-stream)
+1882     # . . push args
+1883     68/push  _test-input-stream/imm32
+1884     # . . call
+1885     e8/call  clear-stream/disp32
+1886     # . . discard args
+1887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1888     # . clear-stream(_test-output-stream)
+1889     # . . push args
+1890     68/push  _test-output-stream/imm32
+1891     # . . call
+1892     e8/call  clear-stream/disp32
+1893     # . . discard args
+1894     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1895     # . clear-stream(_test-output-buffered-file+4)
+1896     # . . push args
+1897     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1898     05/add-to-EAX  4/imm32
+1899     50/push-EAX
+1900     # . . call
+1901     e8/call  clear-stream/disp32
+1902     # . . discard args
+1903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1904     # initialize input
+1905     # . write(_test-input-stream, "30/imm32 # comment")
+1906     # . . push args
+1907     68/push  "30/imm32 # comment"/imm32
+1908     68/push  _test-input-stream/imm32
 1909     # . . call
-1910     e8/call  clear-stream/disp32
+1910     e8/call  write/disp32
 1911     # . . discard args
-1912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1913     # . clear-stream(_test-output-stream)
+1912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1913     # convert-data(_test-input-stream, _test-output-buffered-file)
 1914     # . . push args
-1915     68/push  _test-output-stream/imm32
-1916     # . . call
-1917     e8/call  clear-stream/disp32
-1918     # . . discard args
-1919     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1920     # . clear-stream(_test-output-buffered-file+4)
-1921     # . . push args
-1922     b8/copy-to-EAX  _test-buffered-file/imm32
-1923     05/add-to-EAX  4/imm32
-1924     50/push-EAX
+1915     68/push  _test-output-buffered-file/imm32
+1916     68/push  _test-input-stream/imm32
+1917     # . . call
+1918     e8/call  convert-data/disp32
+1919     # . . discard args
+1920     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1921     # check output
+1922     # . flush(_test-output-buffered-file)
+1923     # . . push args
+1924     68/push  _test-output-buffered-file/imm32
 1925     # . . call
-1926     e8/call  clear-stream/disp32
+1926     e8/call  flush/disp32
 1927     # . . discard args
 1928     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1929     # initialize input
-1930     # . write(_test-input-stream, "# abcd")
-1931     # . . push args
-1932     68/push  "# abcd"/imm32
-1933     68/push  _test-input-stream/imm32
-1934     # . . call
-1935     e8/call  write/disp32
-1936     # . . discard args
-1937     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1938     # convert-instruction(_test-input-stream, _test-output-buffered-file)
-1939     # . . push args
-1940     68/push  _test-output-buffered-file/imm32
-1941     68/push  _test-input-stream/imm32
-1942     # . . call
-1943     e8/call  convert-instruction/disp32
-1944     # . . discard args
-1945     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1946     # check that the line just passed through
-1947     # . flush(_test-output-buffered-file)
-1948     # . . push args
-1949     68/push  _test-output-buffered-file/imm32
-1950     # . . call
-1951     e8/call  flush/disp32
-1952     # . . discard args
-1953     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1954     # . check-stream-equal(_test-output-stream, "# abcd", msg)
-1955     # . . push args
-1956     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
-1957     68/push  "# abcd"/imm32
-1958     68/push  _test-output-stream/imm32
-1959     # . . call
-1960     e8/call  check-stream-equal/disp32
-1961     # . . discard args
-1962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1963     # . epilog
-1964     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1965     5d/pop-to-EBP
-1966     c3/return
-1967 
-1968 test-convert-instruction-passes-labels-through:
-1969     # if the first word ends with ':', pass along the entire line unchanged
-1970     # . prolog
-1971     55/push-EBP
-1972     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1973     # setup
-1974     # . clear-stream(_test-input-stream)
-1975     # . . push args
-1976     68/push  _test-input-stream/imm32
-1977     # . . call
-1978     e8/call  clear-stream/disp32
-1979     # . . discard args
-1980     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1981     # . clear-stream(_test-output-stream)
-1982     # . . push args
-1983     68/push  _test-output-stream/imm32
-1984     # . . call
-1985     e8/call  clear-stream/disp32
-1986     # . . discard args
-1987     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1988     # . clear-stream(_test-output-buffered-file+4)
-1989     # . . push args
-1990     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1991     05/add-to-EAX  4/imm32
-1992     50/push-EAX
-1993     # . . call
-1994     e8/call  clear-stream/disp32
-1995     # . . discard args
-1996     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1997     # initialize input
-1998     # . write(_test-input-stream, "ab: # cd")
-1999     # . . push args
-2000     68/push  "ab: # cd"/imm32
-2001     68/push  _test-input-stream/imm32
-2002     # . . call
-2003     e8/call  write/disp32
-2004     # . . discard args
-2005     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2006     # convert-instruction(_test-input-stream, _test-output-buffered-file)
-2007     # . . push args
-2008     68/push  _test-output-buffered-file/imm32
-2009     68/push  _test-input-stream/imm32
-2010     # . . call
-2011     e8/call  convert-instruction/disp32
-2012     # . . discard args
-2013     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2014     # check that the line just passed through
-2015     # . flush(_test-output-buffered-file)
-2016     # . . push args
-2017     68/push  _test-output-buffered-file/imm32
-2018     # . . call
-2019     e8/call  flush/disp32
-2020     # . . discard args
-2021     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2022     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
-2023     # . . push args
-2024     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
-2025     68/push  "ab: # cd"/imm32
-2026     68/push  _test-output-stream/imm32
-2027     # . . call
-2028     e8/call  check-stream-equal/disp32
-2029     # . . discard args
-2030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2031     # . epilog
-2032     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2033     5d/pop-to-EBP
-2034     c3/return
-2035 
-2036 # (re)compute the bounds of the next word in the line
-2037 # return empty string on reaching end of file
-2038 next-word:  # line : (address stream byte), out : (address slice)
-2039     # . prolog
-2040     55/push-EBP
-2041     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2042     # . save registers
-2043     50/push-EAX
-2044     51/push-ECX
-2045     56/push-ESI
-2046     57/push-EDI
-2047     # ESI = line
-2048     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-2049     # EDI = out
-2050     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
-2051     # skip-chars-matching(line, ' ')
-2052     # . . push args
-2053     68/push  0x20/imm32/space
-2054     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2055     # . . call
-2056     e8/call  skip-chars-matching/disp32
-2057     # . . discard args
-2058     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2059 $next-word:check0:
-2060     # if (line->read >= line->write) clear out and return
-2061     # . EAX = line->read
-2062     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
-2063     # . if (EAX < line->write) goto next check
-2064     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
-2065     7c/jump-if-lesser  $next-word:check1/disp8
-2066     # . return out = {0, 0}
-2067     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
-2068     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
-2069     eb/jump  $next-word:end/disp8
-2070 $next-word:check1:
-2071     # out->start = &line->data[line->read]
-2072     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-2073     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
-2074     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
-2075     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
-2076     # . EAX = line->data[line->read]
-2077     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-2078     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
-2079     # . compare
-2080     3d/compare-EAX-with  0x23/imm32/pound
-2081     75/jump-if-not-equal  $next-word:not-comment/disp8
-2082     # . out->end = &line->data[line->write]
-2083     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-2084     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
-2085     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-2086     # . line->read = line->write
-2087     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
-2088     # . return
-2089     eb/jump  $next-word:end/disp8
-2090 $next-word:not-comment:
-2091     # otherwise skip-chars-not-matching-whitespace(line)
-2092     # . . push args
-2093     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2094     # . . call
-2095     e8/call  skip-chars-not-matching-whitespace/disp32
-2096     # . . discard args
-2097     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2098     # out->end = &line->data[line->read]
-2099     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-2100     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
-2101     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-2102 $next-word:end:
-2103     # . restore registers
-2104     5f/pop-to-EDI
-2105     5e/pop-to-ESI
-2106     59/pop-to-ECX
-2107     58/pop-to-EAX
-2108     # . epilog
-2109     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2110     5d/pop-to-EBP
-2111     c3/return
-2112 
-2113 test-next-word:
-2114     # . prolog
-2115     55/push-EBP
-2116     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2117     # setup
-2118     # . clear-stream(_test-stream)
-2119     # . . push args
-2120     68/push  _test-stream/imm32
-2121     # . . call
-2122     e8/call  clear-stream/disp32
-2123     # . . discard args
-2124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2125     # var slice/ECX = {0, 0}
-2126     68/push  0/imm32/end
-2127     68/push  0/imm32/start
-2128     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2129     # write(_test-stream, "  ab")
-2130     # . . push args
-2131     68/push  "  ab"/imm32
-2132     68/push  _test-stream/imm32
-2133     # . . call
-2134     e8/call  write/disp32
-2135     # . . discard args
-2136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2137     # next-word(_test-stream, slice)
-2138     # . . push args
-2139     51/push-ECX
-2140     68/push  _test-stream/imm32
-2141     # . . call
-2142     e8/call  next-word/disp32
-2143     # . . discard args
-2144     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2145     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
-2146     # . check-ints-equal(slice->start - _test-stream, 14, msg)
-2147     # . . push args
-2148     68/push  "F - test-next-word: start"/imm32
-2149     68/push  0xe/imm32
-2150     # . . push slice->start - _test-stream
-2151     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-2152     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-2153     50/push-EAX
-2154     # . . call
-2155     e8/call  check-ints-equal/disp32
-2156     # . . discard args
-2157     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2158     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
-2159     # . check-ints-equal(slice->end - _test-stream, 16, msg)
-2160     # . . push args
-2161     68/push  "F - test-next-word: end"/imm32
-2162     68/push  0x10/imm32
-2163     # . . push slice->end - _test-stream
-2164     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-2165     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-2166     50/push-EAX
-2167     # . . call
-2168     e8/call  check-ints-equal/disp32
-2169     # . . discard args
-2170     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2171     # . epilog
-2172     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2173     5d/pop-to-EBP
-2174     c3/return
-2175 
-2176 test-next-word-returns-whole-comment:
-2177     # . prolog
-2178     55/push-EBP
-2179     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2180     # setup
-2181     # . clear-stream(_test-stream)
-2182     # . . push args
-2183     68/push  _test-stream/imm32
-2184     # . . call
-2185     e8/call  clear-stream/disp32
-2186     # . . discard args
-2187     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2188     # var slice/ECX = {0, 0}
-2189     68/push  0/imm32/end
-2190     68/push  0/imm32/start
-2191     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2192     # write(_test-stream, "  # a")
-2193     # . . push args
-2194     68/push  "  # a"/imm32
-2195     68/push  _test-stream/imm32
-2196     # . . call
-2197     e8/call  write/disp32
-2198     # . . discard args
-2199     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2200     # next-word(_test-stream, slice)
-2201     # . . push args
-2202     51/push-ECX
-2203     68/push  _test-stream/imm32
-2204     # . . call
-2205     e8/call  next-word/disp32
-2206     # . . discard args
-2207     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2208     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
-2209     # . check-ints-equal(slice->start - _test-stream, 14, msg)
-2210     # . . push args
-2211     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
-2212     68/push  0xe/imm32
-2213     # . . push slice->start - _test-stream
-2214     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-2215     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-2216     50/push-EAX
-2217     # . . call
-2218     e8/call  check-ints-equal/disp32
-2219     # . . discard args
-2220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2221     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
-2222     # . check-ints-equal(slice->end - _test-stream, 17, msg)
-2223     # . . push args
-2224     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
-2225     68/push  0x11/imm32
-2226     # . . push slice->end - _test-stream
-2227     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-2228     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-2229     50/push-EAX
+1929 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+1963     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
+1964     # . . push args
+1965     68/push  "F - test-convert-data-trailing-comment"/imm32
+1966     68/push  "30 00 00 00 # comment"/imm32
+1967     68/push  _test-output-stream/imm32
+1968     # . . call
+1969     e8/call  check-stream-equal/disp32
+1970     # . . discard args
+1971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1972     # . epilog
+1973     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1974     5d/pop-to-EBP
+1975     c3/return
+1976 
+1977 # pack an instruction, following the C++ version
+1978 #
+1979 # zero error handling at the moment (continuing to rely on the C++ version for that):
+1980 #   missing fields are always 0-filled
+1981 #   bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte.
+1982 #   may pick up any of duplicate operands in an instruction
+1983 #   silently drop extraneous operands
+1984 #   unceremoniously abort on non-numeric operands except disp or imm
+1985 #   opcodes must be lowercase and zero padded
+1986 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
+1987 convert-instruction:  # line : (address stream byte), out : (address buffered-file) -> <void>
+1988     # pseudocode:
+1989     #   # some early exits
+1990     #   var word-slice = next-word(line)
+1991     #   if slice-empty?(word-slice)
+1992     #     write-stream-data(out, line)
+1993     #     return
+1994     #   if slice-starts-with?(word-slice, "#")
+1995     #     write-stream-data(out, line)
+1996     #     return
+1997     #   if slice-ends-with?(word-slice, ":")
+1998     #     write-stream-data(out, line)
+1999     #     return
+2000     #   # really convert
+2001     #   emit-opcodes(line, out)
+2002     #   emit-modrm(line, out)
+2003     #   emit-sib(line, out)
+2004     #   emit-disp(line, out)
+2005     #   emit-imm(line, out)
+2006     #   emit-line-in-comment(line, out)
+2007     #
+2008     # . prolog
+2009     55/push-EBP
+2010     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2011     # . save registers
+2012     50/push-EAX
+2013     51/push-ECX
+2014     52/push-EDX
+2015     # var word-slice/ECX = {0, 0}
+2016     68/push  0/imm32/end
+2017     68/push  0/imm32/start
+2018     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2019     # next-word(line, word-slice)
+2020     # . . push args
+2021     51/push-ECX
+2022     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2023     # . . call
+2024     e8/call  next-word/disp32
+2025     # . . discard args
+2026     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2027 $convert-instruction:check0:
+2028     # if (slice-empty?(word-slice)) break
+2029     # . EAX = slice-empty?(word-slice)
+2030     # . . push args
+2031     51/push-ECX
+2032     # . . call
+2033     e8/call  slice-empty?/disp32
+2034     # . . discard args
+2035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2036     # . if (EAX != 0) pass through
+2037     3d/compare-EAX-and  0/imm32
+2038     75/jump-if-not-equal  $convert-instruction:pass-through/disp8
+2039 $convert-instruction:check1:
+2040     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
+2041     # . start/EDX = word-slice->start
+2042     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+2043     # . c/EAX = *start
+2044     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2045     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
+2046     # . if (EAX == '#') pass through
+2047     3d/compare-EAX-and  0x23/imm32/hash
+2048     74/jump-if-equal  $convert-instruction:pass-through/disp8
+2049 $convert-instruction:check2:
+2050     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
+2051     # . end/EDX = word-slice->end
+2052     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
+2053     # . c/EAX = *(end-1)
+2054     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2055     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
+2056     # . if (EAX == ':') pass through
+2057     3d/compare-EAX-and  0x3a/imm32/colon
+2058     75/jump-if-not-equal  $convert-instruction:really-convert/disp8
+2059 $convert-instruction:pass-through:
+2060     # write-stream-data(out, line)
+2061     # . . push args
+2062     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2063     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2064     # . . call
+2065     e8/call  write-stream-data/disp32
+2066     # . . discard args
+2067     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2068     # return
+2069     eb/jump  $convert-instruction:end/disp8
+2070 $convert-instruction:really-convert:
+2071     # emit-opcodes(line, out)
+2072     # . . push args
+2073     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
+2074     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2075     # . . call
+2076     e8/call  emit-opcodes/disp32
+2077     # . . discard args
+2078     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2079     # emit-modrm(line, out)
+2080     # . . push args
+2081     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
+2082     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2083     # . . call
+2084     e8/call  emit-modrm/disp32
+2085     # . . discard args
+2086     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2087     # emit-sib(line, out)
+2088     # . . push args
+2089     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
+2090     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2091     # . . call
+2092     e8/call  emit-sib/disp32
+2093     # . . discard args
+2094     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2095     # emit-disp(line, out)
+2096     # . . push args
+2097     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
+2098     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2099     # . . call
+2100     e8/call  emit-disp/disp32
+2101     # . . discard args
+2102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2103     # emit-imm(line, out)
+2104     # . . push args
+2105     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
+2106     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2107     # . . call
+2108     e8/call  emit-imm/disp32
+2109     # . . discard args
+2110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2111     # emit-line-in-comment(line, out)
+2112     # . . push args
+2113     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
+2114     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2115     # . . call
+2116     e8/call  emit-line-in-comment/disp32
+2117     # . . discard args
+2118     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2119 $convert-instruction:end:
+2120     # . restore locals
+2121     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2122     # . restore registers
+2123     5a/pop-to-EDX
+2124     59/pop-to-ECX
+2125     58/pop-to-EAX
+2126     # . epilog
+2127     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2128     5d/pop-to-EBP
+2129     c3/return
+2130 
+2131 emit-opcodes:  # line : (address stream byte), out : (address buffered-file) -> <void>
+2132     # opcodes occupy 1-3 bytes:
+2133     #   xx
+2134     #   0f xx
+2135     #   f2 xx
+2136     #   f3 xx
+2137     #   f2 0f xx
+2138     #   f3 0f xx
+2139     #
+2140     # pseudocode:
+2141     #   rewind-stream(line)
+2142     #
+2143     #   var op1 = next-word(line)
+2144     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
+2145     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
+2146     #   write-slice(out, op1)
+2147     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3")
+2148     #     return
+2149     #
+2150     #   var op2 = next-word(line)
+2151     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
+2152     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
+2153     #   write-slice(out, op2)
+2154     #   if slice-equal?(op1, "0f")
+2155     #     return
+2156     #   if !slice-equal?(op2, "0f")
+2157     #     return
+2158     #
+2159     #   var op3 = next-word(line)
+2160     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
+2161     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
+2162     #   write-slice(out, op3)
+2163     #
+2164     # . prolog
+2165     55/push-EBP
+2166     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2167     # . save registers
+2168     50/push-EAX
+2169     51/push-ECX
+2170     52/push-EDX
+2171     53/push-EBX
+2172     # var op1/ECX = {0, 0}
+2173     68/push  0/imm32/end
+2174     68/push  0/imm32/start
+2175     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2176     # var op2/EDX = {0, 0}
+2177     68/push  0/imm32/end
+2178     68/push  0/imm32/start
+2179     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+2180     # rewind-stream(line)
+2181     # . . push args
+2182     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2183     # . . call
+2184     e8/call  rewind-stream/disp32
+2185     # . . discard args
+2186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2187 $emit-opcodes:op1:
+2188     # next-word(line, op1)
+2189     # . . push args
+2190     51/push-ECX
+2191     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2192     # . . call
+2193     e8/call  next-word/disp32
+2194     # . . discard args
+2195     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2196     # if (slice-empty?(op1)) return
+2197     # . EAX = slice-empty?(op1)
+2198     # . . push args
+2199     51/push-ECX
+2200     # . . call
+2201     e8/call  slice-empty?/disp32
+2202     # . . discard args
+2203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2204     # . if (EAX != 0) return
+2205     3d/compare-EAX-and  0/imm32
+2206     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
+2207     # if (slice-starts-with?(op1, "#")) return
+2208     # . start/EBX = op1->start
+2209     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # copy *ECX to EBX
+2210     # . c/EAX = *start
+2211     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2212     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
+2213     # . if (EAX == '#') return
+2214     3d/compare-EAX-and  0x23/imm32/hash
+2215     0f 84/jump-if-equal  $emit-opcodes:end/disp32
+2216     # op1 = next-token-from-slice(op1->start, op1->end, '/')
+2217     # . . push args
+2218     51/push-ECX
+2219     68/push  0x2f/imm32/slash
+2220     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+2221     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+2222     # . . call
+2223     e8/call  next-token-from-slice/disp32
+2224     # . . discard args
+2225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+2226     # write-slice(out, op1)
+2227     # . . push args
+2228     51/push-ECX
+2229     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 2230     # . . call
-2231     e8/call  check-ints-equal/disp32
+2231     e8/call  write-slice/disp32
 2232     # . . discard args
-2233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2234     # . epilog
-2235     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2236     5d/pop-to-EBP
-2237     c3/return
-2238 
-2239 test-next-word-returns-empty-string-on-eof:
-2240     # . prolog
-2241     55/push-EBP
-2242     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2243     # setup
-2244     # . clear-stream(_test-stream)
-2245     # . . push args
-2246     68/push  _test-stream/imm32
+2233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2234     # write-buffered(out, " ")
+2235     # . . push args
+2236     68/push  " "/imm32
+2237     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2238     # . . call
+2239     e8/call  write-buffered/disp32
+2240     # . . discard args
+2241     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2242     # if (slice-equal?(op1, "0f")) goto op2
+2243     # . EAX = slice-equal?(op1, "0f")
+2244     # . . push args
+2245     68/push  "0f"/imm32
+2246     51/push-ECX
 2247     # . . call
-2248     e8/call  clear-stream/disp32
+2248     e8/call  slice-equal?/disp32
 2249     # . . discard args
-2250     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2251     # var slice/ECX = {0, 0}
-2252     68/push  0/imm32/end
-2253     68/push  0/imm32/start
-2254     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2255     # write nothing to _test-stream
-2256     # next-word(_test-stream, slice)
-2257     # . . push args
+2250     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2251     # . if (EAX != 0) goto op2
+2252     3d/compare-EAX-and  0/imm32
+2253     75/jump-if-not-equal  $emit-opcodes:op2/disp8
+2254     # if (slice-equal?(op1, "f2")) goto op2
+2255     # . EAX = slice-equal?(op1, "f2")
+2256     # . . push args
+2257     68/push  "f2"/imm32
 2258     51/push-ECX
-2259     68/push  _test-stream/imm32
-2260     # . . call
-2261     e8/call  next-word/disp32
-2262     # . . discard args
-2263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2264     # check-ints-equal(slice->end - slice->start, 0, msg)
-2265     # . . push args
-2266     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
-2267     68/push  0/imm32
-2268     # . . push slice->end - slice->start
-2269     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-2270     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
-2271     50/push-EAX
-2272     # . . call
-2273     e8/call  check-ints-equal/disp32
-2274     # . . discard args
-2275     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2276     # . epilog
-2277     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2278     5d/pop-to-EBP
-2279     c3/return
-2280 
-2281 has-metadata?:  # word : (address slice), s : (address string) -> EAX : boolean
-2282     # pseudocode:
-2283     #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
-2284     #   curr = twig->end
-2285     #   while true
-2286     #     twig = next-token-from-slice(curr, word->end, '/')
-2287     #     if (twig.empty()) break
-2288     #     if (slice-equal?(twig, s)) return true
-2289     #     curr = twig->end
-2290     #   return false
-2291     # . prolog
-2292     55/push-EBP
-2293     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2294     # . save registers
-2295     51/push-ECX
-2296     52/push-EDX
-2297     56/push-ESI
-2298     57/push-EDI
-2299     # ESI = word
-2300     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-2301     # EDX = word->end
-2302     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
-2303     # var twig/EDI : (address slice) = {0, 0}
-2304     68/push  0/imm32/end
-2305     68/push  0/imm32/start
-2306     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
-2307     # next-token-from-slice(word->start, word->end, '/', twig)
-2308     # . . push args
-2309     57/push-EDI
-2310     68/push  0x2f/imm32/slash
+2259     # . . call
+2260     e8/call  slice-equal?/disp32
+2261     # . . discard args
+2262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2263     # . if (EAX != 0) goto op2
+2264     3d/compare-EAX-and  0/imm32
+2265     75/jump-if-not-equal  $emit-opcodes:op2/disp8
+2266     # if (slice-equal?(op1, "f3")) goto op2
+2267     # . EAX = slice-equal?(op1, "f3")
+2268     # . . push args
+2269     68/push  "f3"/imm32
+2270     51/push-ECX
+2271     # . . call
+2272     e8/call  slice-equal?/disp32
+2273     # . . discard args
+2274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2275     # . if (EAX != 0) goto op2
+2276     3d/compare-EAX-and  0/imm32
+2277     75/jump-if-not-equal  $emit-opcodes:op2/disp8
+2278     # otherwise return
+2279     e9/jump  $emit-opcodes:end/disp32
+2280 $emit-opcodes:op2:
+2281     # next-word(line, op2)
+2282     # . . push args
+2283     52/push-EDX
+2284     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2285     # . . call
+2286     e8/call  next-word/disp32
+2287     # . . discard args
+2288     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2289     # if (slice-empty?(op2)) return
+2290     # . EAX = slice-empty?(op2)
+2291     # . . push args
+2292     52/push-EDX
+2293     # . . call
+2294     e8/call  slice-empty?/disp32
+2295     # . . discard args
+2296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2297     # . if (EAX != 0) return
+2298     3d/compare-EAX-and  0/imm32
+2299     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
+2300     # if (slice-starts-with?(op2, "#")) return
+2301     # . start/EBX = op2->start
+2302     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
+2303     # . c/EAX = *start
+2304     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2305     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
+2306     # . if (EAX == '#') return
+2307     3d/compare-EAX-and  0x23/imm32/hash
+2308     0f 84/jump-if-equal  $emit-opcodes:end/disp32
+2309     # op2 = next-token-from-slice(op2->start, op2->end, '/')
+2310     # . . push args
 2311     52/push-EDX
-2312     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-2313     # . . call
-2314     e8/call  next-token-from-slice/disp32
-2315     # . . discard args
-2316     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-2317     # curr/ECX = twig->end
-2318     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
-2319 $has-metadata?:loop:
-2320     # next-token-from-slice(curr, word->end, '/', twig)
-2321     # . . push args
-2322     57/push-EDI
-2323     68/push  0x2f/imm32/slash
-2324     52/push-EDX
-2325     51/push-ECX
-2326     # . . call
-2327     e8/call  next-token-from-slice/disp32
-2328     # . . discard args
-2329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-2330     # if (slice-empty?(twig)) return false
-2331     # . EAX = slice-empty?(twig)
-2332     # . . push args
-2333     57/push-EDI
-2334     # . . call
-2335     e8/call  slice-empty?/disp32
-2336     # . . discard args
-2337     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2338     # . if (EAX != 0) return false
-2339     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
-2340     75/jump-if-not-equal  $has-metadata?:false/disp8
-2341     # if (slice-equal?(twig, s)) return true
-2342     # . EAX = slice-equal?(twig, s)
-2343     # . . push args
-2344     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-2345     57/push-EDI
-2346     # . . call
-2347     e8/call  slice-equal?/disp32
-2348     # . . discard args
-2349     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2350     # . if (EAX != 0) return true
-2351     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
-2352     75/jump-if-not-equal  $has-metadata?:true/disp8
-2353     # curr = twig->end
-2354     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
-2355     eb/jump  $has-metadata?:loop/disp8
-2356 $has-metadata?:true:
-2357     b8/copy-to-EAX  1/imm32/true
-2358     eb/jump  $has-metadata?:end/disp8
-2359 $has-metadata?:false:
-2360     b8/copy-to-EAX  0/imm32/false
-2361 $has-metadata?:end:
-2362     # . reclaim locals
-2363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2364     # . restore registers
-2365     5f/pop-to-EDI
-2366     5e/pop-to-ESI
-2367     5a/pop-to-EDX
-2368     59/pop-to-ECX
-2369     # . epilog
-2370     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2371     5d/pop-to-EBP
-2372     c3/return
-2373 
-2374 test-has-metadata-true:
-2375     # . prolog
-2376     55/push-EBP
-2377     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2378     # (EAX..ECX) = "ab/c"
-2379     b8/copy-to-EAX  "ab/c"/imm32
-2380     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2381     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
-2382     05/add-to-EAX  4/imm32
-2383     # var in/ESI : (address slice) = {EAX, ECX}
-2384     51/push-ECX
-2385     50/push-EAX
-2386     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
-2387     # EAX = has-metadata?(ESI, "c")
-2388     # . . push args
-2389     68/push  "c"/imm32
-2390     56/push-ESI
-2391     # . . call
-2392     e8/call  has-metadata?/disp32
-2393     # . . discard args
-2394     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
-2395     # check-ints-equal(EAX, 1, msg)
-2396     # . . push args
-2397     68/push  "F - test-has-metadata-true"/imm32
-2398     68/push  1/imm32/true
-2399     50/push-EAX
-2400     # . . call
-2401     e8/call  check-ints-equal/disp32
-2402     # . . discard args
-2403     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2404     # . epilog
-2405     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2406     5d/pop-to-EBP
-2407     c3/return
-2408 
-2409 test-has-metadata-false:
-2410     # . prolog
-2411     55/push-EBP
-2412     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2413     # (EAX..ECX) = "ab/c"
-2414     b8/copy-to-EAX  "ab/c"/imm32
-2415     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2416     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
-2417     05/add-to-EAX  4/imm32
-2418     # var in/ESI : (address slice) = {EAX, ECX}
-2419     51/push-ECX
-2420     50/push-EAX
-2421     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
-2422     # EAX = has-metadata?(ESI, "c")
-2423     # . . push args
-2424     68/push  "d"/imm32
-2425     56/push-ESI
-2426     # . . call
-2427     e8/call  has-metadata?/disp32
-2428     # . . discard args
-2429     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
-2430     # check-ints-equal(EAX, 0, msg)
-2431     # . . push args
-2432     68/push  "F - test-has-metadata-false"/imm32
-2433     68/push  0/imm32/false
-2434     50/push-EAX
-2435     # . . call
-2436     e8/call  check-ints-equal/disp32
-2437     # . . discard args
-2438     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2439     # . epilog
-2440     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2441     5d/pop-to-EBP
-2442     c3/return
-2443 
-2444 test-has-metadata-ignore-name:
-2445     # . prolog
-2446     55/push-EBP
-2447     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2448     # (EAX..ECX) = "a/b"
-2449     b8/copy-to-EAX  "a/b"/imm32
-2450     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2451     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
-2452     05/add-to-EAX  4/imm32
-2453     # var in/ESI : (address slice) = {EAX, ECX}
-2454     51/push-ECX
-2455     50/push-EAX
-2456     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
-2457     # EAX = has-metadata?(ESI, "a")
-2458     # . . push args
-2459     68/push  "a"/imm32
-2460     56/push-ESI
-2461     # . . call
-2462     e8/call  has-metadata?/disp32
-2463     # . . discard args
-2464     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
-2465     # check-ints-equal(EAX, 0, msg)
-2466     # . . push args
-2467     68/push  "F - test-has-metadata-ignore-name"/imm32
-2468     68/push  0/imm32/false
-2469     50/push-EAX
-2470     # . . call
-2471     e8/call  check-ints-equal/disp32
-2472     # . . discard args
-2473     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2474     # . epilog
-2475     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2476     5d/pop-to-EBP
-2477     c3/return
-2478 
-2479 test-has-metadata-multiple-true:
-2480     # . prolog
-2481     55/push-EBP
-2482     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2483     # (EAX..ECX) = "a/b/c"
-2484     b8/copy-to-EAX  "a/b/c"/imm32
-2485     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2486     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
-2487     05/add-to-EAX  4/imm32
-2488     # var in/ESI : (address slice) = {EAX, ECX}
-2489     51/push-ECX
-2490     50/push-EAX
-2491     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
-2492     # EAX = has-metadata?(ESI, "c")
-2493     # . . push args
-2494     68/push  "c"/imm32
-2495     56/push-ESI
-2496     # . . call
-2497     e8/call  has-metadata?/disp32
-2498     # . . discard args
-2499     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
-2500     # check-ints-equal(EAX, 1, msg)
-2501     # . . push args
-2502     68/push  "F - test-has-metadata-multiple-true"/imm32
-2503     68/push  1/imm32/true
-2504     50/push-EAX
-2505     # . . call
-2506     e8/call  check-ints-equal/disp32
-2507     # . . discard args
-2508     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2509     # . epilog
-2510     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2511     5d/pop-to-EBP
-2512     c3/return
-2513 
-2514 test-has-metadata-multiple-false:
-2515     # . prolog
-2516     55/push-EBP
-2517     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2518     # (EAX..ECX) = "a/b/c"
-2519     b8/copy-to-EAX  "a/b/c"/imm32
-2520     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2521     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
-2522     05/add-to-EAX  4/imm32
-2523     # var in/ESI : (address slice) = {EAX, ECX}
-2524     51/push-ECX
-2525     50/push-EAX
-2526     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
-2527     # EAX = has-metadata?(ESI, "d")
-2528     # . . push args
-2529     68/push  "d"/imm32
-2530     56/push-ESI
-2531     # . . call
-2532     e8/call  has-metadata?/disp32
-2533     # . . discard args
-2534     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
-2535     # check-ints-equal(EAX, 0, msg)
-2536     # . . push args
-2537     68/push  "F - test-has-metadata-multiple-false"/imm32
-2538     68/push  0/imm32/false
-2539     50/push-EAX
-2540     # . . call
-2541     e8/call  check-ints-equal/disp32
-2542     # . . discard args
-2543     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2544     # . epilog
-2545     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2546     5d/pop-to-EBP
-2547     c3/return
-2548 
-2549 # If value of 'word' is not a valid name, it must be a hex int. Parse and print
-2550 # it in 'width' bytes of hex, least significant first.
-2551 # Otherwise just print the entire word including metadata.
-2552 # Always print a trailing space.
-2553 emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
-2554     # . prolog
-2555     55/push-EBP
-2556     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2557     # . save registers
-2558     50/push-EAX
-2559     56/push-ESI
-2560     57/push-EDI
-2561     # ESI = word
-2562     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
-2563     # var name/EDI : (address slice) = {0, 0}
-2564     68/push  0/imm32/end
-2565     68/push  0/imm32/start
-2566     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
-2567     # name = next-token-from-slice(word->start, word->end, '/')
-2568     # . . push args
-2569     57/push-EDI
-2570     68/push  0x2f/imm32/slash
-2571     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
-2572     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-2573     # . . call
-2574     e8/call  next-token-from-slice/disp32
-2575     # . . discard args
-2576     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-2577     # if (is-valid-name?(name)) write-slice(out, word) and return
-2578     # . EAX = is-valid-name?(name)
-2579     # . . push args
-2580     57/push-EDI
-2581     # . . call
-2582     e8/call  is-valid-name?/disp32
-2583     # . . discard args
-2584     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2585     # . if (EAX != 0)
-2586     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
-2587     74/jump-if-equal  $emit:hex-int/disp8
-2588 $emit:name:
-2589     # . write-slice(out, word)
-2590     # . . push args
-2591     56/push-ESI
-2592     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2593     # . . call
-2594     e8/call  write-slice/disp32
-2595     # . . discard args
-2596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2597     # . write-buffered(out, " ")
-2598     # . . push args
-2599     68/push  " "/imm32
-2600     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2601     # . . call
-2602     e8/call  write-buffered/disp32
-2603     # . . discard args
-2604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2605     # . return
-2606     eb/jump  $emit:end/disp8
-2607     # otherwise emit-hex(out, parse-hex-int(name), width)
-2608     #   (Weird shit can happen here if the value of 'word' isn't either a valid
-2609     #   name or a hex number, but we're only going to be passing in real legal
-2610     #   programs. We just want to make sure that valid names aren't treated as
-2611     #   (valid) hex numbers.)
-2612 $emit:hex-int:
-2613     # . n/EAX = parse-hex-int(name)
-2614     # . . push args
-2615     57/push-EDI
-2616     # . . call
-2617     e8/call  parse-hex-int/disp32
-2618     # . . discard args
-2619     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2620     # . emit-hex(out, n, width)
-2621     # . . push args
-2622     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
-2623     50/push-EAX
-2624     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2625     # . . call
-2626     e8/call  emit-hex/disp32
-2627     # . . discard args
-2628     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2629 $emit:end:
-2630     # . reclaim locals
-2631     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2632     # . restore registers
-2633     5f/pop-to-EDI
-2634     5e/pop-to-ESI
-2635     58/pop-to-EAX
-2636     # . epilog
-2637     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2638     5d/pop-to-EBP
-2639     c3/return
-2640 
-2641 test-emit-number:
-2642     # . prolog
-2643     55/push-EBP
-2644     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2645     # setup
-2646     # . clear-stream(_test-stream)
-2647     # . . push args
-2648     68/push  _test-stream/imm32
-2649     # . . call
-2650     e8/call  clear-stream/disp32
-2651     # . . discard args
-2652     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2653     # . clear-stream(_test-buffered-file+4)
-2654     # . . push args
-2655     b8/copy-to-EAX  _test-buffered-file/imm32
-2656     05/add-to-EAX  4/imm32
-2657     50/push-EAX
-2658     # . . call
-2659     e8/call  clear-stream/disp32
-2660     # . . discard args
-2661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2662     # var slice/ECX = "30"
-2663     68/push  _test-slice-three-zero-end/imm32/end
-2664     68/push  _test-slice-three-zero/imm32/start
-2665     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2666     # emit(_test-buffered-file, slice, 1)
-2667     # . . push args
-2668     68/push  1/imm32
-2669     51/push-ECX
-2670     68/push  _test-buffered-file/imm32
-2671     # . . call
-2672     e8/call  emit/disp32
-2673     # . . discard args
-2674     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2675     # flush(_test-buffered-file)
-2676     # . . push args
-2677     68/push  _test-buffered-file/imm32
-2678     # . . call
-2679     e8/call  flush/disp32
-2680     # . . discard args
-2681     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2682     # check-stream-equal(_test-stream, "30 ", msg)
-2683     # . . push args
-2684     68/push  "F - test-emit-number/1"/imm32
-2685     68/push  "30 "/imm32
-2686     68/push  _test-stream/imm32
-2687     # . . call
-2688     e8/call  check-stream-equal/disp32
-2689     # . . discard args
-2690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2691     # . epilog
-2692     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2693     5d/pop-to-EBP
-2694     c3/return
-2695 
-2696 test-emit-negative-number:
-2697     # test support for sign-extending negative numbers
-2698     # . prolog
-2699     55/push-EBP
-2700     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2701     # setup
-2702     # . clear-stream(_test-stream)
-2703     # . . push args
-2704     68/push  _test-stream/imm32
-2705     # . . call
-2706     e8/call  clear-stream/disp32
-2707     # . . discard args
-2708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2709     # . clear-stream(_test-buffered-file+4)
-2710     # . . push args
-2711     b8/copy-to-EAX  _test-buffered-file/imm32
-2712     05/add-to-EAX  4/imm32
-2713     50/push-EAX
-2714     # . . call
-2715     e8/call  clear-stream/disp32
-2716     # . . discard args
-2717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2718     # var slice/ECX = "-2"
-2719     68/push  _test-slice-negative-two-end/imm32/end
-2720     68/push  _test-slice-negative-two/imm32/start
-2721     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2722     # emit(_test-buffered-file, slice, 2)
-2723     # . . push args
-2724     68/push  2/imm32
-2725     51/push-ECX
-2726     68/push  _test-buffered-file/imm32
-2727     # . . call
-2728     e8/call  emit/disp32
-2729     # . . discard args
-2730     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2731     # flush(_test-buffered-file)
+2312     68/push  0x2f/imm32/slash
+2313     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
+2314     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
+2315     # . . call
+2316     e8/call  next-token-from-slice/disp32
+2317     # . . discard args
+2318     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+2319     # write-slice(out, op2)
+2320     # . . push args
+2321     52/push-EDX
+2322     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2323     # . . call
+2324     e8/call  write-slice/disp32
+2325     # . . discard args
+2326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2327     # write-buffered(out, " ")
+2328     # . . push args
+2329     68/push  " "/imm32
+2330     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2331     # . . call
+2332     e8/call  write-buffered/disp32
+2333     # . . discard args
+2334     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2335     # if (slice-equal?(op1, "0f")) return
+2336     # . EAX = slice-equal?(op1, "0f")
+2337     # . . push args
+2338     68/push  "0f"/imm32
+2339     51/push-ECX
+2340     # . . call
+2341     e8/call  slice-equal?/disp32
+2342     # . . discard args
+2343     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2344     # . if (EAX != 0) return
+2345     3d/compare-EAX-and  0/imm32
+2346     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
+2347     # if (!slice-equal?(op2, "0f")) return
+2348     # . EAX = slice-equal?(op2, "0f")
+2349     # . . push args
+2350     68/push  "0f"/imm32
+2351     52/push-EDX
+2352     # . . call
+2353     e8/call  slice-equal?/disp32
+2354     # . . discard args
+2355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2356     # . if (EAX == 0) return
+2357     3d/compare-EAX-and  0/imm32
+2358     0f 84/jump-if-equal  $emit-opcodes:end/disp32
+2359 $emit-opcodes:op3:
+2360     # next-word(line, op3)  # reuse op2/EDX
+2361     # . . push args
+2362     52/push-EDX
+2363     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2364     # . . call
+2365     e8/call  next-word/disp32
+2366     # . . discard args
+2367     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2368     # if (slice-empty?(op3)) return
+2369     # . EAX = slice-empty?(op3)
+2370     # . . push args
+2371     52/push-EDX
+2372     # . . call
+2373     e8/call  slice-empty?/disp32
+2374     # . . discard args
+2375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2376     # . if (EAX != 0) return
+2377     3d/compare-EAX-and  0/imm32
+2378     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
+2379     # if (slice-starts-with?(op3, "#")) return
+2380     # . start/EBX = op2->start
+2381     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
+2382     # . c/EAX = *start
+2383     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2384     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
+2385     # . if (EAX == '#') return
+2386     3d/compare-EAX-and  0x23/imm32/hash
+2387     0f 84/jump-if-equal  $emit-opcodes:end/disp32
+2388     # op3 = next-token-from-slice(op3->start, op3->end, '/')
+2389     # . . push args
+2390     52/push-EDX
+2391     68/push  0x2f/imm32/slash
+2392     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
+2393     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
+2394     # . . call
+2395     e8/call  next-token-from-slice/disp32
+2396     # . . discard args
+2397     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+2398     # write-slice(out, op3)
+2399     # . . push args
+2400     52/push-EDX
+2401     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2402     # . . call
+2403     e8/call  write-slice/disp32
+2404     # . . discard args
+2405     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2406     # write-buffered(out, " ")
+2407     # . . push args
+2408     68/push  " "/imm32
+2409     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2410     # . . call
+2411     e8/call  write-buffered/disp32
+2412     # . . discard args
+2413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2414 $emit-opcodes:end:
+2415     # . restore locals
+2416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+2417     # . restore registers
+2418     5b/pop-to-EBX
+2419     5a/pop-to-EDX
+2420     59/pop-to-ECX
+2421     58/pop-to-EAX
+2422     # . epilog
+2423     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2424     5d/pop-to-EBP
+2425     c3/return
+2426 
+2427 emit-modrm:  # line : (address stream byte), out : (address buffered-file) -> <void>
+2428     # pseudocode:
+2429     #   rewind-stream(line)
+2430     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
+2431     #   var word-slice = {0, 0}
+2432     #   while true
+2433     #     word-slice = next-word(line)
+2434     #     if (slice-empty?(word-slice)) break
+2435     #     if (slice-starts-with?(word-slice, "#")) break
+2436     #     if (has-metadata?(word-slice, "mod"))
+2437     #       mod = parse-hex-int(next-token-from-slice(word-slice, "/"))
+2438     #       has-modrm? = true
+2439     #     else if (has-metadata?(word-slice, "rm32"))
+2440     #       rm32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
+2441     #       has-modrm? = true
+2442     #     else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop"))
+2443     #       r32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
+2444     #       has-modrm? = true
+2445     #   if has-modrm?
+2446     #     var modrm = mod & 0b11
+2447     #     modrm <<= 2
+2448     #     modrm |= r32 & 0b111
+2449     #     modrm <<= 3
+2450     #     modrm |= rm32 & 0b111
+2451     #     emit-hex(out, modrm, 1)
+2452     #
+2453     # . prolog
+2454     55/push-EBP
+2455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2456     # . save registers
+2457     50/push-EAX
+2458     51/push-ECX
+2459     52/push-EDX
+2460     53/push-EBX
+2461     56/push-ESI
+2462     57/push-EDI
+2463     # var word-slice/ECX = {0, 0}
+2464     68/push  0/imm32/end
+2465     68/push  0/imm32/start
+2466     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2467     # var has-modrm?/EDX = false
+2468     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
+2469     # var mod/EBX = 0
+2470     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+2471     # var rm32/ESI = 0
+2472     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
+2473     # var r32/EDI = 0
+2474     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
+2475     # rewind-stream(line)
+2476     # . . push args
+2477     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2478     # . . call
+2479     e8/call  rewind-stream/disp32
+2480     # . . discard args
+2481     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2482 $emit-modrm:loop:
+2483 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+2517     # next-word(line, word-slice)
+2518     # . . push args
+2519     51/push-ECX
+2520     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2521     # . . call
+2522     e8/call  next-word/disp32
+2523     # . . discard args
+2524     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2525 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+2575 $emit-modrm:check0:
+2576     # if (slice-empty?(word-slice)) break
+2577     # . EAX = slice-empty?(word-slice)
+2578     # . . push args
+2579     51/push-ECX
+2580     # . . call
+2581     e8/call  slice-empty?/disp32
+2582     # . . discard args
+2583     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2584     # . if (EAX != 0) pass through
+2585     3d/compare-EAX-and  0/imm32
+2586     0f 85/jump-if-not-equal  $emit-modrm:break/disp32
+2587 $emit-modrm:check1:
+2588     # if (slice-starts-with?(word-slice, "#")) break
+2589     # . spill EDX
+2590     52/push-EDX
+2591     # . start/EDX = word-slice->start
+2592     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+2593     # . c/EAX = *start
+2594     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2595     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
+2596     # . restore EDX
+2597     5a/pop-to-EDX
+2598     # . if (EAX == '#') pass through
+2599     3d/compare-EAX-and  0x23/imm32/hash
+2600     0f 84/jump-if-equal  $emit-modrm:break/disp32
+2601 $emit-modrm:check-for-mod:
+2602     # if (has-metadata?(word-slice, "mod"))
+2603     # . EAX = has-metadata?(ECX, "mod")
+2604     # . . push args
+2605     68/push  "mod"/imm32
+2606     51/push-ECX
+2607     # . . call
+2608     e8/call  has-metadata?/disp32
+2609     # . . discard args
+2610     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2611     # . if (EAX == 0) goto next check
+2612     3d/compare-EAX-and  0/imm32
+2613     74/jump-if-equal  $emit-modrm:check-for-rm32/disp8
+2614 $emit-modrm:mod:
+2615     # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
+2616     # . EAX = parse-datum-of-word(word-slice)
+2617     # . . push args
+2618     51/push-ECX
+2619     # . . call
+2620     e8/call  parse-datum-of-word/disp32
+2621     # . . discard args
+2622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2623     # . mod = EAX
+2624     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+2625     # has-modrm? = true
+2626     ba/copy-to-EDX  1/imm32/true
+2627     # continue
+2628     e9/jump  $emit-modrm:loop/disp32
+2629 $emit-modrm:check-for-rm32:
+2630     # if (has-metadata?(word-slice, "rm32"))
+2631     # . EAX = has-metadata?(ECX, "rm32")
+2632     # . . push args
+2633     68/push  "rm32"/imm32
+2634     51/push-ECX
+2635     # . . call
+2636     e8/call  has-metadata?/disp32
+2637     # . . discard args
+2638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2639     # . if (EAX == 0) goto next check
+2640     3d/compare-EAX-and  0/imm32
+2641     74/jump-if-equal  $emit-modrm:check-for-r32/disp8
+2642 $emit-modrm:rm32:
+2643     # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
+2644     # . EAX = parse-datum-of-word(word-slice)
+2645     # . . push args
+2646     51/push-ECX
+2647     # . . call
+2648     e8/call  parse-datum-of-word/disp32
+2649     # . . discard args
+2650     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2651     # . rm32 = EAX
+2652     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
+2653     # has-modrm? = true
+2654     ba/copy-to-EDX  1/imm32/true
+2655     # continue
+2656     e9/jump  $emit-modrm:loop/disp32
+2657 $emit-modrm:check-for-r32:
+2658     # if (has-metadata?(word-slice, "r32"))
+2659     # . EAX = has-metadata?(ECX, "r32")
+2660     # . . push args
+2661     68/push  "r32"/imm32
+2662     51/push-ECX
+2663     # . . call
+2664     e8/call  has-metadata?/disp32
+2665     # . . discard args
+2666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2667     # . if (EAX == 0) goto next check
+2668     3d/compare-EAX-and  0/imm32
+2669     74/jump-if-equal  $emit-modrm:check-for-subop/disp8
+2670 $emit-modrm:r32:
+2671     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
+2672     # . EAX = parse-datum-of-word(word-slice)
+2673     # . . push args
+2674     51/push-ECX
+2675     # . . call
+2676     e8/call  parse-datum-of-word/disp32
+2677     # . . discard args
+2678     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2679     # . r32 = EAX
+2680     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
+2681     # has-modrm? = true
+2682     ba/copy-to-EDX  1/imm32/true
+2683     # continue
+2684     e9/jump  $emit-modrm:loop/disp32
+2685 $emit-modrm:check-for-subop:
+2686     # if (has-metadata?(word-slice, "subop"))
+2687     # . EAX = has-metadata?(ECX, "subop")
+2688     # . . push args
+2689     68/push  "subop"/imm32
+2690     51/push-ECX
+2691     # . . call
+2692     e8/call  has-metadata?/disp32
+2693     # . . discard args
+2694     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2695     # . if (EAX == 0) loop
+2696     3d/compare-EAX-and  0/imm32
+2697     0f 84/jump-if-equal  $emit-modrm:loop/disp32
+2698 $emit-modrm:subop:
+2699     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
+2700     # . EAX = parse-datum-of-word(word-slice)
+2701     # . . push args
+2702     51/push-ECX
+2703     # . . call
+2704     e8/call  parse-datum-of-word/disp32
+2705     # . . discard args
+2706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2707     # . r32 = EAX
+2708     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
+2709     # has-modrm? = true
+2710     ba/copy-to-EDX  1/imm32/true
+2711     # continue
+2712     e9/jump  $emit-modrm:loop/disp32
+2713 $emit-modrm:break:
+2714     # if (!has-modrm?) return
+2715     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
+2716     74/jump-if-equal  $emit-modrm:end/disp8
+2717 $emit-modrm:calculate:
+2718     # modrm/EBX = mod & 0b11
+2719     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
+2720     # modrm <<= 2
+2721     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
+2722     # modrm |= r32 & 0b111
+2723     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
+2724     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
+2725     # modrm <<= 3
+2726     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
+2727     # modrm |= rm32 & 0b111
+2728     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
+2729     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
+2730 $emit-modrm:emit:
+2731     # emit-hex(out, modrm, 1)
 2732     # . . push args
-2733     68/push  _test-buffered-file/imm32
-2734     # . . call
-2735     e8/call  flush/disp32
-2736     # . . discard args
-2737     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2738     # check-stream-equal(_test-stream, "fe ff ", msg)
-2739     # . . push args
-2740     68/push  "F - test-emit-number/1"/imm32
-2741     68/push  "fe ff "/imm32
-2742     68/push  _test-stream/imm32
-2743     # . . call
-2744     e8/call  check-stream-equal/disp32
-2745     # . . discard args
-2746     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2747     # . epilog
-2748     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2749     5d/pop-to-EBP
-2750     c3/return
-2751 
-2752 test-emit-number-with-metadata:
-2753     # . prolog
-2754     55/push-EBP
-2755     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2756     # setup
-2757     # . clear-stream(_test-stream)
-2758     # . . push args
-2759     68/push  _test-stream/imm32
-2760     # . . call
-2761     e8/call  clear-stream/disp32
-2762     # . . discard args
-2763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2764     # . clear-stream(_test-buffered-file+4)
-2765     # . . push args
-2766     b8/copy-to-EAX  _test-buffered-file/imm32
-2767     05/add-to-EAX  4/imm32
-2768     50/push-EAX
-2769     # . . call
-2770     e8/call  clear-stream/disp32
-2771     # . . discard args
-2772     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2773     # var slice/ECX = "-2/foo"
-2774     68/push  _test-slice-negative-two-metadata-end/imm32/end
-2775     68/push  _test-slice-negative-two/imm32/start
-2776     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2777     # emit(_test-buffered-file, slice, 2)
-2778     # . . push args
-2779     68/push  2/imm32
-2780     51/push-ECX
-2781     68/push  _test-buffered-file/imm32
-2782     # . . call
-2783     e8/call  emit/disp32
-2784     # . . discard args
-2785     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2786     # flush(_test-buffered-file)
-2787     # . . push args
-2788     68/push  _test-buffered-file/imm32
-2789     # . . call
-2790     e8/call  flush/disp32
-2791     # . . discard args
-2792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2793     # the '/foo' will have no impact on the output
-2794     # check-stream-equal(_test-stream, "fe ff ", msg)
-2795     # . . push args
-2796     68/push  "F - test-emit-number-with-metadata"/imm32
-2797     68/push  "fe ff "/imm32
-2798     68/push  _test-stream/imm32
-2799     # . . call
-2800     e8/call  check-stream-equal/disp32
-2801     # . . discard args
-2802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2803     # . epilog
-2804     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2805     5d/pop-to-EBP
-2806     c3/return
-2807 
-2808 test-emit-non-number:
-2809     # . prolog
-2810     55/push-EBP
-2811     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2812     # setup
-2813     # . clear-stream(_test-stream)
-2814     # . . push args
-2815     68/push  _test-stream/imm32
-2816     # . . call
-2817     e8/call  clear-stream/disp32
-2818     # . . discard args
-2819     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2820     # . clear-stream(_test-buffered-file+4)
-2821     # . . push args
-2822     b8/copy-to-EAX  _test-buffered-file/imm32
-2823     05/add-to-EAX  4/imm32
-2824     50/push-EAX
-2825     # . . call
-2826     e8/call  clear-stream/disp32
-2827     # . . discard args
-2828     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2829     # var slice/ECX = "xyz"
-2830     68/push  _test-slice-non-number-word-end/imm32/end
-2831     68/push  _test-slice-non-number-word/imm32/start
-2832     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2833     # emit(_test-buffered-file, slice, 2)
-2834     # . . push args
-2835     68/push  2/imm32
-2836     51/push-ECX
-2837     68/push  _test-buffered-file/imm32
-2838     # . . call
-2839     e8/call  emit/disp32
-2840     # . . discard args
-2841     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2842     # flush(_test-buffered-file)
-2843     # . . push args
-2844     68/push  _test-buffered-file/imm32
-2845     # . . call
-2846     e8/call  flush/disp32
-2847     # . . discard args
-2848     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2849     # check-stream-equal(_test-stream, "xyz", msg)
-2850     # . . push args
-2851     68/push  "F - test-emit-non-number"/imm32
-2852     68/push  "xyz "/imm32
-2853     68/push  _test-stream/imm32
-2854     # . . call
-2855     e8/call  check-stream-equal/disp32
-2856     # . . discard args
-2857     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2858     # . epilog
-2859     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2860     5d/pop-to-EBP
-2861     c3/return
-2862 
-2863 test-emit-non-number-with-metadata:
-2864     # . prolog
-2865     55/push-EBP
-2866     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2867     # setup
-2868     # . clear-stream(_test-stream)
-2869     # . . push args
-2870     68/push  _test-stream/imm32
-2871     # . . call
-2872     e8/call  clear-stream/disp32
-2873     # . . discard args
-2874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2875     # . clear-stream(_test-buffered-file+4)
-2876     # . . push args
-2877     b8/copy-to-EAX  _test-buffered-file/imm32
-2878     05/add-to-EAX  4/imm32
-2879     50/push-EAX
-2880     # . . call
-2881     e8/call  clear-stream/disp32
-2882     # . . discard args
-2883     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2884     # var slice/ECX = "xyz/"
-2885     68/push  _test-slice-non-number-word-metadata-end/imm32/end
-2886     68/push  _test-slice-non-number-word/imm32/start
-2887     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2888     # emit(_test-buffered-file, slice, 2)
-2889     # . . push args
-2890     68/push  2/imm32
-2891     51/push-ECX
-2892     68/push  _test-buffered-file/imm32
-2893     # . . call
-2894     e8/call  emit/disp32
-2895     # . . discard args
-2896     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2897     # flush(_test-buffered-file)
-2898     # . . push args
-2899     68/push  _test-buffered-file/imm32
-2900     # . . call
-2901     e8/call  flush/disp32
-2902     # . . discard args
-2903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2904     # check-stream-equal(_test-stream, "xyz/", msg)
+2733     68/push  1/imm32
+2734     53/push-EBX
+2735     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2736     # . . call
+2737     e8/call  emit-hex/disp32
+2738     # . . discard args
+2739     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2740 $emit-modrm:end:
+2741     # . restore locals
+2742     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2743     # . restore registers
+2744     5f/pop-to-EDI
+2745     5e/pop-to-ESI
+2746     5b/pop-to-EBX
+2747     5a/pop-to-EDX
+2748     59/pop-to-ECX
+2749     58/pop-to-EAX
+2750     # . epilog
+2751     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2752     5d/pop-to-EBP
+2753     c3/return
+2754 
+2755 emit-sib:  # line : (address stream byte), out : (address buffered-file) -> <void>
+2756     # pseudocode:
+2757     #   var has-sib? = false, base = 0, index = 0, scale = 0
+2758     #   var word-slice = {0, 0}
+2759     #   while true
+2760     #     word-slice = next-word(line)
+2761     #     if (slice-empty?(word-slice)) break
+2762     #     if (slice-starts-with?(word-slice, "#")) break
+2763     #     if (has-metadata?(word-slice, "base")
+2764     #       base = parse-hex-int(next-token-from-slice(word-slice, "/"))
+2765     #       has-sib? = true
+2766     #     else if (has-metadata?(word-slice, "index")
+2767     #       index = parse-hex-int(next-token-from-slice(word-slice, "/"))
+2768     #       has-sib? = true
+2769     #     else if (has-metadata?(word-slice, "scale")
+2770     #       scale = parse-hex-int(next-token-from-slice(word-slice, "/"))
+2771     #       has-sib? = true
+2772     #   if has-sib?
+2773     #     var sib = scale & 0b11
+2774     #     sib <<= 2
+2775     #     sib |= index & 0b111
+2776     #     sib <<= 3
+2777     #     sib |= base & 0b111
+2778     #     emit-hex(out, sib, 1)
+2779     #
+2780     # . prolog
+2781     55/push-EBP
+2782     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2783     # . save registers
+2784     50/push-EAX
+2785     51/push-ECX
+2786     52/push-EDX
+2787     53/push-EBX
+2788     56/push-ESI
+2789     57/push-EDI
+2790     # var word-slice/ECX = {0, 0}
+2791     68/push  0/imm32/end
+2792     68/push  0/imm32/start
+2793     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2794     # var has-sib?/EDX = false
+2795     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
+2796     # var scale/EBX = 0
+2797     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+2798     # var base/ESI = 0
+2799     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
+2800     # var index/EDI = 0
+2801     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
+2802     # rewind-stream(line)
+2803     # . . push args
+2804     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2805     # . . call
+2806     e8/call  rewind-stream/disp32
+2807     # . . discard args
+2808     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2809 $emit-sib:loop:
+2810 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+2844     # next-word(line, word-slice)
+2845     # . . push args
+2846     51/push-ECX
+2847     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2848     # . . call
+2849     e8/call  next-word/disp32
+2850     # . . discard args
+2851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2852 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+2902 $emit-sib:check0:
+2903     # if (slice-empty?(word-slice)) break
+2904     # . EAX = slice-empty?(word-slice)
 2905     # . . push args
-2906     68/push  "F - test-emit-non-number-with-metadata"/imm32
-2907     68/push  "xyz/ "/imm32
-2908     68/push  _test-stream/imm32
-2909     # . . call
-2910     e8/call  check-stream-equal/disp32
-2911     # . . discard args
-2912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2913     # . epilog
-2914     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2915     5d/pop-to-EBP
-2916     c3/return
-2917 
-2918 test-emit-non-number-with-all-hex-digits-and-metadata:
-2919     # . prolog
-2920     55/push-EBP
-2921     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2922     # setup
-2923     # . clear-stream(_test-stream)
-2924     # . . push args
-2925     68/push  _test-stream/imm32
-2926     # . . call
-2927     e8/call  clear-stream/disp32
-2928     # . . discard args
-2929     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2930     # . clear-stream(_test-buffered-file+4)
+2906     51/push-ECX
+2907     # . . call
+2908     e8/call  slice-empty?/disp32
+2909     # . . discard args
+2910     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2911     # . if (EAX != 0) pass through
+2912     3d/compare-EAX-and  0/imm32
+2913     0f 85/jump-if-not-equal  $emit-sib:break/disp32
+2914 $emit-sib:check1:
+2915     # if (slice-starts-with?(word-slice, "#")) break
+2916     # . spill EDX
+2917     52/push-EDX
+2918     # . start/EDX = word-slice->start
+2919     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+2920     # . c/EAX = *start
+2921     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+2922     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
+2923     # . restore EDX
+2924     5a/pop-to-EDX
+2925     # . if (EAX == '#') pass through
+2926     3d/compare-EAX-and  0x23/imm32/hash
+2927     0f 84/jump-if-equal  $emit-sib:break/disp32
+2928 $emit-sib:check-for-scale:
+2929     # if (has-metadata?(word-slice, "scale"))
+2930     # . EAX = has-metadata?(ECX, "scale")
 2931     # . . push args
-2932     b8/copy-to-EAX  _test-buffered-file/imm32
-2933     05/add-to-EAX  4/imm32
-2934     50/push-EAX
-2935     # . . call
-2936     e8/call  clear-stream/disp32
-2937     # . . discard args
-2938     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2939     # var slice/ECX = "abcd/xyz"
-2940     68/push  _test-slice-hexlike-non-number-word-metadata-end/imm32/end
-2941     68/push  _test-slice-hexlike-non-number-word/imm32/start
-2942     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2943     # emit(_test-buffered-file, slice, 2)
+2932     68/push  "scale"/imm32
+2933     51/push-ECX
+2934     # . . call
+2935     e8/call  has-metadata?/disp32
+2936     # . . discard args
+2937     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2938     # . if (EAX == 0) goto next check
+2939     3d/compare-EAX-and  0/imm32
+2940     74/jump-if-equal  $emit-sib:check-for-base/disp8
+2941 $emit-sib:scale:
+2942     # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
+2943     # . EAX = parse-datum-of-word(word-slice)
 2944     # . . push args
-2945     68/push  2/imm32
-2946     51/push-ECX
-2947     68/push  _test-buffered-file/imm32
-2948     # . . call
-2949     e8/call  emit/disp32
-2950     # . . discard args
-2951     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2952     # flush(_test-buffered-file)
-2953     # . . push args
-2954     68/push  _test-buffered-file/imm32
-2955     # . . call
-2956     e8/call  flush/disp32
-2957     # . . discard args
-2958     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2959     # check-stream-equal(_test-stream, "abcd/xyz")
-2960     # . . push args
-2961     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
-2962     68/push  "abcd/xyz"/imm32
-2963     68/push  _test-stream/imm32
-2964     # . . call
-2965     e8/call  check-stream-equal/disp32
-2966     # . . discard args
-2967     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2968     # . epilog
-2969     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2970     5d/pop-to-EBP
-2971     c3/return
-2972 
-2973 # conditions for 'valid' names that are not at risk of looking like hex numbers
-2974 # keep in sync with the rules in labels.cc
-2975 #: - if it starts with a digit, it's treated as a number. If it can't be
-2976 #:   parsed as hex it will raise an error.
-2977 #: - if it starts with '-' it's treated as a number.
-2978 #: - if it starts with '0x' it's treated as a number. (redundant)
-2979 #: - if it's two characters long, it can't be a name. Either it's a hex
-2980 #:   byte, or it raises an error.
-2981 is-valid-name?:  # in : (address slice) -> EAX : boolean
-2982     # . prolog
-2983     55/push-EBP
-2984     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2985     # . save registers
-2986     51/push-ECX
-2987     56/push-ESI
-2988     # ESI = in
-2989     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-2990     # start/ECX = in->start
-2991     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
-2992     # end/EAX = in->end
-2993     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
-2994 $is-valid-name?:check0:
-2995     # if (start >= end) return false
-2996     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # compare ECX with EAX
-2997     7d/jump-if-greater-or-equal  $is-valid-name?:false/disp8
-2998 $is-valid-name?:check1:
-2999     # EAX -= ECX
-3000     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
-3001     # if (EAX == 2) return false
-3002     3d/compare-with-EAX  2/imm32
-3003     74/jump-if-equal  $is-valid-name?:false/disp8
-3004 $is-valid-name?:check2:
-3005     # c/EAX = *ECX
-3006     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-3007     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-3008     # if (c == "-") return false
-3009     3d/compare-with-EAX  2d/imm32/-
-3010     74/jump-if-equal  $is-valid-name?:false/disp8
-3011 $is-valid-name?:check3a:
-3012     # if (c < "0") return true
-3013     3d/compare-with-EAX  30/imm32/0
-3014     7c/jump-if-lesser  $is-valid-name?:true/disp8
-3015 $is-valid-name?:check3b:
-3016     # if (c > "9") return true
-3017     3d/compare-with-EAX  39/imm32/9
-3018     7f/jump-if-greater  $is-valid-name?:true/disp8
-3019 $is-valid-name?:false:
-3020     # return false
-3021     b8/copy-to-EAX  0/imm32/false
-3022     eb/jump  $is-valid-name?:end/disp8
-3023 $is-valid-name?:true:
-3024     # return true
-3025     b8/copy-to-EAX  1/imm32/true
-3026 $is-valid-name?:end:
-3027     # . restore registers
-3028     5e/pop-to-ESI
-3029     59/pop-to-ECX
-3030     # . epilog
-3031     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3032     5d/pop-to-EBP
-3033     c3/return
-3034 
-3035 test-is-valid-name-digit-prefix:
-3036     # . prolog
-3037     55/push-EBP
-3038     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3039     # var slice/ECX = "34"
-3040     68/push  _test-slice-hex-int-end/imm32
-3041     68/push  _test-slice-hex-int/imm32
-3042     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3043     # EAX = is-valid-name?(slice)
-3044     # . . push args
-3045     51/push-ECX
-3046     # . . call
-3047     e8/call  is-valid-name?/disp32
-3048     # . . discard args
-3049     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3050     # check-ints-equal(EAX, 0, msg)
-3051     # . . push args
-3052     68/push  "F - test-is-valid-name-digit-prefix"/imm32
-3053     68/push  0/imm32/false
-3054     50/push-EAX
-3055     # . . call
-3056     e8/call  check-ints-equal/disp32
-3057     # . . discard args
-3058     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3059     # . epilog
-3060     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3061     5d/pop-to-EBP
-3062     c3/return
-3063 
-3064 test-is-valid-name-negative-prefix:
-3065     # . prolog
-3066     55/push-EBP
-3067     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3068     # var slice/ECX = "-0x34"
-3069     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
-3070     68/push  _test-slice-hex-int-with-0x-prefix-negative/imm32
-3071     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3072     # EAX = is-valid-name?(slice)
-3073     # . . push args
-3074     51/push-ECX
-3075     # . . call
-3076     e8/call  is-valid-name?/disp32
-3077     # . . discard args
-3078     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3079     # check-ints-equal(EAX, 0, msg)
-3080     # . . push args
-3081     68/push  "F - test-is-valid-name-negative-prefix"/imm32
-3082     68/push  0/imm32/false
-3083     50/push-EAX
-3084     # . . call
-3085     e8/call  check-ints-equal/disp32
-3086     # . . discard args
-3087     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3088     # . epilog
-3089     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3090     5d/pop-to-EBP
-3091     c3/return
-3092 
-3093 test-is-valid-name-0x-prefix:
-3094     # . prolog
-3095     55/push-EBP
-3096     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3097     # var slice/ECX = "0x34"
-3098     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
-3099     68/push  _test-slice-hex-int-with-0x-prefix/imm32
-3100     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3101     # EAX = is-valid-name?(slice)
-3102     # . . push args
-3103     51/push-ECX
-3104     # . . call
-3105     e8/call  is-valid-name?/disp32
-3106     # . . discard args
-3107     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3108     # check-ints-equal(EAX, 0, msg)
-3109     # . . push args
-3110     68/push  "F - test-is-valid-name-0x-prefix"/imm32
-3111     68/push  0/imm32/false
-3112     50/push-EAX
-3113     # . . call
-3114     e8/call  check-ints-equal/disp32
-3115     # . . discard args
-3116     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3117     # . epilog
-3118     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3119     5d/pop-to-EBP
-3120     c3/return
-3121 
-3122 test-is-valid-name-starts-with-pre-digit:
-3123     # . prolog
-3124     55/push-EBP
-3125     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3126     # var slice/ECX = "/03"
-3127     68/push  _test-slice-with-slash-prefix-end/imm32
-3128     68/push  _test-slice-with-slash-prefix/imm32
-3129     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3130     # EAX = is-valid-name?(slice)
-3131     # . . push args
-3132     51/push-ECX
-3133     # . . call
-3134     e8/call  is-valid-name?/disp32
-3135     # . . discard args
-3136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3137     # check-ints-equal(EAX, 1, msg)
-3138     # . . push args
-3139     68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
-3140     68/push  1/imm32/true
-3141     50/push-EAX
-3142     # . . call
-3143     e8/call  check-ints-equal/disp32
-3144     # . . discard args
-3145     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3146     # . epilog
-3147     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3148     5d/pop-to-EBP
-3149     c3/return
-3150 
-3151 test-is-valid-name-starts-with-post-digit:
-3152     # . prolog
-3153     55/push-EBP
-3154     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3155     # var slice/ECX = "q34"
-3156     68/push  _test-slice-char-and-digits-end/imm32
-3157     68/push  _test-slice-char-and-digits/imm32
-3158     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3159     # EAX = is-valid-name?(slice)
-3160     # . . push args
-3161     51/push-ECX
-3162     # . . call
-3163     e8/call  is-valid-name?/disp32
-3164     # . . discard args
-3165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3166     # check-ints-equal(EAX, 1, msg)
-3167     # . . push args
-3168     68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
-3169     68/push  1/imm32/true
-3170     50/push-EAX
-3171     # . . call
-3172     e8/call  check-ints-equal/disp32
-3173     # . . discard args
-3174     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3175     # . epilog
-3176     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3177     5d/pop-to-EBP
-3178     c3/return
-3179 
-3180 test-is-valid-name-starts-with-digit:
-3181     # . prolog
-3182     55/push-EBP
-3183     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3184     # var slice/ECX = "0x34"
-3185     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
-3186     68/push  _test-slice-hex-int-with-0x-prefix/imm32
-3187     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3188     # EAX = is-valid-name?(slice)
-3189     # . . push args
-3190     51/push-ECX
-3191     # . . call
-3192     e8/call  is-valid-name?/disp32
-3193     # . . discard args
-3194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3195     # check-ints-equal(EAX, 0, msg)
-3196     # . . push args
-3197     68/push  "F - test-is-valid-name-starts-with-digit"/imm32
-3198     68/push  0/imm32/false
-3199     50/push-EAX
-3200     # . . call
-3201     e8/call  check-ints-equal/disp32
-3202     # . . discard args
-3203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3204     # . epilog
-3205     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3206     5d/pop-to-EBP
-3207     c3/return
-3208 
-3209 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
-3210 emit-hex:  # out : (address buffered-file), n : int, width : int -> <void>
-3211     # . prolog
-3212     55/push-EBP
-3213     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3214     # . save registers
-3215     50/push-EAX
-3216     51/push-ECX
-3217     52/push-EDX
-3218     53/push-EBX
-3219     57/push-EDI
-3220     # EDI = out
-3221     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
-3222     # EBX = n
-3223     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
-3224     # EDX = width
-3225     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
-3226     # var curr/ECX = 0
-3227     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
-3228 $emit-hex:loop:
-3229     # if (curr >= width) break
-3230     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
-3231     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
-3232 #?     # if (EBX == 0) write(out, "00 ") and continue
-3233 #?     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0/imm32           # compare ECX
-3234 #?     75/jump-if-not-equal  $emit-hex:print-octet/disp8
-3235 #? $emit-hex:pad-zero:
-3236 #?     # . write(out, "00 ")
-3237 #?     # . . push args
-3238 #?     68/push  "00 "/imm32
-3239 #?     57/push-EDI
-3240 #?     # . . call
-3241 #?     e8/call  write/disp32
-3242 #?     # . . discard args
-3243 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3244 #?     eb/jump  $emit-hex:continue/disp8
-3245 #? $emit-hex:print-octet:
-3246     # print-byte(out, EBX)
-3247     # . . push args
-3248     53/push-EBX
-3249     57/push-EDI
-3250     # . . call
-3251     e8/call  print-byte/disp32
-3252     # . . discard args
-3253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3254     # write-byte(out, ' ')
-3255     # . . push args
-3256     68/push  0x20/imm32/space
-3257     57/push-EDI
-3258     # . . call
-3259     e8/call  write-byte/disp32
-3260     # . . discard args
-3261     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3262     # EBX = EBX >> 8
-3263     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
-3264 #? $emit-hex:continue:
-3265     # ++curr
-3266     41/increment-ECX
-3267     eb/jump  $emit-hex:loop/disp8
-3268 $emit-hex:end:
-3269     # . restore registers
-3270     5f/pop-to-EDI
-3271     5b/pop-to-EBX
-3272     5a/pop-to-EDX
-3273     59/pop-to-ECX
-3274     58/pop-to-EAX
-3275     # . epilog
-3276     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3277     5d/pop-to-EBP
-3278     c3/return
-3279 
-3280 test-emit-hex-single-byte:
-3281     # setup
-3282     # . clear-stream(_test-stream)
-3283     # . . push args
-3284     68/push  _test-stream/imm32
-3285     # . . call
-3286     e8/call  clear-stream/disp32
-3287     # . . discard args
-3288     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3289     # . clear-stream(_test-buffered-file+4)
-3290     # . . push args
-3291     b8/copy-to-EAX  _test-buffered-file/imm32
-3292     05/add-to-EAX  4/imm32
-3293     50/push-EAX
-3294     # . . call
-3295     e8/call  clear-stream/disp32
-3296     # . . discard args
-3297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3298     # emit-hex(_test-buffered-file, 0xab, 1)
-3299     # . . push args
-3300     68/push  1/imm32
-3301     68/push  0xab/imm32
-3302     68/push  _test-buffered-file/imm32
-3303     # . . call
-3304     e8/call  emit-hex/disp32
-3305     # . . discard args
-3306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3307     # flush(_test-buffered-file)
-3308     # . . push args
-3309     68/push  _test-buffered-file/imm32
-3310     # . . call
-3311     e8/call  flush/disp32
-3312     # . . discard args
-3313     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3314     # check-ints-equal(*_test-stream->data, 'ab ', msg)
-3315     # . . push args
-3316     68/push  "F - test-emit-hex-single-byte"/imm32
-3317     68/push  0x206261/imm32
-3318     # . . push *_test-stream->data
-3319     b8/copy-to-EAX  _test-stream/imm32
-3320     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-3321     # . . call
-3322     e8/call  check-ints-equal/disp32
-3323     # . . discard args
-3324     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3325     # . end
-3326     c3/return
-3327 
-3328 test-emit-hex-multiple-byte:
-3329     # setup
-3330     # . clear-stream(_test-stream)
-3331     # . . push args
-3332     68/push  _test-stream/imm32
-3333     # . . call
-3334     e8/call  clear-stream/disp32
-3335     # . . discard args
-3336     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3337     # . clear-stream(_test-buffered-file+4)
-3338     # . . push args
-3339     b8/copy-to-EAX  _test-buffered-file/imm32
-3340     05/add-to-EAX  4/imm32
-3341     50/push-EAX
-3342     # . . call
-3343     e8/call  clear-stream/disp32
-3344     # . . discard args
-3345     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3346     # emit-hex(_test-buffered-file, 0x1234, 2)
-3347     # . . push args
-3348     68/push  2/imm32
-3349     68/push  0x1234/imm32
-3350     68/push  _test-buffered-file/imm32
-3351     # . . call
-3352     e8/call  emit-hex/disp32
-3353     # . . discard args
-3354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3355     # flush(_test-buffered-file)
-3356     # . . push args
-3357     68/push  _test-buffered-file/imm32
-3358     # . . call
-3359     e8/call  flush/disp32
-3360     # . . discard args
-3361     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3362     # check-stream-equal(_test-stream, "34 12 ", msg)
+2945     51/push-ECX
+2946     # . . call
+2947     e8/call  parse-datum-of-word/disp32
+2948     # . . discard args
+2949     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2950     # . scale = EAX
+2951     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+2952     # has-sib? = true
+2953     ba/copy-to-EDX  1/imm32/true
+2954     # continue
+2955     e9/jump  $emit-sib:loop/disp32
+2956 $emit-sib:check-for-base:
+2957     # if (has-metadata?(word-slice, "base"))
+2958     # . EAX = has-metadata?(ECX, "base")
+2959     # . . push args
+2960     68/push  "base"/imm32
+2961     51/push-ECX
+2962     # . . call
+2963     e8/call  has-metadata?/disp32
+2964     # . . discard args
+2965     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2966     # . if (EAX == 0) goto next check
+2967     3d/compare-EAX-and  0/imm32
+2968     74/jump-if-equal  $emit-sib:check-for-index/disp8
+2969 $emit-sib:base:
+2970     # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
+2971     # . EAX = parse-datum-of-word(word-slice)
+2972     # . . push args
+2973     51/push-ECX
+2974     # . . call
+2975     e8/call  parse-datum-of-word/disp32
+2976     # . . discard args
+2977     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2978     # . base = EAX
+2979     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
+2980     # has-sib? = true
+2981     ba/copy-to-EDX  1/imm32/true
+2982     # continue
+2983     e9/jump  $emit-sib:loop/disp32
+2984 $emit-sib:check-for-index:
+2985     # if (has-metadata?(word-slice, "index"))
+2986     # . EAX = has-metadata?(ECX, "index")
+2987     # . . push args
+2988     68/push  "index"/imm32
+2989     51/push-ECX
+2990     # . . call
+2991     e8/call  has-metadata?/disp32
+2992     # . . discard args
+2993     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2994     # . if (EAX == 0) loop
+2995     3d/compare-EAX-and  0/imm32
+2996     0f 84/jump-if-equal  $emit-sib:loop/disp32
+2997 $emit-sib:index:
+2998     # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
+2999     # . EAX = parse-datum-of-word(word-slice)
+3000     # . . push args
+3001     51/push-ECX
+3002     # . . call
+3003     e8/call  parse-datum-of-word/disp32
+3004     # . . discard args
+3005     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3006     # . index = EAX
+3007     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
+3008     # has-sib? = true
+3009     ba/copy-to-EDX  1/imm32/true
+3010     # continue
+3011     e9/jump  $emit-sib:loop/disp32
+3012 $emit-sib:break:
+3013     # if (!has-sib?) return
+3014     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
+3015     74/jump-if-equal  $emit-sib:end/disp8
+3016 $emit-sib:calculate:
+3017     # sib/EBX = scale & 0b11
+3018     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
+3019     # sib <<= 2
+3020     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
+3021     # sib |= index & 0b111
+3022     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
+3023     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
+3024     # sib <<= 3
+3025     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
+3026     # sib |= base & 0b111
+3027     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
+3028     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
+3029 $emit-sib:emit:
+3030     # emit-hex(out, sib, 1)
+3031     # . . push args
+3032     68/push  1/imm32
+3033     53/push-EBX
+3034     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3035     # . . call
+3036     e8/call  emit-hex/disp32
+3037     # . . discard args
+3038     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3039 $emit-sib:end:
+3040     # . restore locals
+3041     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3042     # . restore registers
+3043     5f/pop-to-EDI
+3044     5e/pop-to-ESI
+3045     5b/pop-to-EBX
+3046     5a/pop-to-EDX
+3047     59/pop-to-ECX
+3048     58/pop-to-EAX
+3049     # . epilog
+3050     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3051     5d/pop-to-EBP
+3052     c3/return
+3053 
+3054 emit-disp:  # line : (address stream byte), out : (address buffered-file) -> <void>
+3055     # pseudocode:
+3056     #   rewind-stream(line)
+3057     #   var word-slice = {0, 0}
+3058     #   while true
+3059     #     word-slice = next-word(line)
+3060     #     if (slice-empty?(word-slice)) break
+3061     #     if (slice-starts-with?(word-slice, "#")) break
+3062     #     if has-metadata?(word-slice, "disp32")
+3063     #       emit(out, word-slice, 4)
+3064     #       break
+3065     #     if has-metadata?(word-slice, "disp16")
+3066     #       emit(out, word-slice, 2)
+3067     #       break
+3068     #     if has-metadata?(word-slice, "disp8")
+3069     #       emit(out, word-slice, 1)
+3070     #       break
+3071     #
+3072     # . prolog
+3073     55/push-EBP
+3074     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3075     # . save registers
+3076     50/push-EAX
+3077     51/push-ECX
+3078     52/push-EDX
+3079     # var word-slice/ECX = {0, 0}
+3080     68/push  0/imm32/end
+3081     68/push  0/imm32/start
+3082     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+3083     # rewind-stream(line)
+3084     # . . push args
+3085     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3086     # . . call
+3087     e8/call  rewind-stream/disp32
+3088     # . . discard args
+3089     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3090 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+3124 $emit-disp:loop:
+3125     # next-word(line, word-slice)
+3126     # . . push args
+3127     51/push-ECX
+3128     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3129     # . . call
+3130     e8/call  next-word/disp32
+3131     # . . discard args
+3132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3133 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+3183 $emit-disp:check0:
+3184     # if (slice-empty?(word-slice)) break
+3185     # . EAX = slice-empty?(word-slice)
+3186     # . . push args
+3187     51/push-ECX
+3188     # . . call
+3189     e8/call  slice-empty?/disp32
+3190     # . . discard args
+3191     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3192     # . if (EAX != 0) pass through
+3193     3d/compare-EAX-and  0/imm32
+3194     0f 85/jump-if-not-equal  $emit-disp:break/disp32
+3195 $emit-disp:check1:
+3196     # if (slice-starts-with?(word-slice, "#")) break
+3197     # . start/EDX = word-slice->start
+3198     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+3199     # . c/EAX = *start
+3200     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+3201     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
+3202     # . if (EAX == '#') break
+3203     3d/compare-EAX-and  0x23/imm32/hash
+3204     0f 84/jump-if-equal  $emit-disp:break/disp32
+3205 $emit-disp:check-for-disp32:
+3206     # if (has-metadata?(word-slice, "disp32"))
+3207     # . EAX = has-metadata?(ECX, "disp32")
+3208     # . . push args
+3209     68/push  "disp32"/imm32
+3210     51/push-ECX
+3211     # . . call
+3212     e8/call  has-metadata?/disp32
+3213     # . . discard args
+3214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3215     # . if (EAX == 0) goto next check
+3216     3d/compare-EAX-and  0/imm32
+3217     74/jump-if-equal  $emit-disp:check-for-disp16/disp8
+3218 $emit-disp:disp32:
+3219     # emit(out, word-slice, 4)
+3220     # . . push args
+3221     68/push  4/imm32
+3222     51/push-ECX
+3223     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3224     # . . call
+3225     e8/call  emit/disp32
+3226     # . . discard args
+3227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3228     # break
+3229     e9/jump  $emit-disp:break/disp32
+3230 $emit-disp:check-for-disp16:
+3231     # else if (has-metadata?(word-slice, "disp16"))
+3232     # . EAX = has-metadata?(ECX, "disp16")
+3233     # . . push args
+3234     68/push  "disp16"/imm32
+3235     51/push-ECX
+3236     # . . call
+3237     e8/call  has-metadata?/disp32
+3238     # . . discard args
+3239     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3240     # . if (EAX == 0) goto next check
+3241     3d/compare-EAX-and  0/imm32
+3242     74/jump-if-equal  $emit-disp:check-for-disp8/disp8
+3243 $emit-disp:disp16:
+3244     # emit(out, word-slice, 2)
+3245     # . . push args
+3246     68/push  2/imm32
+3247     51/push-ECX
+3248     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3249     # . . call
+3250     e8/call  emit/disp32
+3251     # . . discard args
+3252     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3253     # break
+3254     e9/jump  $emit-disp:break/disp32
+3255 $emit-disp:check-for-disp8:
+3256     # if (has-metadata?(word-slice, "disp8"))
+3257     # . EAX = has-metadata?(ECX, "disp8")
+3258     # . . push args
+3259     68/push  "disp8"/imm32
+3260     51/push-ECX
+3261     # . . call
+3262     e8/call  has-metadata?/disp32
+3263     # . . discard args
+3264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3265     # . if (EAX == 0) loop
+3266     3d/compare-EAX-and  0/imm32
+3267     0f 84/jump-if-equal  $emit-disp:loop/disp32
+3268 $emit-disp:disp8:
+3269     # emit(out, word-slice, 1)
+3270     # . . push args
+3271     68/push  1/imm32
+3272     51/push-ECX
+3273     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3274     # . . call
+3275     e8/call  emit/disp32
+3276     # . . discard args
+3277     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3278     # break
+3279 $emit-disp:break:
+3280     # . restore locals
+3281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3282     # . restore registers
+3283     5a/pop-to-EDX
+3284     59/pop-to-ECX
+3285     58/pop-to-EAX
+3286     # . epilog
+3287     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3288     5d/pop-to-EBP
+3289     c3/return
+3290 
+3291 emit-imm:  # line : (address stream byte), out : (address buffered-file) -> <void>
+3292     # pseudocode:
+3293     #   rewind-stream(line)
+3294     #   var word-slice = {0, 0}
+3295     #   while true
+3296     #     word-slice = next-word(line)
+3297     #     if (slice-empty?(word-slice)) break
+3298     #     if (slice-starts-with?(word-slice, "#")) break
+3299     #     if has-metadata?(word-slice, "imm32")
+3300     #       emit(out, word-slice, 4)
+3301     #       break
+3302     #     if has-metadata?(word-slice, "imm16")
+3303     #       emit(out, word-slice, 2)
+3304     #       break
+3305     #     if has-metadata?(word-slice, "imm8")
+3306     #       emit(out, word-slice, 1)
+3307     #       break
+3308     #
+3309     # . prolog
+3310     55/push-EBP
+3311     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3312     # . save registers
+3313     50/push-EAX
+3314     51/push-ECX
+3315     52/push-EDX
+3316     # var word-slice/ECX = {0, 0}
+3317     68/push  0/imm32/end
+3318     68/push  0/imm32/start
+3319     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+3320     # rewind-stream(line)
+3321     # . . push args
+3322     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3323     # . . call
+3324     e8/call  rewind-stream/disp32
+3325     # . . discard args
+3326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3327 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
+3361 $emit-imm:loop:
+3362     # next-word(line, word-slice)
 3363     # . . push args
-3364     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
-3365     68/push  "34 12 "/imm32
-3366     68/push  _test-stream/imm32
-3367     # . . call
-3368     e8/call  check-stream-equal/disp32
-3369     # . . discard args
-3370     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3371     # . end
-3372     c3/return
-3373 
-3374 test-emit-hex-zero-pad:
-3375     # setup
-3376     # . clear-stream(_test-stream)
-3377     # . . push args
-3378     68/push  _test-stream/imm32
-3379     # . . call
-3380     e8/call  clear-stream/disp32
-3381     # . . discard args
-3382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3383     # . clear-stream(_test-buffered-file+4)
-3384     # . . push args
-3385     b8/copy-to-EAX  _test-buffered-file/imm32
-3386     05/add-to-EAX  4/imm32
-3387     50/push-EAX
-3388     # . . call
-3389     e8/call  clear-stream/disp32
-3390     # . . discard args
-3391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3392     # emit-hex(_test-buffered-file, 0xab, 2)
-3393     # . . push args
-3394     68/push  2/imm32
-3395     68/push  0xab/imm32
-3396     68/push  _test-buffered-file/imm32
-3397     # . . call
-3398     e8/call  emit-hex/disp32
-3399     # . . discard args
-3400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3401     # flush(_test-buffered-file)
-3402     # . . push args
-3403     68/push  _test-buffered-file/imm32
-3404     # . . call
-3405     e8/call  flush/disp32
-3406     # . . discard args
-3407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3408     # check(_test-stream->data == 'ab 00 ')
-3409     # . . push args
-3410     68/push  "F - test-emit-hex-zero-pad/1"/imm32
-3411     68/push  "ab 00 "/imm32
-3412     68/push  _test-stream/imm32
-3413     # . . call
-3414     e8/call  check-stream-equal/disp32
-3415     # . . discard args
-3416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3417     # . end
-3418     c3/return
-3419 
-3420 test-emit-hex-negative:
-3421     # setup
-3422     # . clear-stream(_test-stream)
+3364     51/push-ECX
+3365     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3366     # . . call
+3367     e8/call  next-word/disp32
+3368     # . . discard args
+3369     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3370 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+3420 $emit-imm:check0:
+3421     # if (slice-empty?(word-slice)) break
+3422     # . EAX = slice-empty?(word-slice)
 3423     # . . push args
-3424     68/push  _test-stream/imm32
+3424     51/push-ECX
 3425     # . . call
-3426     e8/call  clear-stream/disp32
+3426     e8/call  slice-empty?/disp32
 3427     # . . discard args
 3428     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3429     # . clear-stream(_test-buffered-file+4)
-3430     # . . push args
-3431     b8/copy-to-EAX  _test-buffered-file/imm32
-3432     05/add-to-EAX  4/imm32
-3433     50/push-EAX
-3434     # . . call
-3435     e8/call  clear-stream/disp32
-3436     # . . discard args
-3437     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3438     # emit-hex(_test-buffered-file, -1, 2)
-3439     # . . push args
-3440     68/push  2/imm32
-3441     68/push  -1/imm32
-3442     68/push  _test-buffered-file/imm32
-3443     # . . call
-3444     e8/call  emit-hex/disp32
-3445     # . . discard args
-3446     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3447     # flush(_test-buffered-file)
-3448     # . . push args
-3449     68/push  _test-buffered-file/imm32
-3450     # . . call
-3451     e8/call  flush/disp32
-3452     # . . discard args
-3453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3454     # check-stream-equal(_test-stream == "ff ff ")
-3455     # . . push args
-3456     68/push  "F - test-emit-hex-negative/1"/imm32
-3457     68/push  "ff ff "/imm32
-3458     68/push  _test-stream/imm32
-3459     # . . call
-3460     e8/call  check-stream-equal/disp32
-3461     # . . discard args
-3462     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3463     # . end
-3464     c3/return
-3465 
-3466 == data
-3467 
-3468 _test-slice-negative-two:
-3469     2d/- 32/2
-3470 _test-slice-negative-two-end:
-3471     2f/slash 66/f 6f/o 6f/o
-3472 _test-slice-negative-two-metadata-end:
-3473 
-3474 _test-slice-three-zero:
-3475     33/3 30/0
-3476 _test-slice-three-zero-end:
-3477 
-3478 _test-slice-non-number-word:
-3479     78/x 79/y 7a/z
-3480 _test-slice-non-number-word-end:
-3481     2f/slash
-3482 _test-slice-non-number-word-metadata-end:
-3483 
-3484 _test-input-stream:
-3485     # current write index
-3486     0/imm32
-3487     # current read index
-3488     0/imm32
-3489     # length
-3490     0x20/imm32
-3491     # data
-3492     00 00 00 00 00 00 00 00  # 8 bytes
-3493     00 00 00 00 00 00 00 00  # 8 bytes
-3494     00 00 00 00 00 00 00 00  # 8 bytes
-3495     00 00 00 00 00 00 00 00  # 8 bytes
-3496 
-3497 # a test buffered file for _test-input-stream
-3498 _test-input-buffered-file:
-3499     # file descriptor or (address stream)
-3500     _test-input-stream/imm32
-3501     # current write index
-3502     0/imm32
-3503     # current read index
-3504     0/imm32
-3505     # length
-3506     6/imm32
-3507     # data
-3508     00 00 00 00 00 00  # 6 bytes
-3509 
-3510 _test-output-stream:
-3511     # current write index
-3512     0/imm32
-3513     # current read index
-3514     0/imm32
-3515     # length
-3516     0x20/imm32
-3517     # data
-3518     00 00 00 00 00 00 00 00  # 8 bytes
-3519     00 00 00 00 00 00 00 00  # 8 bytes
-3520     00 00 00 00 00 00 00 00  # 8 bytes
-3521     00 00 00 00 00 00 00 00  # 8 bytes
-3522 
-3523 # a test buffered file for _test-output-stream
-3524 _test-output-buffered-file:
-3525     # file descriptor or (address stream)
-3526     _test-output-stream/imm32
-3527     # current write index
-3528     0/imm32
-3529     # current read index
-3530     0/imm32
-3531     # length
-3532     6/imm32
-3533     # data
-3534     00 00 00 00 00 00  # 6 bytes
-3535 
-3536 _test-slice-hexlike-non-number-word:
-3537     61/a 62/b 63/c 64/d
-3538 _test-slice-hexlike-non-number-word-end:
-3539     2f/slash
-3540     78/x 79/y 7a/z
-3541 _test-slice-hexlike-non-number-word-metadata-end:
-3542 
-3543 _test-slice-with-slash-prefix:
-3544   2f/slash 30/0 33/3
-3545 _test-slice-with-slash-prefix-end:
-3546 
-3547 # . . vim:nowrap:textwidth=0
+3429     # . if (EAX != 0) pass through
+3430     3d/compare-EAX-and  0/imm32
+3431     0f 85/jump-if-not-equal  $emit-imm:break/disp32
+3432 $emit-imm:check1:
+3433     # if (slice-starts-with?(word-slice, "#")) break
+3434     # . start/EDX = slice->start
+3435     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+3436     # . c/EAX = *start
+3437     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+3438     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
+3439     # . if (EAX == '#') break
+3440     3d/compare-EAX-and  0x23/imm32/hash
+3441     0f 84/jump-if-equal  $emit-imm:break/disp32
+3442 $emit-imm:check-for-imm32:
+3443     # if (has-metadata?(word-slice, "imm32"))
+3444     # . EAX = has-metadata?(ECX, "imm32")
+3445     # . . push args
+3446     68/push  "imm32"/imm32
+3447     51/push-ECX
+3448     # . . call
+3449     e8/call  has-metadata?/disp32
+3450     # . . discard args
+3451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3452     # . if (EAX == 0) goto next check
+3453     3d/compare-EAX-and  0/imm32
+3454     74/jump-if-equal  $emit-imm:check-for-imm16/disp8
+3455 $emit-imm:imm32:
+3456     # emit(out, word-slice, 4)
+3457     # . . push args
+3458     68/push  4/imm32
+3459     51/push-ECX
+3460     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3461     # . . call
+3462     e8/call  emit/disp32
+3463     # . . discard args
+3464     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3465     # break
+3466     e9/jump  $emit-imm:break/disp32
+3467 $emit-imm:check-for-imm16:
+3468     # if (has-metadata?(word-slice, "imm16"))
+3469     # . EAX = has-metadata?(ECX, "imm16")
+3470     # . . push args
+3471     68/push  "imm16"/imm32
+3472     51/push-ECX
+3473     # . . call
+3474     e8/call  has-metadata?/disp32
+3475     # . . discard args
+3476     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3477     # . if (EAX == 0) goto next check
+3478     3d/compare-EAX-and  0/imm32
+3479     74/jump-if-equal  $emit-imm:check-for-imm8/disp8
+3480 $emit-imm:imm16:
+3481     # emit(out, word-slice, 2)
+3482     # . . push args
+3483     68/push  2/imm32
+3484     51/push-ECX
+3485     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3486     # . . call
+3487     e8/call  emit/disp32
+3488     # . . discard args
+3489     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3490     # break
+3491     e9/jump  $emit-imm:break/disp32
+3492 $emit-imm:check-for-imm8:
+3493     # if (has-metadata?(word-slice, "imm8"))
+3494     # . EAX = has-metadata?(ECX, "imm8")
+3495     # . . push args
+3496     68/push  "imm8"/imm32
+3497     51/push-ECX
+3498     # . . call
+3499     e8/call  has-metadata?/disp32
+3500     # . . discard args
+3501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3502     # . if (EAX == 0) loop
+3503     3d/compare-EAX-and  0/imm32
+3504     0f 84/jump-if-equal  $emit-imm:loop/disp32
+3505 $emit-imm:imm8:
+3506     # emit(out, word-slice, 1)
+3507     # . . push args
+3508     68/push  1/imm32
+3509     51/push-ECX
+3510     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3511     # . . call
+3512     e8/call  emit/disp32
+3513     # . . discard args
+3514     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3515     # break
+3516 $emit-imm:break:
+3517     # . restore locals
+3518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3519     # . restore registers
+3520     5a/pop-to-EDX
+3521     59/pop-to-ECX
+3522     58/pop-to-EAX
+3523     # . epilog
+3524     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3525     5d/pop-to-EBP
+3526     c3/return
+3527 
+3528 emit-line-in-comment:  # line : (address stream byte), out : (address buffered-file) -> <void>
+3529     # . prolog
+3530     55/push-EBP
+3531     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3532     # write-buffered(out, " # ")
+3533     # . . push args
+3534     68/push  " # "/imm32
+3535     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3536     # . . call
+3537     e8/call  write-buffered/disp32
+3538     # . . discard args
+3539     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3540     # write-stream-data(out, line)
+3541     # . . push args
+3542     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3543     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3544     # . . call
+3545     e8/call  write-stream-data/disp32
+3546     # . . discard args
+3547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3548 $emit-line-in-comment:end:
+3549     # . epilog
+3550     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3551     5d/pop-to-EBP
+3552     c3/return
+3553 
+3554 test-convert-instruction-passes-comments-through:
+3555     # if a line starts with '#', pass it along unchanged
+3556     # . prolog
+3557     55/push-EBP
+3558     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3559     # setup
+3560     # . clear-stream(_test-input-stream)
+3561     # . . push args
+3562     68/push  _test-input-stream/imm32
+3563     # . . call
+3564     e8/call  clear-stream/disp32
+3565     # . . discard args
+3566     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3567     # . clear-stream(_test-output-stream)
+3568     # . . push args
+3569     68/push  _test-output-stream/imm32
+3570     # . . call
+3571     e8/call  clear-stream/disp32
+3572     # . . discard args
+3573     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3574     # . clear-stream(_test-output-buffered-file+4)
+3575     # . . push args
+3576     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3577     05/add-to-EAX  4/imm32
+3578     50/push-EAX
+3579     # . . call
+3580     e8/call  clear-stream/disp32
+3581     # . . discard args
+3582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3583     # initialize input
+3584     # . write(_test-input-stream, "# abcd")
+3585     # . . push args
+3586     68/push  "# abcd"/imm32
+3587     68/push  _test-input-stream/imm32
+3588     # . . call
+3589     e8/call  write/disp32
+3590     # . . discard args
+3591     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3592     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+3593     # . . push args
+3594     68/push  _test-output-buffered-file/imm32
+3595     68/push  _test-input-stream/imm32
+3596     # . . call
+3597     e8/call  convert-instruction/disp32
+3598     # . . discard args
+3599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3600     # check that the line just passed through
+3601     # . flush(_test-output-buffered-file)
+3602     # . . push args
+3603     68/push  _test-output-buffered-file/imm32
+3604     # . . call
+3605     e8/call  flush/disp32
+3606     # . . discard args
+3607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3608     # . check-stream-equal(_test-output-stream, "# abcd", msg)
+3609     # . . push args
+3610     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
+3611     68/push  "# abcd"/imm32
+3612     68/push  _test-output-stream/imm32
+3613     # . . call
+3614     e8/call  check-stream-equal/disp32
+3615     # . . discard args
+3616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3617     # . epilog
+3618     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3619     5d/pop-to-EBP
+3620     c3/return
+3621 
+3622 test-convert-instruction-passes-labels-through:
+3623     # if the first word ends with ':', pass along the entire line unchanged
+3624     # . prolog
+3625     55/push-EBP
+3626     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3627     # setup
+3628     # . clear-stream(_test-input-stream)
+3629     # . . push args
+3630     68/push  _test-input-stream/imm32
+3631     # . . call
+3632     e8/call  clear-stream/disp32
+3633     # . . discard args
+3634     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3635     # . clear-stream(_test-output-stream)
+3636     # . . push args
+3637     68/push  _test-output-stream/imm32
+3638     # . . call
+3639     e8/call  clear-stream/disp32
+3640     # . . discard args
+3641     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3642     # . clear-stream(_test-output-buffered-file+4)
+3643     # . . push args
+3644     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3645     05/add-to-EAX  4/imm32
+3646     50/push-EAX
+3647     # . . call
+3648     e8/call  clear-stream/disp32
+3649     # . . discard args
+3650     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3651     # initialize input
+3652     # . write(_test-input-stream, "ab: # cd")
+3653     # . . push args
+3654     68/push  "ab: # cd"/imm32
+3655     68/push  _test-input-stream/imm32
+3656     # . . call
+3657     e8/call  write/disp32
+3658     # . . discard args
+3659     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3660     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+3661     # . . push args
+3662     68/push  _test-output-buffered-file/imm32
+3663     68/push  _test-input-stream/imm32
+3664     # . . call
+3665     e8/call  convert-instruction/disp32
+3666     # . . discard args
+3667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3668     # check that the line just passed through
+3669     # . flush(_test-output-buffered-file)
+3670     # . . push args
+3671     68/push  _test-output-buffered-file/imm32
+3672     # . . call
+3673     e8/call  flush/disp32
+3674     # . . discard args
+3675     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3676     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
+3677     # . . push args
+3678     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
+3679     68/push  "ab: # cd"/imm32
+3680     68/push  _test-output-stream/imm32
+3681     # . . call
+3682     e8/call  check-stream-equal/disp32
+3683     # . . discard args
+3684     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3685     # . epilog
+3686     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3687     5d/pop-to-EBP
+3688     c3/return
+3689 
+3690 test-convert-instruction-handles-single-opcode:
+3691     # if the instruction consists of a single opcode, strip its metadata and pass it along
+3692     # . prolog
+3693     55/push-EBP
+3694     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3695     # setup
+3696     # . clear-stream(_test-input-stream)
+3697     # . . push args
+3698     68/push  _test-input-stream/imm32
+3699     # . . call
+3700     e8/call  clear-stream/disp32
+3701     # . . discard args
+3702     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3703     # . clear-stream(_test-output-stream)
+3704     # . . push args
+3705     68/push  _test-output-stream/imm32
+3706     # . . call
+3707     e8/call  clear-stream/disp32
+3708     # . . discard args
+3709     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3710     # . clear-stream(_test-output-buffered-file+4)
+3711     # . . push args
+3712     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3713     05/add-to-EAX  4/imm32
+3714     50/push-EAX
+3715     # . . call
+3716     e8/call  clear-stream/disp32
+3717     # . . discard args
+3718     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3719     # initialize input
+3720     # . write(_test-input-stream, "ab/cd # comment")
+3721     # . . push args
+3722     68/push  "ab/cd # comment"/imm32
+3723     68/push  _test-input-stream/imm32
+3724     # . . call
+3725     e8/call  write/disp32
+3726     # . . discard args
+3727     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3728     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+3729     # . . push args
+3730     68/push  _test-output-buffered-file/imm32
+3731     68/push  _test-input-stream/imm32
+3732     # . . call
+3733     e8/call  convert-instruction/disp32
+3734     # . . discard args
+3735     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3736     # check output
+3737     # . flush(_test-output-buffered-file)
+3738     # . . push args
+3739     68/push  _test-output-buffered-file/imm32
+3740     # . . call
+3741     e8/call  flush/disp32
+3742     # . . discard args
+3743     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3744 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+3778     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
+3779     # . . push args
+3780     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
+3781     68/push  "ab  # ab/cd # comment"/imm32
+3782     68/push  _test-output-stream/imm32
+3783     # . . call
+3784     e8/call  check-stream-equal/disp32
+3785     # . . discard args
+3786     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3787     # . epilog
+3788     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3789     5d/pop-to-EBP
+3790     c3/return
+3791 
+3792 test-convert-instruction-handles-0f-opcode:
+3793     # if the instruction starts with 0f opcode, include a second opcode
+3794     # . prolog
+3795     55/push-EBP
+3796     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3797     # setup
+3798     # . clear-stream(_test-input-stream)
+3799     # . . push args
+3800     68/push  _test-input-stream/imm32
+3801     # . . call
+3802     e8/call  clear-stream/disp32
+3803     # . . discard args
+3804     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3805     # . clear-stream(_test-output-stream)
+3806     # . . push args
+3807     68/push  _test-output-stream/imm32
+3808     # . . call
+3809     e8/call  clear-stream/disp32
+3810     # . . discard args
+3811     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3812     # . clear-stream(_test-output-buffered-file+4)
+3813     # . . push args
+3814     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3815     05/add-to-EAX  4/imm32
+3816     50/push-EAX
+3817     # . . call
+3818     e8/call  clear-stream/disp32
+3819     # . . discard args
+3820     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3821     # initialize input
+3822     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
+3823     # . . push args
+3824     68/push  "0f/m1 ab/m2 # comment"/imm32
+3825     68/push  _test-input-stream/imm32
+3826     # . . call
+3827     e8/call  write/disp32
+3828     # . . discard args
+3829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3830     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+3831     # . . push args
+3832     68/push  _test-output-buffered-file/imm32
+3833     68/push  _test-input-stream/imm32
+3834     # . . call
+3835     e8/call  convert-instruction/disp32
+3836     # . . discard args
+3837     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3838     # check output
+3839     # . flush(_test-output-buffered-file)
+3840     # . . push args
+3841     68/push  _test-output-buffered-file/imm32
+3842     # . . call
+3843     e8/call  flush/disp32
+3844     # . . discard args
+3845     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3846 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+3880     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
+3881     # . . push args
+3882     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
+3883     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
+3884     68/push  _test-output-stream/imm32
+3885     # . . call
+3886     e8/call  check-stream-equal/disp32
+3887     # . . discard args
+3888     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3889     # . epilog
+3890     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3891     5d/pop-to-EBP
+3892     c3/return
+3893 
+3894 test-convert-instruction-handles-f2-opcode:
+3895     # if the instruction starts with f2 opcode, include a second opcode
+3896     # . prolog
+3897     55/push-EBP
+3898     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3899     # setup
+3900     # . clear-stream(_test-input-stream)
+3901     # . . push args
+3902     68/push  _test-input-stream/imm32
+3903     # . . call
+3904     e8/call  clear-stream/disp32
+3905     # . . discard args
+3906     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3907     # . clear-stream(_test-output-stream)
+3908     # . . push args
+3909     68/push  _test-output-stream/imm32
+3910     # . . call
+3911     e8/call  clear-stream/disp32
+3912     # . . discard args
+3913     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3914     # . clear-stream(_test-output-buffered-file+4)
+3915     # . . push args
+3916     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3917     05/add-to-EAX  4/imm32
+3918     50/push-EAX
+3919     # . . call
+3920     e8/call  clear-stream/disp32
+3921     # . . discard args
+3922     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3923     # initialize input
+3924     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
+3925     # . . push args
+3926     68/push  "f2/m1 ab/m2 # comment"/imm32
+3927     68/push  _test-input-stream/imm32
+3928     # . . call
+3929     e8/call  write/disp32
+3930     # . . discard args
+3931     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3932     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+3933     # . . push args
+3934     68/push  _test-output-buffered-file/imm32
+3935     68/push  _test-input-stream/imm32
+3936     # . . call
+3937     e8/call  convert-instruction/disp32
+3938     # . . discard args
+3939     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3940     # check output
+3941     # . flush(_test-output-buffered-file)
+3942     # . . push args
+3943     68/push  _test-output-buffered-file/imm32
+3944     # . . call
+3945     e8/call  flush/disp32
+3946     # . . discard args
+3947     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3948 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+3982     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
+3983     # . . push args
+3984     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
+3985     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
+3986     68/push  _test-output-stream/imm32
+3987     # . . call
+3988     e8/call  check-stream-equal/disp32
+3989     # . . discard args
+3990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3991     # . epilog
+3992     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3993     5d/pop-to-EBP
+3994     c3/return
+3995 
+3996 test-convert-instruction-handles-f3-opcode:
+3997     # if the instruction starts with f3 opcode, include a second opcode
+3998     # . prolog
+3999     55/push-EBP
+4000     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4001     # setup
+4002     # . clear-stream(_test-input-stream)
+4003     # . . push args
+4004     68/push  _test-input-stream/imm32
+4005     # . . call
+4006     e8/call  clear-stream/disp32
+4007     # . . discard args
+4008     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4009     # . clear-stream(_test-output-stream)
+4010     # . . push args
+4011     68/push  _test-output-stream/imm32
+4012     # . . call
+4013     e8/call  clear-stream/disp32
+4014     # . . discard args
+4015     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4016     # . clear-stream(_test-output-buffered-file+4)
+4017     # . . push args
+4018     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4019     05/add-to-EAX  4/imm32
+4020     50/push-EAX
+4021     # . . call
+4022     e8/call  clear-stream/disp32
+4023     # . . discard args
+4024     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4025     # initialize input
+4026     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
+4027     # . . push args
+4028     68/push  "f3/m1 ab/m2 # comment"/imm32
+4029     68/push  _test-input-stream/imm32
+4030     # . . call
+4031     e8/call  write/disp32
+4032     # . . discard args
+4033     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4034     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4035     # . . push args
+4036     68/push  _test-output-buffered-file/imm32
+4037     68/push  _test-input-stream/imm32
+4038     # . . call
+4039     e8/call  convert-instruction/disp32
+4040     # . . discard args
+4041     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4042     # check output
+4043     # . flush(_test-output-buffered-file)
+4044     # . . push args
+4045     68/push  _test-output-buffered-file/imm32
+4046     # . . call
+4047     e8/call  flush/disp32
+4048     # . . discard args
+4049     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4050 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4084     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
+4085     # . . push args
+4086     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
+4087     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
+4088     68/push  _test-output-stream/imm32
+4089     # . . call
+4090     e8/call  check-stream-equal/disp32
+4091     # . . discard args
+4092     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4093     # . epilog
+4094     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4095     5d/pop-to-EBP
+4096     c3/return
+4097 
+4098 test-convert-instruction-handles-f2-0f-opcode:
+4099     # if the instruction starts with f2 0f opcode, include a second opcode
+4100     # . prolog
+4101     55/push-EBP
+4102     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4103     # setup
+4104     # . clear-stream(_test-input-stream)
+4105     # . . push args
+4106     68/push  _test-input-stream/imm32
+4107     # . . call
+4108     e8/call  clear-stream/disp32
+4109     # . . discard args
+4110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4111     # . clear-stream(_test-output-stream)
+4112     # . . push args
+4113     68/push  _test-output-stream/imm32
+4114     # . . call
+4115     e8/call  clear-stream/disp32
+4116     # . . discard args
+4117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4118     # . clear-stream(_test-output-buffered-file+4)
+4119     # . . push args
+4120     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4121     05/add-to-EAX  4/imm32
+4122     50/push-EAX
+4123     # . . call
+4124     e8/call  clear-stream/disp32
+4125     # . . discard args
+4126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4127     # initialize input
+4128     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
+4129     # . . push args
+4130     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
+4131     68/push  _test-input-stream/imm32
+4132     # . . call
+4133     e8/call  write/disp32
+4134     # . . discard args
+4135     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4136     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4137     # . . push args
+4138     68/push  _test-output-buffered-file/imm32
+4139     68/push  _test-input-stream/imm32
+4140     # . . call
+4141     e8/call  convert-instruction/disp32
+4142     # . . discard args
+4143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4144     # check output
+4145     # . flush(_test-output-buffered-file)
+4146     # . . push args
+4147     68/push  _test-output-buffered-file/imm32
+4148     # . . call
+4149     e8/call  flush/disp32
+4150     # . . discard args
+4151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4152 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4186     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
+4187     # . . push args
+4188     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
+4189     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
+4190     68/push  _test-output-stream/imm32
+4191     # . . call
+4192     e8/call  check-stream-equal/disp32
+4193     # . . discard args
+4194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4195     # . epilog
+4196     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4197     5d/pop-to-EBP
+4198     c3/return
+4199 
+4200 test-convert-instruction-handles-f3-0f-opcode:
+4201     # if the instruction starts with f3 0f opcode, include a second opcode
+4202     # . prolog
+4203     55/push-EBP
+4204     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4205     # setup
+4206     # . clear-stream(_test-input-stream)
+4207     # . . push args
+4208     68/push  _test-input-stream/imm32
+4209     # . . call
+4210     e8/call  clear-stream/disp32
+4211     # . . discard args
+4212     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4213     # . clear-stream(_test-output-stream)
+4214     # . . push args
+4215     68/push  _test-output-stream/imm32
+4216     # . . call
+4217     e8/call  clear-stream/disp32
+4218     # . . discard args
+4219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4220     # . clear-stream(_test-output-buffered-file+4)
+4221     # . . push args
+4222     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4223     05/add-to-EAX  4/imm32
+4224     50/push-EAX
+4225     # . . call
+4226     e8/call  clear-stream/disp32
+4227     # . . discard args
+4228     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4229     # initialize input
+4230     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
+4231     # . . push args
+4232     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
+4233     68/push  _test-input-stream/imm32
+4234     # . . call
+4235     e8/call  write/disp32
+4236     # . . discard args
+4237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4238     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4239     # . . push args
+4240     68/push  _test-output-buffered-file/imm32
+4241     68/push  _test-input-stream/imm32
+4242     # . . call
+4243     e8/call  convert-instruction/disp32
+4244     # . . discard args
+4245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4246     # check output
+4247     # . flush(_test-output-buffered-file)
+4248     # . . push args
+4249     68/push  _test-output-buffered-file/imm32
+4250     # . . call
+4251     e8/call  flush/disp32
+4252     # . . discard args
+4253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4254 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4288     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
+4289     # . . push args
+4290     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
+4291     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
+4292     68/push  _test-output-stream/imm32
+4293     # . . call
+4294     e8/call  check-stream-equal/disp32
+4295     # . . discard args
+4296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4297     # . epilog
+4298     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4299     5d/pop-to-EBP
+4300     c3/return
+4301 
+4302 test-convert-instruction-handles-unused-opcodes:
+4303     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
+4304     # . prolog
+4305     55/push-EBP
+4306     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4307     # setup
+4308     # . clear-stream(_test-input-stream)
+4309     # . . push args
+4310     68/push  _test-input-stream/imm32
+4311     # . . call
+4312     e8/call  clear-stream/disp32
+4313     # . . discard args
+4314     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4315     # . clear-stream(_test-output-stream)
+4316     # . . push args
+4317     68/push  _test-output-stream/imm32
+4318     # . . call
+4319     e8/call  clear-stream/disp32
+4320     # . . discard args
+4321     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4322     # . clear-stream(_test-output-buffered-file+4)
+4323     # . . push args
+4324     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4325     05/add-to-EAX  4/imm32
+4326     50/push-EAX
+4327     # . . call
+4328     e8/call  clear-stream/disp32
+4329     # . . discard args
+4330     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4331     # initialize input
+4332     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
+4333     # . . push args
+4334     68/push  "ab/m1 cd/m2 # comment"/imm32
+4335     68/push  _test-input-stream/imm32
+4336     # . . call
+4337     e8/call  write/disp32
+4338     # . . discard args
+4339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4340     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4341     # . . push args
+4342     68/push  _test-output-buffered-file/imm32
+4343     68/push  _test-input-stream/imm32
+4344     # . . call
+4345     e8/call  convert-instruction/disp32
+4346     # . . discard args
+4347     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4348     # check output
+4349     # . flush(_test-output-buffered-file)
+4350     # . . push args
+4351     68/push  _test-output-buffered-file/imm32
+4352     # . . call
+4353     e8/call  flush/disp32
+4354     # . . discard args
+4355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4356 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4390     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
+4391     # . . push args
+4392     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
+4393     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
+4394     68/push  _test-output-stream/imm32
+4395     # . . call
+4396     e8/call  check-stream-equal/disp32
+4397     # . . discard args
+4398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4399     # . epilog
+4400     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4401     5d/pop-to-EBP
+4402     c3/return
+4403 
+4404 test-convert-instruction-handles-unused-second-opcodes:
+4405     # if the second opcode isn't 0f, don't include further opcodes
+4406     # . prolog
+4407     55/push-EBP
+4408     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4409     # setup
+4410     # . clear-stream(_test-input-stream)
+4411     # . . push args
+4412     68/push  _test-input-stream/imm32
+4413     # . . call
+4414     e8/call  clear-stream/disp32
+4415     # . . discard args
+4416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4417     # . clear-stream(_test-output-stream)
+4418     # . . push args
+4419     68/push  _test-output-stream/imm32
+4420     # . . call
+4421     e8/call  clear-stream/disp32
+4422     # . . discard args
+4423     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4424     # . clear-stream(_test-output-buffered-file+4)
+4425     # . . push args
+4426     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4427     05/add-to-EAX  4/imm32
+4428     50/push-EAX
+4429     # . . call
+4430     e8/call  clear-stream/disp32
+4431     # . . discard args
+4432     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4433     # initialize input
+4434     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
+4435     # . . push args
+4436     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
+4437     68/push  _test-input-stream/imm32
+4438     # . . call
+4439     e8/call  write/disp32
+4440     # . . discard args
+4441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4442     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4443     # . . push args
+4444     68/push  _test-output-buffered-file/imm32
+4445     68/push  _test-input-stream/imm32
+4446     # . . call
+4447     e8/call  convert-instruction/disp32
+4448     # . . discard args
+4449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4450     # check output
+4451     # . flush(_test-output-buffered-file)
+4452     # . . push args
+4453     68/push  _test-output-buffered-file/imm32
+4454     # . . call
+4455     e8/call  flush/disp32
+4456     # . . discard args
+4457     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4458 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4492     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
+4493     # . . push args
+4494     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
+4495     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
+4496     68/push  _test-output-stream/imm32
+4497     # . . call
+4498     e8/call  check-stream-equal/disp32
+4499     # . . discard args
+4500     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4501     # . epilog
+4502     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4503     5d/pop-to-EBP
+4504     c3/return
+4505 
+4506 test-convert-instruction-handles-unused-second-opcodes-2:
+4507     # if the second opcode isn't 0f, don't include further opcodes
+4508     # . prolog
+4509     55/push-EBP
+4510     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4511     # setup
+4512     # . clear-stream(_test-input-stream)
+4513     # . . push args
+4514     68/push  _test-input-stream/imm32
+4515     # . . call
+4516     e8/call  clear-stream/disp32
+4517     # . . discard args
+4518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4519     # . clear-stream(_test-output-stream)
+4520     # . . push args
+4521     68/push  _test-output-stream/imm32
+4522     # . . call
+4523     e8/call  clear-stream/disp32
+4524     # . . discard args
+4525     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4526     # . clear-stream(_test-output-buffered-file+4)
+4527     # . . push args
+4528     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4529     05/add-to-EAX  4/imm32
+4530     50/push-EAX
+4531     # . . call
+4532     e8/call  clear-stream/disp32
+4533     # . . discard args
+4534     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4535     # initialize input
+4536     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
+4537     # . . push args
+4538     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
+4539     68/push  _test-input-stream/imm32
+4540     # . . call
+4541     e8/call  write/disp32
+4542     # . . discard args
+4543     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4544     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4545     # . . push args
+4546     68/push  _test-output-buffered-file/imm32
+4547     68/push  _test-input-stream/imm32
+4548     # . . call
+4549     e8/call  convert-instruction/disp32
+4550     # . . discard args
+4551     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4552     # check output
+4553     # . flush(_test-output-buffered-file)
+4554     # . . push args
+4555     68/push  _test-output-buffered-file/imm32
+4556     # . . call
+4557     e8/call  flush/disp32
+4558     # . . discard args
+4559     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4560 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4594     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
+4595     # . . push args
+4596     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
+4597     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
+4598     68/push  _test-output-stream/imm32
+4599     # . . call
+4600     e8/call  check-stream-equal/disp32
+4601     # . . discard args
+4602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4603     # . epilog
+4604     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4605     5d/pop-to-EBP
+4606     c3/return
+4607 
+4608 test-convert-instruction-emits-modrm-byte:
+4609     # pack mod, rm32 and r32 operands into ModR/M byte
+4610     # . prolog
+4611     55/push-EBP
+4612     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4613     # setup
+4614     # . clear-stream(_test-input-stream)
+4615     # . . push args
+4616     68/push  _test-input-stream/imm32
+4617     # . . call
+4618     e8/call  clear-stream/disp32
+4619     # . . discard args
+4620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4621     # . clear-stream(_test-output-stream)
+4622     # . . push args
+4623     68/push  _test-output-stream/imm32
+4624     # . . call
+4625     e8/call  clear-stream/disp32
+4626     # . . discard args
+4627     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4628     # . clear-stream(_test-output-buffered-file+4)
+4629     # . . push args
+4630     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4631     05/add-to-EAX  4/imm32
+4632     50/push-EAX
+4633     # . . call
+4634     e8/call  clear-stream/disp32
+4635     # . . discard args
+4636     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4637     # initialize input
+4638     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
+4639     # . . push args
+4640     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
+4641     68/push  _test-input-stream/imm32
+4642     # . . call
+4643     e8/call  write/disp32
+4644     # . . discard args
+4645     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4646     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4647     # . . push args
+4648     68/push  _test-output-buffered-file/imm32
+4649     68/push  _test-input-stream/imm32
+4650     # . . call
+4651     e8/call  convert-instruction/disp32
+4652     # . . discard args
+4653     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4654     # check output
+4655     # . flush(_test-output-buffered-file)
+4656     # . . push args
+4657     68/push  _test-output-buffered-file/imm32
+4658     # . . call
+4659     e8/call  flush/disp32
+4660     # . . discard args
+4661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4662 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4696     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
+4697     # . . push args
+4698     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
+4699     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
+4700     68/push  _test-output-stream/imm32
+4701     # . . call
+4702     e8/call  check-stream-equal/disp32
+4703     # . . discard args
+4704     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4705     # . epilog
+4706     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4707     5d/pop-to-EBP
+4708     c3/return
+4709 
+4710 test-convert-instruction-emits-modrm-byte-from-subop:
+4711     # pack mod, rm32 and subop operands into ModR/M byte
+4712     # . prolog
+4713     55/push-EBP
+4714     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4715     # setup
+4716     # . clear-stream(_test-input-stream)
+4717     # . . push args
+4718     68/push  _test-input-stream/imm32
+4719     # . . call
+4720     e8/call  clear-stream/disp32
+4721     # . . discard args
+4722     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4723     # . clear-stream(_test-output-stream)
+4724     # . . push args
+4725     68/push  _test-output-stream/imm32
+4726     # . . call
+4727     e8/call  clear-stream/disp32
+4728     # . . discard args
+4729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4730     # . clear-stream(_test-output-buffered-file+4)
+4731     # . . push args
+4732     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4733     05/add-to-EAX  4/imm32
+4734     50/push-EAX
+4735     # . . call
+4736     e8/call  clear-stream/disp32
+4737     # . . discard args
+4738     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4739     # initialize input
+4740     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
+4741     # . . push args
+4742     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
+4743     68/push  _test-input-stream/imm32
+4744     # . . call
+4745     e8/call  write/disp32
+4746     # . . discard args
+4747     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4748     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4749     # . . push args
+4750     68/push  _test-output-buffered-file/imm32
+4751     68/push  _test-input-stream/imm32
+4752     # . . call
+4753     e8/call  convert-instruction/disp32
+4754     # . . discard args
+4755     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4756     # check output
+4757     # . flush(_test-output-buffered-file)
+4758     # . . push args
+4759     68/push  _test-output-buffered-file/imm32
+4760     # . . call
+4761     e8/call  flush/disp32
+4762     # . . discard args
+4763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4764 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4798     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
+4799     # . . push args
+4800     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
+4801     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
+4802     68/push  _test-output-stream/imm32
+4803     # . . call
+4804     e8/call  check-stream-equal/disp32
+4805     # . . discard args
+4806     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4807     # . epilog
+4808     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4809     5d/pop-to-EBP
+4810     c3/return
+4811 
+4812 test-convert-instruction-emits-modrm-byte-with-missing-mod:
+4813     # pack rm32 and r32 operands into ModR/M byte
+4814     # . prolog
+4815     55/push-EBP
+4816     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4817     # setup
+4818     # . clear-stream(_test-input-stream)
+4819     # . . push args
+4820     68/push  _test-input-stream/imm32
+4821     # . . call
+4822     e8/call  clear-stream/disp32
+4823     # . . discard args
+4824     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4825     # . clear-stream(_test-output-stream)
+4826     # . . push args
+4827     68/push  _test-output-stream/imm32
+4828     # . . call
+4829     e8/call  clear-stream/disp32
+4830     # . . discard args
+4831     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4832     # . clear-stream(_test-output-buffered-file+4)
+4833     # . . push args
+4834     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4835     05/add-to-EAX  4/imm32
+4836     50/push-EAX
+4837     # . . call
+4838     e8/call  clear-stream/disp32
+4839     # . . discard args
+4840     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4841     # initialize input
+4842     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
+4843     # . . push args
+4844     68/push  "8b/copy 0/rm32 1/r32"/imm32
+4845     68/push  _test-input-stream/imm32
+4846     # . . call
+4847     e8/call  write/disp32
+4848     # . . discard args
+4849     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4850     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4851     # . . push args
+4852     68/push  _test-output-buffered-file/imm32
+4853     68/push  _test-input-stream/imm32
+4854     # . . call
+4855     e8/call  convert-instruction/disp32
+4856     # . . discard args
+4857     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4858     # check output
+4859     # . flush(_test-output-buffered-file)
+4860     # . . push args
+4861     68/push  _test-output-buffered-file/imm32
+4862     # . . call
+4863     e8/call  flush/disp32
+4864     # . . discard args
+4865     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4866 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+4900     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
+4901     # . . push args
+4902     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
+4903     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
+4904     68/push  _test-output-stream/imm32
+4905     # . . call
+4906     e8/call  check-stream-equal/disp32
+4907     # . . discard args
+4908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4909     # . epilog
+4910     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4911     5d/pop-to-EBP
+4912     c3/return
+4913 
+4914 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
+4915     # pack mod and r32 operands into ModR/M byte
+4916     # . prolog
+4917     55/push-EBP
+4918     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4919     # setup
+4920     # . clear-stream(_test-input-stream)
+4921     # . . push args
+4922     68/push  _test-input-stream/imm32
+4923     # . . call
+4924     e8/call  clear-stream/disp32
+4925     # . . discard args
+4926     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4927     # . clear-stream(_test-output-stream)
+4928     # . . push args
+4929     68/push  _test-output-stream/imm32
+4930     # . . call
+4931     e8/call  clear-stream/disp32
+4932     # . . discard args
+4933     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4934     # . clear-stream(_test-output-buffered-file+4)
+4935     # . . push args
+4936     b8/copy-to-EAX  _test-output-buffered-file/imm32
+4937     05/add-to-EAX  4/imm32
+4938     50/push-EAX
+4939     # . . call
+4940     e8/call  clear-stream/disp32
+4941     # . . discard args
+4942     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4943     # initialize input
+4944     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
+4945     # . . push args
+4946     68/push  "8b/copy 0/mod 1/r32"/imm32
+4947     68/push  _test-input-stream/imm32
+4948     # . . call
+4949     e8/call  write/disp32
+4950     # . . discard args
+4951     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4952     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+4953     # . . push args
+4954     68/push  _test-output-buffered-file/imm32
+4955     68/push  _test-input-stream/imm32
+4956     # . . call
+4957     e8/call  convert-instruction/disp32
+4958     # . . discard args
+4959     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+4960     # check output
+4961     # . flush(_test-output-buffered-file)
+4962     # . . push args
+4963     68/push  _test-output-buffered-file/imm32
+4964     # . . call
+4965     e8/call  flush/disp32
+4966     # . . discard args
+4967     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4968 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5002     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
+5003     # . . push args
+5004     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
+5005     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
+5006     68/push  _test-output-stream/imm32
+5007     # . . call
+5008     e8/call  check-stream-equal/disp32
+5009     # . . discard args
+5010     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5011     # . epilog
+5012     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5013     5d/pop-to-EBP
+5014     c3/return
+5015 
+5016 test-convert-instruction-emits-modrm-byte-with-missing-r32:
+5017     # pack mod and rm32 operands into ModR/M byte
+5018     # . prolog
+5019     55/push-EBP
+5020     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5021     # setup
+5022     # . clear-stream(_test-input-stream)
+5023     # . . push args
+5024     68/push  _test-input-stream/imm32
+5025     # . . call
+5026     e8/call  clear-stream/disp32
+5027     # . . discard args
+5028     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5029     # . clear-stream(_test-output-stream)
+5030     # . . push args
+5031     68/push  _test-output-stream/imm32
+5032     # . . call
+5033     e8/call  clear-stream/disp32
+5034     # . . discard args
+5035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5036     # . clear-stream(_test-output-buffered-file+4)
+5037     # . . push args
+5038     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5039     05/add-to-EAX  4/imm32
+5040     50/push-EAX
+5041     # . . call
+5042     e8/call  clear-stream/disp32
+5043     # . . discard args
+5044     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5045     # initialize input
+5046     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
+5047     # . . push args
+5048     68/push  "8b/copy 0/mod 0/rm32"/imm32
+5049     68/push  _test-input-stream/imm32
+5050     # . . call
+5051     e8/call  write/disp32
+5052     # . . discard args
+5053     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5054     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5055     # . . push args
+5056     68/push  _test-output-buffered-file/imm32
+5057     68/push  _test-input-stream/imm32
+5058     # . . call
+5059     e8/call  convert-instruction/disp32
+5060     # . . discard args
+5061     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5062     # check output
+5063     # . flush(_test-output-buffered-file)
+5064     # . . push args
+5065     68/push  _test-output-buffered-file/imm32
+5066     # . . call
+5067     e8/call  flush/disp32
+5068     # . . discard args
+5069     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5070 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5104     # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
+5105     # . . push args
+5106     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
+5107     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
+5108     68/push  _test-output-stream/imm32
+5109     # . . call
+5110     e8/call  check-stream-equal/disp32
+5111     # . . discard args
+5112     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5113     # . epilog
+5114     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5115     5d/pop-to-EBP
+5116     c3/return
+5117 
+5118 test-convert-instruction-emits-sib-byte:
+5119     # pack base, index and scale operands into SIB byte
+5120     # . prolog
+5121     55/push-EBP
+5122     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5123     # setup
+5124     # . clear-stream(_test-input-stream)
+5125     # . . push args
+5126     68/push  _test-input-stream/imm32
+5127     # . . call
+5128     e8/call  clear-stream/disp32
+5129     # . . discard args
+5130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5131     # . clear-stream(_test-output-stream)
+5132     # . . push args
+5133     68/push  _test-output-stream/imm32
+5134     # . . call
+5135     e8/call  clear-stream/disp32
+5136     # . . discard args
+5137     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5138     # . clear-stream(_test-output-buffered-file+4)
+5139     # . . push args
+5140     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5141     05/add-to-EAX  4/imm32
+5142     50/push-EAX
+5143     # . . call
+5144     e8/call  clear-stream/disp32
+5145     # . . discard args
+5146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5147     # initialize input
+5148     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
+5149     # . . push args
+5150     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
+5151     68/push  _test-input-stream/imm32
+5152     # . . call
+5153     e8/call  write/disp32
+5154     # . . discard args
+5155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5156     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5157     # . . push args
+5158     68/push  _test-output-buffered-file/imm32
+5159     68/push  _test-input-stream/imm32
+5160     # . . call
+5161     e8/call  convert-instruction/disp32
+5162     # . . discard args
+5163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5164     # check output
+5165     # . flush(_test-output-buffered-file)
+5166     # . . push args
+5167     68/push  _test-output-buffered-file/imm32
+5168     # . . call
+5169     e8/call  flush/disp32
+5170     # . . discard args
+5171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5172 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5206     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
+5207     # . . push args
+5208     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
+5209     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
+5210     68/push  _test-output-stream/imm32
+5211     # . . call
+5212     e8/call  check-stream-equal/disp32
+5213     # . . discard args
+5214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5215     # . epilog
+5216     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5217     5d/pop-to-EBP
+5218     c3/return
+5219 
+5220 test-convert-instruction-emits-sib-byte-with-missing-base:
+5221     # pack index and scale operands into SIB byte
+5222     # . prolog
+5223     55/push-EBP
+5224     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5225     # setup
+5226     # . clear-stream(_test-input-stream)
+5227     # . . push args
+5228     68/push  _test-input-stream/imm32
+5229     # . . call
+5230     e8/call  clear-stream/disp32
+5231     # . . discard args
+5232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5233     # . clear-stream(_test-output-stream)
+5234     # . . push args
+5235     68/push  _test-output-stream/imm32
+5236     # . . call
+5237     e8/call  clear-stream/disp32
+5238     # . . discard args
+5239     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5240     # . clear-stream(_test-output-buffered-file+4)
+5241     # . . push args
+5242     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5243     05/add-to-EAX  4/imm32
+5244     50/push-EAX
+5245     # . . call
+5246     e8/call  clear-stream/disp32
+5247     # . . discard args
+5248     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5249     # initialize input
+5250     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
+5251     # . . push args
+5252     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
+5253     68/push  _test-input-stream/imm32
+5254     # . . call
+5255     e8/call  write/disp32
+5256     # . . discard args
+5257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5258     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5259     # . . push args
+5260     68/push  _test-output-buffered-file/imm32
+5261     68/push  _test-input-stream/imm32
+5262     # . . call
+5263     e8/call  convert-instruction/disp32
+5264     # . . discard args
+5265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5266     # check output
+5267     # . flush(_test-output-buffered-file)
+5268     # . . push args
+5269     68/push  _test-output-buffered-file/imm32
+5270     # . . call
+5271     e8/call  flush/disp32
+5272     # . . discard args
+5273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5274 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5308     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
+5309     # . . push args
+5310     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
+5311     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
+5312     68/push  _test-output-stream/imm32
+5313     # . . call
+5314     e8/call  check-stream-equal/disp32
+5315     # . . discard args
+5316     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5317     # . epilog
+5318     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5319     5d/pop-to-EBP
+5320     c3/return
+5321 
+5322 test-convert-instruction-emits-sib-byte-with-missing-index:
+5323     # pack base and scale operands into SIB byte
+5324     # . prolog
+5325     55/push-EBP
+5326     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5327     # setup
+5328     # . clear-stream(_test-input-stream)
+5329     # . . push args
+5330     68/push  _test-input-stream/imm32
+5331     # . . call
+5332     e8/call  clear-stream/disp32
+5333     # . . discard args
+5334     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5335     # . clear-stream(_test-output-stream)
+5336     # . . push args
+5337     68/push  _test-output-stream/imm32
+5338     # . . call
+5339     e8/call  clear-stream/disp32
+5340     # . . discard args
+5341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5342     # . clear-stream(_test-output-buffered-file+4)
+5343     # . . push args
+5344     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5345     05/add-to-EAX  4/imm32
+5346     50/push-EAX
+5347     # . . call
+5348     e8/call  clear-stream/disp32
+5349     # . . discard args
+5350     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5351     # initialize input
+5352     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
+5353     # . . push args
+5354     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
+5355     68/push  _test-input-stream/imm32
+5356     # . . call
+5357     e8/call  write/disp32
+5358     # . . discard args
+5359     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5360     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5361     # . . push args
+5362     68/push  _test-output-buffered-file/imm32
+5363     68/push  _test-input-stream/imm32
+5364     # . . call
+5365     e8/call  convert-instruction/disp32
+5366     # . . discard args
+5367     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5368     # check output
+5369     # . flush(_test-output-buffered-file)
+5370     # . . push args
+5371     68/push  _test-output-buffered-file/imm32
+5372     # . . call
+5373     e8/call  flush/disp32
+5374     # . . discard args
+5375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5376 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5410     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
+5411     # . . push args
+5412     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
+5413     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
+5414     68/push  _test-output-stream/imm32
+5415     # . . call
+5416     e8/call  check-stream-equal/disp32
+5417     # . . discard args
+5418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5419     # . epilog
+5420     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5421     5d/pop-to-EBP
+5422     c3/return
+5423 
+5424 test-convert-instruction-emits-sib-byte-with-missing-scale:
+5425     # pack base and index operands into SIB byte
+5426     # . prolog
+5427     55/push-EBP
+5428     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5429     # setup
+5430     # . clear-stream(_test-input-stream)
+5431     # . . push args
+5432     68/push  _test-input-stream/imm32
+5433     # . . call
+5434     e8/call  clear-stream/disp32
+5435     # . . discard args
+5436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5437     # . clear-stream(_test-output-stream)
+5438     # . . push args
+5439     68/push  _test-output-stream/imm32
+5440     # . . call
+5441     e8/call  clear-stream/disp32
+5442     # . . discard args
+5443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5444     # . clear-stream(_test-output-buffered-file+4)
+5445     # . . push args
+5446     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5447     05/add-to-EAX  4/imm32
+5448     50/push-EAX
+5449     # . . call
+5450     e8/call  clear-stream/disp32
+5451     # . . discard args
+5452     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5453     # initialize input
+5454     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
+5455     # . . push args
+5456     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
+5457     68/push  _test-input-stream/imm32
+5458     # . . call
+5459     e8/call  write/disp32
+5460     # . . discard args
+5461     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5462     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5463     # . . push args
+5464     68/push  _test-output-buffered-file/imm32
+5465     68/push  _test-input-stream/imm32
+5466     # . . call
+5467     e8/call  convert-instruction/disp32
+5468     # . . discard args
+5469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5470     # check output
+5471     # . flush(_test-output-buffered-file)
+5472     # . . push args
+5473     68/push  _test-output-buffered-file/imm32
+5474     # . . call
+5475     e8/call  flush/disp32
+5476     # . . discard args
+5477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5478 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5512     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
+5513     # . . push args
+5514     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
+5515     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
+5516     68/push  _test-output-stream/imm32
+5517     # . . call
+5518     e8/call  check-stream-equal/disp32
+5519     # . . discard args
+5520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5521     # . epilog
+5522     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5523     5d/pop-to-EBP
+5524     c3/return
+5525 
+5526 test-convert-instruction-handles-disp32-operand:
+5527     # expand /disp32 operand into 4 bytes
+5528     # . prolog
+5529     55/push-EBP
+5530     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5531     # setup
+5532     # . clear-stream(_test-input-stream)
+5533     # . . push args
+5534     68/push  _test-input-stream/imm32
+5535     # . . call
+5536     e8/call  clear-stream/disp32
+5537     # . . discard args
+5538     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5539     # . clear-stream(_test-output-stream)
+5540     # . . push args
+5541     68/push  _test-output-stream/imm32
+5542     # . . call
+5543     e8/call  clear-stream/disp32
+5544     # . . discard args
+5545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5546     # . clear-stream(_test-output-buffered-file+4)
+5547     # . . push args
+5548     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5549     05/add-to-EAX  4/imm32
+5550     50/push-EAX
+5551     # . . call
+5552     e8/call  clear-stream/disp32
+5553     # . . discard args
+5554     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5555     # initialize input
+5556     # . write(_test-input-stream, "e8/call 20/disp32")
+5557     # . . push args
+5558     68/push  "e8/call 20/disp32"/imm32
+5559     68/push  _test-input-stream/imm32
+5560     # . . call
+5561     e8/call  write/disp32
+5562     # . . discard args
+5563     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5564     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5565     # . . push args
+5566     68/push  _test-output-buffered-file/imm32
+5567     68/push  _test-input-stream/imm32
+5568     # . . call
+5569     e8/call  convert-instruction/disp32
+5570     # . . discard args
+5571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5572     # check output
+5573     # . flush(_test-output-buffered-file)
+5574     # . . push args
+5575     68/push  _test-output-buffered-file/imm32
+5576     # . . call
+5577     e8/call  flush/disp32
+5578     # . . discard args
+5579     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5580 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5614     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
+5615     # . . push args
+5616     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
+5617     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
+5618     68/push  _test-output-stream/imm32
+5619     # . . call
+5620     e8/call  check-stream-equal/disp32
+5621     # . . discard args
+5622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5623     # . epilog
+5624     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5625     5d/pop-to-EBP
+5626     c3/return
+5627 
+5628 test-convert-instruction-handles-disp16-operand:
+5629     # expand /disp16 operand into 2 bytes
+5630     # . prolog
+5631     55/push-EBP
+5632     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5633     # setup
+5634     # . clear-stream(_test-input-stream)
+5635     # . . push args
+5636     68/push  _test-input-stream/imm32
+5637     # . . call
+5638     e8/call  clear-stream/disp32
+5639     # . . discard args
+5640     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5641     # . clear-stream(_test-output-stream)
+5642     # . . push args
+5643     68/push  _test-output-stream/imm32
+5644     # . . call
+5645     e8/call  clear-stream/disp32
+5646     # . . discard args
+5647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5648     # . clear-stream(_test-output-buffered-file+4)
+5649     # . . push args
+5650     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5651     05/add-to-EAX  4/imm32
+5652     50/push-EAX
+5653     # . . call
+5654     e8/call  clear-stream/disp32
+5655     # . . discard args
+5656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5657     # initialize input
+5658     # . write(_test-input-stream, "e8/call 20/disp16")
+5659     # . . push args
+5660     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
+5661     68/push  _test-input-stream/imm32
+5662     # . . call
+5663     e8/call  write/disp32
+5664     # . . discard args
+5665     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5666     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5667     # . . push args
+5668     68/push  _test-output-buffered-file/imm32
+5669     68/push  _test-input-stream/imm32
+5670     # . . call
+5671     e8/call  convert-instruction/disp32
+5672     # . . discard args
+5673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5674     # check output
+5675     # . flush(_test-output-buffered-file)
+5676     # . . push args
+5677     68/push  _test-output-buffered-file/imm32
+5678     # . . call
+5679     e8/call  flush/disp32
+5680     # . . discard args
+5681     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5682 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5716     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
+5717     # . . push args
+5718     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
+5719     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
+5720     68/push  _test-output-stream/imm32
+5721     # . . call
+5722     e8/call  check-stream-equal/disp32
+5723     # . . discard args
+5724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5725     # . epilog
+5726     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5727     5d/pop-to-EBP
+5728     c3/return
+5729 
+5730 test-convert-instruction-handles-disp8-operand:
+5731     # expand /disp8 operand into 1 byte
+5732     # . prolog
+5733     55/push-EBP
+5734     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5735     # setup
+5736     # . clear-stream(_test-input-stream)
+5737     # . . push args
+5738     68/push  _test-input-stream/imm32
+5739     # . . call
+5740     e8/call  clear-stream/disp32
+5741     # . . discard args
+5742     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5743     # . clear-stream(_test-output-stream)
+5744     # . . push args
+5745     68/push  _test-output-stream/imm32
+5746     # . . call
+5747     e8/call  clear-stream/disp32
+5748     # . . discard args
+5749     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5750     # . clear-stream(_test-output-buffered-file+4)
+5751     # . . push args
+5752     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5753     05/add-to-EAX  4/imm32
+5754     50/push-EAX
+5755     # . . call
+5756     e8/call  clear-stream/disp32
+5757     # . . discard args
+5758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5759     # initialize input
+5760     # . write(_test-input-stream, "eb/jump 20/disp8")
+5761     # . . push args
+5762     68/push  "eb/jump 20/disp8"/imm32
+5763     68/push  _test-input-stream/imm32
+5764     # . . call
+5765     e8/call  write/disp32
+5766     # . . discard args
+5767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5768     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5769     # . . push args
+5770     68/push  _test-output-buffered-file/imm32
+5771     68/push  _test-input-stream/imm32
+5772     # . . call
+5773     e8/call  convert-instruction/disp32
+5774     # . . discard args
+5775     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5776     # check output
+5777     # . flush(_test-output-buffered-file)
+5778     # . . push args
+5779     68/push  _test-output-buffered-file/imm32
+5780     # . . call
+5781     e8/call  flush/disp32
+5782     # . . discard args
+5783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5784 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5818     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
+5819     # . . push args
+5820     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
+5821     68/push  "eb 20  # eb/jump 20/disp8"/imm32
+5822     68/push  _test-output-stream/imm32
+5823     # . . call
+5824     e8/call  check-stream-equal/disp32
+5825     # . . discard args
+5826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5827     # . epilog
+5828     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5829     5d/pop-to-EBP
+5830     c3/return
+5831 
+5832 test-convert-instruction-handles-disp8-name:
+5833     # pass /disp8 name directly through
+5834     # . prolog
+5835     55/push-EBP
+5836     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5837     # setup
+5838     # . clear-stream(_test-input-stream)
+5839     # . . push args
+5840     68/push  _test-input-stream/imm32
+5841     # . . call
+5842     e8/call  clear-stream/disp32
+5843     # . . discard args
+5844     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5845     # . clear-stream(_test-output-stream)
+5846     # . . push args
+5847     68/push  _test-output-stream/imm32
+5848     # . . call
+5849     e8/call  clear-stream/disp32
+5850     # . . discard args
+5851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5852     # . clear-stream(_test-output-buffered-file+4)
+5853     # . . push args
+5854     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5855     05/add-to-EAX  4/imm32
+5856     50/push-EAX
+5857     # . . call
+5858     e8/call  clear-stream/disp32
+5859     # . . discard args
+5860     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5861     # initialize input
+5862     # . write(_test-input-stream, "eb/jump xyz/disp8")
+5863     # . . push args
+5864     68/push  "eb/jump xyz/disp8"/imm32
+5865     68/push  _test-input-stream/imm32
+5866     # . . call
+5867     e8/call  write/disp32
+5868     # . . discard args
+5869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5870     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5871     # . . push args
+5872     68/push  _test-output-buffered-file/imm32
+5873     68/push  _test-input-stream/imm32
+5874     # . . call
+5875     e8/call  convert-instruction/disp32
+5876     # . . discard args
+5877     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5878     # check output
+5879     # . flush(_test-output-buffered-file)
+5880     # . . push args
+5881     68/push  _test-output-buffered-file/imm32
+5882     # . . call
+5883     e8/call  flush/disp32
+5884     # . . discard args
+5885     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5886 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+5920     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
+5921     # . . push args
+5922     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
+5923     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
+5924     68/push  _test-output-stream/imm32
+5925     # . . call
+5926     e8/call  check-stream-equal/disp32
+5927     # . . discard args
+5928     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+5929     # . epilog
+5930     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+5931     5d/pop-to-EBP
+5932     c3/return
+5933 
+5934 test-convert-instruction-handles-imm32-operand:
+5935     # expand /imm32 operand into 4 bytes
+5936     # . prolog
+5937     55/push-EBP
+5938     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+5939     # setup
+5940     # . clear-stream(_test-input-stream)
+5941     # . . push args
+5942     68/push  _test-input-stream/imm32
+5943     # . . call
+5944     e8/call  clear-stream/disp32
+5945     # . . discard args
+5946     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5947     # . clear-stream(_test-output-stream)
+5948     # . . push args
+5949     68/push  _test-output-stream/imm32
+5950     # . . call
+5951     e8/call  clear-stream/disp32
+5952     # . . discard args
+5953     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5954     # . clear-stream(_test-output-buffered-file+4)
+5955     # . . push args
+5956     b8/copy-to-EAX  _test-output-buffered-file/imm32
+5957     05/add-to-EAX  4/imm32
+5958     50/push-EAX
+5959     # . . call
+5960     e8/call  clear-stream/disp32
+5961     # . . discard args
+5962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5963     # initialize input
+5964     # . write(_test-input-stream, "68/push 0x20/imm32")
+5965     # . . push args
+5966     68/push  "68/push 0x20/imm32"/imm32
+5967     68/push  _test-input-stream/imm32
+5968     # . . call
+5969     e8/call  write/disp32
+5970     # . . discard args
+5971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5972     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+5973     # . . push args
+5974     68/push  _test-output-buffered-file/imm32
+5975     68/push  _test-input-stream/imm32
+5976     # . . call
+5977     e8/call  convert-instruction/disp32
+5978     # . . discard args
+5979     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+5980     # check output
+5981     # . flush(_test-output-buffered-file)
+5982     # . . push args
+5983     68/push  _test-output-buffered-file/imm32
+5984     # . . call
+5985     e8/call  flush/disp32
+5986     # . . discard args
+5987     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+5988 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+6022     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
+6023     # . . push args
+6024     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
+6025     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
+6026     68/push  _test-output-stream/imm32
+6027     # . . call
+6028     e8/call  check-stream-equal/disp32
+6029     # . . discard args
+6030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6031     # . epilog
+6032     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6033     5d/pop-to-EBP
+6034     c3/return
+6035 
+6036 test-convert-instruction-handles-imm16-operand:
+6037     # expand /imm16 operand into 2 bytes
+6038     # we don't have one of these at the moment, so this expands to an invalid instruction
+6039     # . prolog
+6040     55/push-EBP
+6041     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6042     # setup
+6043     # . clear-stream(_test-input-stream)
+6044     # . . push args
+6045     68/push  _test-input-stream/imm32
+6046     # . . call
+6047     e8/call  clear-stream/disp32
+6048     # . . discard args
+6049     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6050     # . clear-stream(_test-output-stream)
+6051     # . . push args
+6052     68/push  _test-output-stream/imm32
+6053     # . . call
+6054     e8/call  clear-stream/disp32
+6055     # . . discard args
+6056     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6057     # . clear-stream(_test-output-buffered-file+4)
+6058     # . . push args
+6059     b8/copy-to-EAX  _test-output-buffered-file/imm32
+6060     05/add-to-EAX  4/imm32
+6061     50/push-EAX
+6062     # . . call
+6063     e8/call  clear-stream/disp32
+6064     # . . discard args
+6065     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6066     # initialize input
+6067     # . write(_test-input-stream, "68/push 0x20/imm16")
+6068     # . . push args
+6069     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
+6070     68/push  _test-input-stream/imm32
+6071     # . . call
+6072     e8/call  write/disp32
+6073     # . . discard args
+6074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6075     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+6076     # . . push args
+6077     68/push  _test-output-buffered-file/imm32
+6078     68/push  _test-input-stream/imm32
+6079     # . . call
+6080     e8/call  convert-instruction/disp32
+6081     # . . discard args
+6082     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6083     # check output
+6084     # . flush(_test-output-buffered-file)
+6085     # . . push args
+6086     68/push  _test-output-buffered-file/imm32
+6087     # . . call
+6088     e8/call  flush/disp32
+6089     # . . discard args
+6090     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6091 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+6125     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
+6126     # . . push args
+6127     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
+6128     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
+6129     68/push  _test-output-stream/imm32
+6130     # . . call
+6131     e8/call  check-stream-equal/disp32
+6132     # . . discard args
+6133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6134     # . epilog
+6135     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6136     5d/pop-to-EBP
+6137     c3/return
+6138 
+6139 test-convert-instruction-handles-imm8-operand:
+6140     # expand /imm8 operand into 1 byte
+6141     # we don't have one of these at the moment, so this expands to an invalid instruction
+6142     # . prolog
+6143     55/push-EBP
+6144     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6145     # setup
+6146     # . clear-stream(_test-input-stream)
+6147     # . . push args
+6148     68/push  _test-input-stream/imm32
+6149     # . . call
+6150     e8/call  clear-stream/disp32
+6151     # . . discard args
+6152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6153     # . clear-stream(_test-output-stream)
+6154     # . . push args
+6155     68/push  _test-output-stream/imm32
+6156     # . . call
+6157     e8/call  clear-stream/disp32
+6158     # . . discard args
+6159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6160     # . clear-stream(_test-output-buffered-file+4)
+6161     # . . push args
+6162     b8/copy-to-EAX  _test-output-buffered-file/imm32
+6163     05/add-to-EAX  4/imm32
+6164     50/push-EAX
+6165     # . . call
+6166     e8/call  clear-stream/disp32
+6167     # . . discard args
+6168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6169     # initialize input
+6170     # . write(_test-input-stream, "68/push 0x20/imm8")
+6171     # . . push args
+6172     68/push  "68/push 0x20/imm8"/imm32
+6173     68/push  _test-input-stream/imm32
+6174     # . . call
+6175     e8/call  write/disp32
+6176     # . . discard args
+6177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6178     # convert-instruction(_test-input-stream, _test-output-buffered-file)
+6179     # . . push args
+6180     68/push  _test-output-buffered-file/imm32
+6181     68/push  _test-input-stream/imm32
+6182     # . . call
+6183     e8/call  convert-instruction/disp32
+6184     # . . discard args
+6185     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6186     # check output
+6187     # . flush(_test-output-buffered-file)
+6188     # . . push args
+6189     68/push  _test-output-buffered-file/imm32
+6190     # . . call
+6191     e8/call  flush/disp32
+6192     # . . discard args
+6193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6194 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+6228     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
+6229     # . . push args
+6230     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
+6231     68/push  "68 20  # 68/push 0x20/imm8"/imm32
+6232     68/push  _test-output-stream/imm32
+6233     # . . call
+6234     e8/call  check-stream-equal/disp32
+6235     # . . discard args
+6236     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6237     # . epilog
+6238     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6239     5d/pop-to-EBP
+6240     c3/return
+6241 
+6242 has-metadata?:  # word : (address slice), s : (address string) -> EAX : boolean
+6243     # pseudocode:
+6244     #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
+6245     #   curr = twig->end
+6246     #   while true
+6247     #     twig = next-token-from-slice(curr, word->end, '/')
+6248     #     if (twig.empty()) break
+6249     #     if (slice-equal?(twig, s)) return true
+6250     #     curr = twig->end
+6251     #   return false
+6252     # . prolog
+6253     55/push-EBP
+6254     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6255     # . save registers
+6256     51/push-ECX
+6257     52/push-EDX
+6258     56/push-ESI
+6259     57/push-EDI
+6260     # ESI = word
+6261     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+6262     # EDX = word->end
+6263     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
+6264     # var twig/EDI : (address slice) = {0, 0}
+6265     68/push  0/imm32/end
+6266     68/push  0/imm32/start
+6267     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+6268     # next-token-from-slice(word->start, word->end, '/', twig)
+6269     # . . push args
+6270     57/push-EDI
+6271     68/push  0x2f/imm32/slash
+6272     52/push-EDX
+6273     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+6274     # . . call
+6275     e8/call  next-token-from-slice/disp32
+6276     # . . discard args
+6277     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+6278     # curr/ECX = twig->end
+6279     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+6280 $has-metadata?:loop:
+6281     # next-token-from-slice(curr, word->end, '/', twig)
+6282     # . . push args
+6283     57/push-EDI
+6284     68/push  0x2f/imm32/slash
+6285     52/push-EDX
+6286     51/push-ECX
+6287     # . . call
+6288     e8/call  next-token-from-slice/disp32
+6289     # . . discard args
+6290     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+6291     # if (slice-empty?(twig)) return false
+6292     # . EAX = slice-empty?(twig)
+6293     # . . push args
+6294     57/push-EDI
+6295     # . . call
+6296     e8/call  slice-empty?/disp32
+6297     # . . discard args
+6298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6299     # . if (EAX != 0) return false
+6300     3d/compare-EAX-and  0/imm32
+6301     75/jump-if-not-equal  $has-metadata?:false/disp8
+6302     # if (slice-equal?(twig, s)) return true
+6303     # . EAX = slice-equal?(twig, s)
+6304     # . . push args
+6305     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+6306     57/push-EDI
+6307     # . . call
+6308     e8/call  slice-equal?/disp32
+6309     # . . discard args
+6310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6311     # . if (EAX != 0) return true
+6312     3d/compare-EAX-and  0/imm32
+6313     75/jump-if-not-equal  $has-metadata?:true/disp8
+6314     # curr = twig->end
+6315     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+6316     eb/jump  $has-metadata?:loop/disp8
+6317 $has-metadata?:true:
+6318     b8/copy-to-EAX  1/imm32/true
+6319     eb/jump  $has-metadata?:end/disp8
+6320 $has-metadata?:false:
+6321     b8/copy-to-EAX  0/imm32/false
+6322 $has-metadata?:end:
+6323     # . reclaim locals
+6324     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6325     # . restore registers
+6326     5f/pop-to-EDI
+6327     5e/pop-to-ESI
+6328     5a/pop-to-EDX
+6329     59/pop-to-ECX
+6330     # . epilog
+6331     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6332     5d/pop-to-EBP
+6333     c3/return
+6334 
+6335 test-has-metadata-true:
+6336     # . prolog
+6337     55/push-EBP
+6338     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6339     # (EAX..ECX) = "ab/c"
+6340     b8/copy-to-EAX  "ab/c"/imm32
+6341     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+6342     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
+6343     05/add-to-EAX  4/imm32
+6344     # var in/ESI : (address slice) = {EAX, ECX}
+6345     51/push-ECX
+6346     50/push-EAX
+6347     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+6348     # EAX = has-metadata?(ESI, "c")
+6349     # . . push args
+6350     68/push  "c"/imm32
+6351     56/push-ESI
+6352     # . . call
+6353     e8/call  has-metadata?/disp32
+6354     # . . discard args
+6355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6356     # check-ints-equal(EAX, 1, msg)
+6357     # . . push args
+6358     68/push  "F - test-has-metadata-true"/imm32
+6359     68/push  1/imm32/true
+6360     50/push-EAX
+6361     # . . call
+6362     e8/call  check-ints-equal/disp32
+6363     # . . discard args
+6364     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6365     # . epilog
+6366     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6367     5d/pop-to-EBP
+6368     c3/return
+6369 
+6370 test-has-metadata-false:
+6371     # . prolog
+6372     55/push-EBP
+6373     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6374     # (EAX..ECX) = "ab/c"
+6375     b8/copy-to-EAX  "ab/c"/imm32
+6376     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+6377     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
+6378     05/add-to-EAX  4/imm32
+6379     # var in/ESI : (address slice) = {EAX, ECX}
+6380     51/push-ECX
+6381     50/push-EAX
+6382     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+6383     # EAX = has-metadata?(ESI, "c")
+6384     # . . push args
+6385     68/push  "d"/imm32
+6386     56/push-ESI
+6387     # . . call
+6388     e8/call  has-metadata?/disp32
+6389     # . . discard args
+6390     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6391     # check-ints-equal(EAX, 0, msg)
+6392     # . . push args
+6393     68/push  "F - test-has-metadata-false"/imm32
+6394     68/push  0/imm32/false
+6395     50/push-EAX
+6396     # . . call
+6397     e8/call  check-ints-equal/disp32
+6398     # . . discard args
+6399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6400     # . epilog
+6401     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6402     5d/pop-to-EBP
+6403     c3/return
+6404 
+6405 test-has-metadata-ignore-name:
+6406     # . prolog
+6407     55/push-EBP
+6408     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6409     # (EAX..ECX) = "a/b"
+6410     b8/copy-to-EAX  "a/b"/imm32
+6411     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+6412     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
+6413     05/add-to-EAX  4/imm32
+6414     # var in/ESI : (address slice) = {EAX, ECX}
+6415     51/push-ECX
+6416     50/push-EAX
+6417     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+6418     # EAX = has-metadata?(ESI, "a")
+6419     # . . push args
+6420     68/push  "a"/imm32
+6421     56/push-ESI
+6422     # . . call
+6423     e8/call  has-metadata?/disp32
+6424     # . . discard args
+6425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6426     # check-ints-equal(EAX, 0, msg)
+6427     # . . push args
+6428     68/push  "F - test-has-metadata-ignore-name"/imm32
+6429     68/push  0/imm32/false
+6430     50/push-EAX
+6431     # . . call
+6432     e8/call  check-ints-equal/disp32
+6433     # . . discard args
+6434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6435     # . epilog
+6436     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6437     5d/pop-to-EBP
+6438     c3/return
+6439 
+6440 test-has-metadata-multiple-true:
+6441     # . prolog
+6442     55/push-EBP
+6443     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6444     # (EAX..ECX) = "a/b/c"
+6445     b8/copy-to-EAX  "a/b/c"/imm32
+6446     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+6447     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
+6448     05/add-to-EAX  4/imm32
+6449     # var in/ESI : (address slice) = {EAX, ECX}
+6450     51/push-ECX
+6451     50/push-EAX
+6452     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+6453     # EAX = has-metadata?(ESI, "c")
+6454     # . . push args
+6455     68/push  "c"/imm32
+6456     56/push-ESI
+6457     # . . call
+6458     e8/call  has-metadata?/disp32
+6459     # . . discard args
+6460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6461     # check-ints-equal(EAX, 1, msg)
+6462     # . . push args
+6463     68/push  "F - test-has-metadata-multiple-true"/imm32
+6464     68/push  1/imm32/true
+6465     50/push-EAX
+6466     # . . call
+6467     e8/call  check-ints-equal/disp32
+6468     # . . discard args
+6469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6470     # . epilog
+6471     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6472     5d/pop-to-EBP
+6473     c3/return
+6474 
+6475 test-has-metadata-multiple-false:
+6476     # . prolog
+6477     55/push-EBP
+6478     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6479     # (EAX..ECX) = "a/b/c"
+6480     b8/copy-to-EAX  "a/b/c"/imm32
+6481     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+6482     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
+6483     05/add-to-EAX  4/imm32
+6484     # var in/ESI : (address slice) = {EAX, ECX}
+6485     51/push-ECX
+6486     50/push-EAX
+6487     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+6488     # EAX = has-metadata?(ESI, "d")
+6489     # . . push args
+6490     68/push  "d"/imm32
+6491     56/push-ESI
+6492     # . . call
+6493     e8/call  has-metadata?/disp32
+6494     # . . discard args
+6495     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6496     # check-ints-equal(EAX, 0, msg)
+6497     # . . push args
+6498     68/push  "F - test-has-metadata-multiple-false"/imm32
+6499     68/push  0/imm32/false
+6500     50/push-EAX
+6501     # . . call
+6502     e8/call  check-ints-equal/disp32
+6503     # . . discard args
+6504     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6505     # . epilog
+6506     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6507     5d/pop-to-EBP
+6508     c3/return
+6509 
+6510 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
+6511 # it in 'width' bytes of hex, least significant first.
+6512 # Otherwise just print the entire word including metadata.
+6513 # Always print a trailing space.
+6514 emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
+6515     # . prolog
+6516     55/push-EBP
+6517     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6518     # . save registers
+6519     50/push-EAX
+6520     56/push-ESI
+6521     57/push-EDI
+6522     # ESI = word
+6523     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+6524     # var name/EDI : (address slice) = {0, 0}
+6525     68/push  0/imm32/end
+6526     68/push  0/imm32/start
+6527     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+6528     # datum = next-token-from-slice(word->start, word->end, '/')
+6529     # . . push args
+6530     57/push-EDI
+6531     68/push  0x2f/imm32/slash
+6532     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+6533     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+6534     # . . call
+6535     e8/call  next-token-from-slice/disp32
+6536     # . . discard args
+6537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+6538     # if (is-valid-name?(datum)) write-slice(out, word) and return
+6539     # . EAX = is-valid-name?(name)
+6540     # . . push args
+6541     57/push-EDI
+6542     # . . call
+6543     e8/call  is-valid-name?/disp32
+6544     # . . discard args
+6545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6546     # . if (EAX != 0)
+6547     3d/compare-EAX-and  0/imm32
+6548     74/jump-if-equal  $emit:hex-int/disp8
+6549 $emit:name:
+6550     # . write-slice(out, word)
+6551     # . . push args
+6552     56/push-ESI
+6553     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+6554     # . . call
+6555     e8/call  write-slice/disp32
+6556     # . . discard args
+6557     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6558     # . write-buffered(out, " ")
+6559     # . . push args
+6560     68/push  " "/imm32
+6561     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+6562     # . . call
+6563     e8/call  write-buffered/disp32
+6564     # . . discard args
+6565     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6566     # . return
+6567     eb/jump  $emit:end/disp8
+6568     # otherwise emit-hex(out, parse-hex-int(datum), width)
+6569     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
+6570     #   name or a hex number, but we're only going to be passing in real legal
+6571     #   programs. We just want to make sure that valid names aren't treated as
+6572     #   (valid) hex numbers.)
+6573 $emit:hex-int:
+6574     # . value/EAX = parse-hex-int(datum)
+6575     # . . push args
+6576     57/push-EDI
+6577     # . . call
+6578     e8/call  parse-hex-int/disp32
+6579     # . . discard args
+6580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6581     # . emit-hex(out, value, width)
+6582     # . . push args
+6583     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+6584     50/push-EAX
+6585     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+6586     # . . call
+6587     e8/call  emit-hex/disp32
+6588     # . . discard args
+6589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6590 $emit:end:
+6591     # . reclaim locals
+6592     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+6593     # . restore registers
+6594     5f/pop-to-EDI
+6595     5e/pop-to-ESI
+6596     58/pop-to-EAX
+6597     # . epilog
+6598     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6599     5d/pop-to-EBP
+6600     c3/return
+6601 
+6602 test-emit-number:
+6603     # . prolog
+6604     55/push-EBP
+6605     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6606     # setup
+6607     # . clear-stream(_test-output-stream)
+6608     # . . push args
+6609     68/push  _test-output-stream/imm32
+6610     # . . call
+6611     e8/call  clear-stream/disp32
+6612     # . . discard args
+6613     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6614     # . clear-stream(_test-output-buffered-file+4)
+6615     # . . push args
+6616     b8/copy-to-EAX  _test-output-buffered-file/imm32
+6617     05/add-to-EAX  4/imm32
+6618     50/push-EAX
+6619     # . . call
+6620     e8/call  clear-stream/disp32
+6621     # . . discard args
+6622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6623     # var slice/ECX = "30"
+6624     68/push  _test-slice-three-zero-end/imm32/end
+6625     68/push  _test-slice-three-zero/imm32/start
+6626     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+6627     # emit(_test-output-buffered-file, slice, 1)
+6628     # . . push args
+6629     68/push  1/imm32
+6630     51/push-ECX
+6631     68/push  _test-output-buffered-file/imm32
+6632     # . . call
+6633     e8/call  emit/disp32
+6634     # . . discard args
+6635     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6636     # flush(_test-output-buffered-file)
+6637     # . . push args
+6638     68/push  _test-output-buffered-file/imm32
+6639     # . . call
+6640     e8/call  flush/disp32
+6641     # . . discard args
+6642     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6643     # check-stream-equal(_test-output-stream, "30 ", msg)
+6644     # . . push args
+6645     68/push  "F - test-emit-number/1"/imm32
+6646     68/push  "30 "/imm32
+6647     68/push  _test-output-stream/imm32
+6648     # . . call
+6649     e8/call  check-stream-equal/disp32
+6650     # . . discard args
+6651     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6652     # . epilog
+6653     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6654     5d/pop-to-EBP
+6655     c3/return
+6656 
+6657 test-emit-negative-number:
+6658     # test support for sign-extending negative numbers
+6659     # . prolog
+6660     55/push-EBP
+6661     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6662     # setup
+6663     # . clear-stream(_test-output-stream)
+6664     # . . push args
+6665     68/push  _test-output-stream/imm32
+6666     # . . call
+6667     e8/call  clear-stream/disp32
+6668     # . . discard args
+6669     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6670     # . clear-stream(_test-output-buffered-file+4)
+6671     # . . push args
+6672     b8/copy-to-EAX  _test-output-buffered-file/imm32
+6673     05/add-to-EAX  4/imm32
+6674     50/push-EAX
+6675     # . . call
+6676     e8/call  clear-stream/disp32
+6677     # . . discard args
+6678     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6679     # var slice/ECX = "-2"
+6680     68/push  _test-slice-negative-two-end/imm32/end
+6681     68/push  _test-slice-negative-two/imm32/start
+6682     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+6683     # emit(_test-output-buffered-file, slice, 2)
+6684     # . . push args
+6685     68/push  2/imm32
+6686     51/push-ECX
+6687     68/push  _test-output-buffered-file/imm32
+6688     # . . call
+6689     e8/call  emit/disp32
+6690     # . . discard args
+6691     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6692     # flush(_test-output-buffered-file)
+6693     # . . push args
+6694     68/push  _test-output-buffered-file/imm32
+6695     # . . call
+6696     e8/call  flush/disp32
+6697     # . . discard args
+6698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6699     # check-stream-equal(_test-output-stream, "fe ff ", msg)
+6700     # . . push args
+6701     68/push  "F - test-emit-number/1"/imm32
+6702     68/push  "fe ff "/imm32
+6703     68/push  _test-output-stream/imm32
+6704     # . . call
+6705     e8/call  check-stream-equal/disp32
+6706     # . . discard args
+6707     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6708     # . epilog
+6709     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6710     5d/pop-to-EBP
+6711     c3/return
+6712 
+6713 test-emit-number-with-metadata:
+6714     # . prolog
+6715     55/push-EBP
+6716     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6717     # setup
+6718     # . clear-stream(_test-output-stream)
+6719     # . . push args
+6720     68/push  _test-output-stream/imm32
+6721     # . . call
+6722     e8/call  clear-stream/disp32
+6723     # . . discard args
+6724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6725     # . clear-stream(_test-output-buffered-file+4)
+6726     # . . push args
+6727     b8/copy-to-EAX  _test-output-buffered-file/imm32
+6728     05/add-to-EAX  4/imm32
+6729     50/push-EAX
+6730     # . . call
+6731     e8/call  clear-stream/disp32
+6732     # . . discard args
+6733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6734     # var slice/ECX = "-2/foo"
+6735     68/push  _test-slice-negative-two-metadata-end/imm32/end
+6736     68/push  _test-slice-negative-two/imm32/start
+6737     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+6738     # emit(_test-output-buffered-file, slice, 2)
+6739     # . . push args
+6740     68/push  2/imm32
+6741     51/push-ECX
+6742     68/push  _test-output-buffered-file/imm32
+6743     # . . call
+6744     e8/call  emit/disp32
+6745     # . . discard args
+6746     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6747     # flush(_test-output-buffered-file)
+6748     # . . push args
+6749     68/push  _test-output-buffered-file/imm32
+6750     # . . call
+6751     e8/call  flush/disp32
+6752     # . . discard args
+6753     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6754     # the '/foo' will have no impact on the output
+6755     # check-stream-equal(_test-output-stream, "fe ff ", msg)
+6756     # . . push args
+6757     68/push  "F - test-emit-number-with-metadata"/imm32
+6758     68/push  "fe ff "/imm32
+6759     68/push  _test-output-stream/imm32
+6760     # . . call
+6761     e8/call  check-stream-equal/disp32
+6762     # . . discard args
+6763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6764     # . epilog
+6765     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6766     5d/pop-to-EBP
+6767     c3/return
+6768 
+6769 test-emit-non-number:
+6770     # . prolog
+6771     55/push-EBP
+6772     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6773     # setup
+6774     # . clear-stream(_test-output-stream)
+6775     # . . push args
+6776     68/push  _test-output-stream/imm32
+6777     # . . call
+6778     e8/call  clear-stream/disp32
+6779     # . . discard args
+6780     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6781     # . clear-stream(_test-output-buffered-file+4)
+6782     # . . push args
+6783     b8/copy-to-EAX  _test-output-buffered-file/imm32
+6784     05/add-to-EAX  4/imm32
+6785     50/push-EAX
+6786     # . . call
+6787     e8/call  clear-stream/disp32
+6788     # . . discard args
+6789     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6790     # var slice/ECX = "xyz"
+6791     68/push  _test-slice-non-number-word-end/imm32/end
+6792     68/push  _test-slice-non-number-word/imm32/start
+6793     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+6794     # emit(_test-output-buffered-file, slice, 2)
+6795     # . . push args
+6796     68/push  2/imm32
+6797     51/push-ECX
+6798     68/push  _test-output-buffered-file/imm32
+6799     # . . call
+6800     e8/call  emit/disp32
+6801     # . . discard args
+6802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6803     # flush(_test-output-buffered-file)
+6804     # . . push args
+6805     68/push  _test-output-buffered-file/imm32
+6806     # . . call
+6807     e8/call  flush/disp32
+6808     # . . discard args
+6809     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6810     # check-stream-equal(_test-output-stream, "xyz", msg)
+6811     # . . push args
+6812     68/push  "F - test-emit-non-number"/imm32
+6813     68/push  "xyz "/imm32
+6814     68/push  _test-output-stream/imm32
+6815     # . . call
+6816     e8/call  check-stream-equal/disp32
+6817     # . . discard args
+6818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6819     # . epilog
+6820     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6821     5d/pop-to-EBP
+6822     c3/return
+6823 
+6824 test-emit-non-number-with-metadata:
+6825     # . prolog
+6826     55/push-EBP
+6827     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6828     # setup
+6829     # . clear-stream(_test-output-stream)
+6830     # . . push args
+6831     68/push  _test-output-stream/imm32
+6832     # . . call
+6833     e8/call  clear-stream/disp32
+6834     # . . discard args
+6835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6836     # . clear-stream(_test-output-buffered-file+4)
+6837     # . . push args
+6838     b8/copy-to-EAX  _test-output-buffered-file/imm32
+6839     05/add-to-EAX  4/imm32
+6840     50/push-EAX
+6841     # . . call
+6842     e8/call  clear-stream/disp32
+6843     # . . discard args
+6844     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6845     # var slice/ECX = "xyz/"
+6846     68/push  _test-slice-non-number-word-metadata-end/imm32/end
+6847     68/push  _test-slice-non-number-word/imm32/start
+6848     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+6849     # emit(_test-output-buffered-file, slice, 2)
+6850     # . . push args
+6851     68/push  2/imm32
+6852     51/push-ECX
+6853     68/push  _test-output-buffered-file/imm32
+6854     # . . call
+6855     e8/call  emit/disp32
+6856     # . . discard args
+6857     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6858     # flush(_test-output-buffered-file)
+6859     # . . push args
+6860     68/push  _test-output-buffered-file/imm32
+6861     # . . call
+6862     e8/call  flush/disp32
+6863     # . . discard args
+6864     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6865     # check-stream-equal(_test-output-stream, "xyz/", msg)
+6866     # . . push args
+6867     68/push  "F - test-emit-non-number-with-metadata"/imm32
+6868     68/push  "xyz/ "/imm32
+6869     68/push  _test-output-stream/imm32
+6870     # . . call
+6871     e8/call  check-stream-equal/disp32
+6872     # . . discard args
+6873     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6874     # . epilog
+6875     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6876     5d/pop-to-EBP
+6877     c3/return
+6878 
+6879 test-emit-non-number-with-all-hex-digits-and-metadata:
+6880     # . prolog
+6881     55/push-EBP
+6882     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6883     # setup
+6884     # . clear-stream(_test-output-stream)
+6885     # . . push args
+6886     68/push  _test-output-stream/imm32
+6887     # . . call
+6888     e8/call  clear-stream/disp32
+6889     # . . discard args
+6890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6891     # . clear-stream(_test-output-buffered-file+4)
+6892     # . . push args
+6893     b8/copy-to-EAX  _test-output-buffered-file/imm32
+6894     05/add-to-EAX  4/imm32
+6895     50/push-EAX
+6896     # . . call
+6897     e8/call  clear-stream/disp32
+6898     # . . discard args
+6899     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6900     # var slice/ECX = "abcd/xyz"
+6901     68/push  _test-slice-hexlike-non-number-word-metadata-end/imm32/end
+6902     68/push  _test-slice-hexlike-non-number-word/imm32/start
+6903     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+6904     # emit(_test-output-buffered-file, slice, 2)
+6905     # . . push args
+6906     68/push  2/imm32
+6907     51/push-ECX
+6908     68/push  _test-output-buffered-file/imm32
+6909     # . . call
+6910     e8/call  emit/disp32
+6911     # . . discard args
+6912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6913     # flush(_test-output-buffered-file)
+6914     # . . push args
+6915     68/push  _test-output-buffered-file/imm32
+6916     # . . call
+6917     e8/call  flush/disp32
+6918     # . . discard args
+6919     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+6920 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+6954     # check-stream-equal(_test-output-stream, "abcd/xyz")
+6955     # . . push args
+6956     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
+6957     68/push  "abcd/xyz "/imm32
+6958     68/push  _test-output-stream/imm32
+6959     # . . call
+6960     e8/call  check-stream-equal/disp32
+6961     # . . discard args
+6962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+6963     # . epilog
+6964     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+6965     5d/pop-to-EBP
+6966     c3/return
+6967 
+6968 # conditions for 'valid' names that are not at risk of looking like hex numbers
+6969 # keep in sync with the rules in labels.cc
+6970 #: - if it starts with a digit, it's treated as a number. If it can't be
+6971 #:   parsed as hex it will raise an error.
+6972 #: - if it starts with '-' it's treated as a number.
+6973 #: - if it starts with '0x' it's treated as a number. (redundant)
+6974 #: - if it's two characters long, it can't be a name. Either it's a hex
+6975 #:   byte, or it raises an error.
+6976 is-valid-name?:  # in : (address slice) -> EAX : boolean
+6977     # . prolog
+6978     55/push-EBP
+6979     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+6980     # . save registers
+6981     51/push-ECX
+6982     56/push-ESI
+6983     # ESI = in
+6984     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+6985     # start/ECX = in->start
+6986     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+6987     # end/EAX = in->end
+6988     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+6989 $is-valid-name?:check0:
+6990     # if (start >= end) return false
+6991     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # compare ECX with EAX
+6992     7d/jump-if-greater-or-equal  $is-valid-name?:false/disp8
+6993 $is-valid-name?:check1:
+6994     # EAX -= ECX
+6995     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+6996     # if (EAX == 2) return false
+6997     3d/compare-EAX-and  2/imm32
+6998     74/jump-if-equal  $is-valid-name?:false/disp8
+6999 $is-valid-name?:check2:
+7000     # c/EAX = *ECX
+7001     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+7002     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+7003     # if (c == "-") return false
+7004     3d/compare-EAX-and  2d/imm32/-
+7005     74/jump-if-equal  $is-valid-name?:false/disp8
+7006 $is-valid-name?:check3a:
+7007     # if (c < "0") return true
+7008     3d/compare-EAX-with  30/imm32/0
+7009     7c/jump-if-lesser  $is-valid-name?:true/disp8
+7010 $is-valid-name?:check3b:
+7011     # if (c > "9") return true
+7012     3d/compare-EAX-with  39/imm32/9
+7013     7f/jump-if-greater  $is-valid-name?:true/disp8
+7014 $is-valid-name?:false:
+7015     # return false
+7016     b8/copy-to-EAX  0/imm32/false
+7017     eb/jump  $is-valid-name?:end/disp8
+7018 $is-valid-name?:true:
+7019     # return true
+7020     b8/copy-to-EAX  1/imm32/true
+7021 $is-valid-name?:end:
+7022     # . restore registers
+7023     5e/pop-to-ESI
+7024     59/pop-to-ECX
+7025     # . epilog
+7026     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7027     5d/pop-to-EBP
+7028     c3/return
+7029 
+7030 test-is-valid-name-digit-prefix:
+7031     # . prolog
+7032     55/push-EBP
+7033     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7034     # var slice/ECX = "34"
+7035     68/push  _test-slice-hex-int-end/imm32
+7036     68/push  _test-slice-hex-int/imm32
+7037     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+7038     # EAX = is-valid-name?(slice)
+7039     # . . push args
+7040     51/push-ECX
+7041     # . . call
+7042     e8/call  is-valid-name?/disp32
+7043     # . . discard args
+7044     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7045     # check-ints-equal(EAX, 0, msg)
+7046     # . . push args
+7047     68/push  "F - test-is-valid-name-digit-prefix"/imm32
+7048     68/push  0/imm32/false
+7049     50/push-EAX
+7050     # . . call
+7051     e8/call  check-ints-equal/disp32
+7052     # . . discard args
+7053     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7054     # . epilog
+7055     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7056     5d/pop-to-EBP
+7057     c3/return
+7058 
+7059 test-is-valid-name-negative-prefix:
+7060     # . prolog
+7061     55/push-EBP
+7062     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7063     # var slice/ECX = "-0x34"
+7064     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
+7065     68/push  _test-slice-hex-int-with-0x-prefix-negative/imm32
+7066     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+7067     # EAX = is-valid-name?(slice)
+7068     # . . push args
+7069     51/push-ECX
+7070     # . . call
+7071     e8/call  is-valid-name?/disp32
+7072     # . . discard args
+7073     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7074     # check-ints-equal(EAX, 0, msg)
+7075     # . . push args
+7076     68/push  "F - test-is-valid-name-negative-prefix"/imm32
+7077     68/push  0/imm32/false
+7078     50/push-EAX
+7079     # . . call
+7080     e8/call  check-ints-equal/disp32
+7081     # . . discard args
+7082     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7083     # . epilog
+7084     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7085     5d/pop-to-EBP
+7086     c3/return
+7087 
+7088 test-is-valid-name-0x-prefix:
+7089     # . prolog
+7090     55/push-EBP
+7091     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7092     # var slice/ECX = "0x34"
+7093     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
+7094     68/push  _test-slice-hex-int-with-0x-prefix/imm32
+7095     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+7096     # EAX = is-valid-name?(slice)
+7097     # . . push args
+7098     51/push-ECX
+7099     # . . call
+7100     e8/call  is-valid-name?/disp32
+7101     # . . discard args
+7102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7103     # check-ints-equal(EAX, 0, msg)
+7104     # . . push args
+7105     68/push  "F - test-is-valid-name-0x-prefix"/imm32
+7106     68/push  0/imm32/false
+7107     50/push-EAX
+7108     # . . call
+7109     e8/call  check-ints-equal/disp32
+7110     # . . discard args
+7111     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7112     # . epilog
+7113     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7114     5d/pop-to-EBP
+7115     c3/return
+7116 
+7117 test-is-valid-name-starts-with-pre-digit:
+7118     # . prolog
+7119     55/push-EBP
+7120     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7121     # var slice/ECX = "/03"
+7122     68/push  _test-slice-with-slash-prefix-end/imm32
+7123     68/push  _test-slice-with-slash-prefix/imm32
+7124     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+7125     # EAX = is-valid-name?(slice)
+7126     # . . push args
+7127     51/push-ECX
+7128     # . . call
+7129     e8/call  is-valid-name?/disp32
+7130     # . . discard args
+7131     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7132     # check-ints-equal(EAX, 1, msg)
+7133     # . . push args
+7134     68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
+7135     68/push  1/imm32/true
+7136     50/push-EAX
+7137     # . . call
+7138     e8/call  check-ints-equal/disp32
+7139     # . . discard args
+7140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7141     # . epilog
+7142     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7143     5d/pop-to-EBP
+7144     c3/return
+7145 
+7146 test-is-valid-name-starts-with-post-digit:
+7147     # . prolog
+7148     55/push-EBP
+7149     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7150     # var slice/ECX = "q34"
+7151     68/push  _test-slice-char-and-digits-end/imm32
+7152     68/push  _test-slice-char-and-digits/imm32
+7153     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+7154     # EAX = is-valid-name?(slice)
+7155     # . . push args
+7156     51/push-ECX
+7157     # . . call
+7158     e8/call  is-valid-name?/disp32
+7159     # . . discard args
+7160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7161     # check-ints-equal(EAX, 1, msg)
+7162     # . . push args
+7163     68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
+7164     68/push  1/imm32/true
+7165     50/push-EAX
+7166     # . . call
+7167     e8/call  check-ints-equal/disp32
+7168     # . . discard args
+7169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7170     # . epilog
+7171     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7172     5d/pop-to-EBP
+7173     c3/return
+7174 
+7175 test-is-valid-name-starts-with-digit:
+7176     # . prolog
+7177     55/push-EBP
+7178     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7179     # var slice/ECX = "0x34"
+7180     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
+7181     68/push  _test-slice-hex-int-with-0x-prefix/imm32
+7182     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+7183     # EAX = is-valid-name?(slice)
+7184     # . . push args
+7185     51/push-ECX
+7186     # . . call
+7187     e8/call  is-valid-name?/disp32
+7188     # . . discard args
+7189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7190     # check-ints-equal(EAX, 0, msg)
+7191     # . . push args
+7192     68/push  "F - test-is-valid-name-starts-with-digit"/imm32
+7193     68/push  0/imm32/false
+7194     50/push-EAX
+7195     # . . call
+7196     e8/call  check-ints-equal/disp32
+7197     # . . discard args
+7198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7199     # . epilog
+7200     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7201     5d/pop-to-EBP
+7202     c3/return
+7203 
+7204 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
+7205 emit-hex:  # out : (address buffered-file), n : int, width : int -> <void>
+7206     # . prolog
+7207     55/push-EBP
+7208     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7209     # . save registers
+7210     50/push-EAX
+7211     51/push-ECX
+7212     52/push-EDX
+7213     53/push-EBX
+7214     57/push-EDI
+7215     # EDI = out
+7216     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+7217     # EBX = n
+7218     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
+7219     # EDX = width
+7220     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
+7221     # var curr/ECX = 0
+7222     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+7223 $emit-hex:loop:
+7224     # if (curr >= width) break
+7225     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
+7226     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
+7227     # print-byte(out, EBX)
+7228     # . . push args
+7229     53/push-EBX
+7230     57/push-EDI
+7231     # . . call
+7232     e8/call  print-byte/disp32
+7233     # . . discard args
+7234     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+7235     # write-byte(out, ' ')
+7236     # . . push args
+7237     68/push  0x20/imm32/space
+7238     57/push-EDI
+7239     # . . call
+7240     e8/call  write-byte/disp32
+7241     # . . discard args
+7242     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+7243     # EBX = EBX >> 8
+7244     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
+7245 $emit-hex:continue:
+7246     # ++curr
+7247     41/increment-ECX
+7248     eb/jump  $emit-hex:loop/disp8
+7249 $emit-hex:end:
+7250     # . restore registers
+7251     5f/pop-to-EDI
+7252     5b/pop-to-EBX
+7253     5a/pop-to-EDX
+7254     59/pop-to-ECX
+7255     58/pop-to-EAX
+7256     # . epilog
+7257     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7258     5d/pop-to-EBP
+7259     c3/return
+7260 
+7261 test-emit-hex-single-byte:
+7262     # setup
+7263     # . clear-stream(_test-output-stream)
+7264     # . . push args
+7265     68/push  _test-output-stream/imm32
+7266     # . . call
+7267     e8/call  clear-stream/disp32
+7268     # . . discard args
+7269     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7270     # . clear-stream(_test-output-buffered-file+4)
+7271     # . . push args
+7272     b8/copy-to-EAX  _test-output-buffered-file/imm32
+7273     05/add-to-EAX  4/imm32
+7274     50/push-EAX
+7275     # . . call
+7276     e8/call  clear-stream/disp32
+7277     # . . discard args
+7278     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7279     # emit-hex(_test-output-buffered-file, 0xab, 1)
+7280     # . . push args
+7281     68/push  1/imm32
+7282     68/push  0xab/imm32
+7283     68/push  _test-output-buffered-file/imm32
+7284     # . . call
+7285     e8/call  emit-hex/disp32
+7286     # . . discard args
+7287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7288     # flush(_test-output-buffered-file)
+7289     # . . push args
+7290     68/push  _test-output-buffered-file/imm32
+7291     # . . call
+7292     e8/call  flush/disp32
+7293     # . . discard args
+7294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7295     # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
+7296     # . . push args
+7297     68/push  "F - test-emit-hex-single-byte"/imm32
+7298     68/push  0x206261/imm32
+7299     # . . push *_test-output-stream->data
+7300     b8/copy-to-EAX  _test-output-stream/imm32
+7301     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+7302     # . . call
+7303     e8/call  check-ints-equal/disp32
+7304     # . . discard args
+7305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7306     # . end
+7307     c3/return
+7308 
+7309 test-emit-hex-multiple-byte:
+7310     # setup
+7311     # . clear-stream(_test-output-stream)
+7312     # . . push args
+7313     68/push  _test-output-stream/imm32
+7314     # . . call
+7315     e8/call  clear-stream/disp32
+7316     # . . discard args
+7317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7318     # . clear-stream(_test-output-buffered-file+4)
+7319     # . . push args
+7320     b8/copy-to-EAX  _test-output-buffered-file/imm32
+7321     05/add-to-EAX  4/imm32
+7322     50/push-EAX
+7323     # . . call
+7324     e8/call  clear-stream/disp32
+7325     # . . discard args
+7326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7327     # emit-hex(_test-output-buffered-file, 0x1234, 2)
+7328     # . . push args
+7329     68/push  2/imm32
+7330     68/push  0x1234/imm32
+7331     68/push  _test-output-buffered-file/imm32
+7332     # . . call
+7333     e8/call  emit-hex/disp32
+7334     # . . discard args
+7335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7336     # flush(_test-output-buffered-file)
+7337     # . . push args
+7338     68/push  _test-output-buffered-file/imm32
+7339     # . . call
+7340     e8/call  flush/disp32
+7341     # . . discard args
+7342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7343     # check-stream-equal(_test-output-stream, "34 12 ", msg)
+7344     # . . push args
+7345     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
+7346     68/push  "34 12 "/imm32
+7347     68/push  _test-output-stream/imm32
+7348     # . . call
+7349     e8/call  check-stream-equal/disp32
+7350     # . . discard args
+7351     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7352     # . end
+7353     c3/return
+7354 
+7355 test-emit-hex-zero-pad:
+7356     # setup
+7357     # . clear-stream(_test-output-stream)
+7358     # . . push args
+7359     68/push  _test-output-stream/imm32
+7360     # . . call
+7361     e8/call  clear-stream/disp32
+7362     # . . discard args
+7363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7364     # . clear-stream(_test-output-buffered-file+4)
+7365     # . . push args
+7366     b8/copy-to-EAX  _test-output-buffered-file/imm32
+7367     05/add-to-EAX  4/imm32
+7368     50/push-EAX
+7369     # . . call
+7370     e8/call  clear-stream/disp32
+7371     # . . discard args
+7372     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7373     # emit-hex(_test-output-buffered-file, 0xab, 2)
+7374     # . . push args
+7375     68/push  2/imm32
+7376     68/push  0xab/imm32
+7377     68/push  _test-output-buffered-file/imm32
+7378     # . . call
+7379     e8/call  emit-hex/disp32
+7380     # . . discard args
+7381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7382     # flush(_test-output-buffered-file)
+7383     # . . push args
+7384     68/push  _test-output-buffered-file/imm32
+7385     # . . call
+7386     e8/call  flush/disp32
+7387     # . . discard args
+7388     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7389     # check(_test-output-stream->data == 'ab 00 ')
+7390     # . . push args
+7391     68/push  "F - test-emit-hex-zero-pad/1"/imm32
+7392     68/push  "ab 00 "/imm32
+7393     68/push  _test-output-stream/imm32
+7394     # . . call
+7395     e8/call  check-stream-equal/disp32
+7396     # . . discard args
+7397     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7398     # . end
+7399     c3/return
+7400 
+7401 test-emit-hex-negative:
+7402     # setup
+7403     # . clear-stream(_test-output-stream)
+7404     # . . push args
+7405     68/push  _test-output-stream/imm32
+7406     # . . call
+7407     e8/call  clear-stream/disp32
+7408     # . . discard args
+7409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7410     # . clear-stream(_test-output-buffered-file+4)
+7411     # . . push args
+7412     b8/copy-to-EAX  _test-output-buffered-file/imm32
+7413     05/add-to-EAX  4/imm32
+7414     50/push-EAX
+7415     # . . call
+7416     e8/call  clear-stream/disp32
+7417     # . . discard args
+7418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7419     # emit-hex(_test-output-buffered-file, -1, 2)
+7420     # . . push args
+7421     68/push  2/imm32
+7422     68/push  -1/imm32
+7423     68/push  _test-output-buffered-file/imm32
+7424     # . . call
+7425     e8/call  emit-hex/disp32
+7426     # . . discard args
+7427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7428     # flush(_test-output-buffered-file)
+7429     # . . push args
+7430     68/push  _test-output-buffered-file/imm32
+7431     # . . call
+7432     e8/call  flush/disp32
+7433     # . . discard args
+7434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7435     # check-stream-equal(_test-output-stream == "ff ff ")
+7436     # . . push args
+7437     68/push  "F - test-emit-hex-negative/1"/imm32
+7438     68/push  "ff ff "/imm32
+7439     68/push  _test-output-stream/imm32
+7440     # . . call
+7441     e8/call  check-stream-equal/disp32
+7442     # . . discard args
+7443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7444     # . end
+7445     c3/return
+7446 
+7447 # write an entire stream's contents to a buffered-file
+7448 # ways to do this:
+7449 #   - construct a 'maximal slice' and pass it to write-slice
+7450 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
+7451 # we'll go with the first way for now
+7452 write-stream-data:  # f : (address buffered-file), s : (address stream) -> <void>
+7453     # . prolog
+7454     55/push-EBP
+7455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7456     # . save registers
+7457     50/push-EAX
+7458     51/push-ECX
+7459     56/push-ESI
+7460     # ESI = s
+7461     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+7462     # var slice/ECX = {s->data, s->data + s->write}
+7463     # . push s->data + s->write
+7464     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+7465     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
+7466     50/push-EAX
+7467     # . push s->data
+7468     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
+7469     50/push-EAX
+7470     # . ECX = ESP
+7471     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+7472     # write-slice(f, slice)
+7473     # . . push args
+7474     51/push-ECX
+7475     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+7476     # . . call
+7477     e8/call  write-slice/disp32
+7478     # . . discard args
+7479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+7480 $write-stream-data:end:
+7481     # . restore locals
+7482     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+7483     # . restore registers
+7484     5e/pop-to-ESI
+7485     59/pop-to-ECX
+7486     58/pop-to-EAX
+7487     # . epilog
+7488     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7489     5d/pop-to-EBP
+7490     c3/return
+7491 
+7492 test-write-stream-data:
+7493     # . prolog
+7494     55/push-EBP
+7495     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7496     # setup
+7497     # . clear-stream(_test-output-stream)
+7498     # . . push args
+7499     68/push  _test-output-stream/imm32
+7500     # . . call
+7501     e8/call  clear-stream/disp32
+7502     # . . discard args
+7503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7504     # . clear-stream(_test-output-buffered-file+4)
+7505     # . . push args
+7506     b8/copy-to-EAX  _test-output-buffered-file/imm32
+7507     05/add-to-EAX  4/imm32
+7508     50/push-EAX
+7509     # . . call
+7510     e8/call  clear-stream/disp32
+7511     # . . discard args
+7512     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7513     # . clear-stream(_test-tmp-stream)
+7514     # . . push args
+7515     68/push  _test-tmp-stream/imm32
+7516     # . . call
+7517     e8/call  clear-stream/disp32
+7518     # . . discard args
+7519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7520     # initialize input
+7521     # . write(_test-tmp-stream, "abcd")
+7522     # . . push args
+7523     68/push  "abcd"/imm32
+7524     68/push  _test-tmp-stream/imm32
+7525     # . . call
+7526     e8/call  write/disp32
+7527     # . . discard args
+7528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+7529     # write-stream-data(_test-output-buffered-file, _test-tmp-stream)
+7530     # . . push args
+7531     68/push  _test-tmp-stream/imm32
+7532     68/push  _test-output-buffered-file/imm32
+7533     # . . call
+7534     e8/call  write-stream-data/disp32
+7535     # . . discard args
+7536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+7537     # check that the write happened as expected
+7538     # . flush(_test-output-buffered-file)
+7539     # . . push args
+7540     68/push  _test-output-buffered-file/imm32
+7541     # . . call
+7542     e8/call  flush/disp32
+7543     # . . discard args
+7544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7545     # . check-stream-equal(_test-output-stream, "abcd", msg)
+7546     # . . push args
+7547     68/push  "F - test-write-stream-data"/imm32
+7548     68/push  "abcd"/imm32
+7549     68/push  _test-output-stream/imm32
+7550     # . . call
+7551     e8/call  check-stream-equal/disp32
+7552     # . . discard args
+7553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+7554     # . epilog
+7555     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7556     5d/pop-to-EBP
+7557     c3/return
+7558 
+7559 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/'))
+7560 parse-datum-of-word:  # word : (address slice) -> value/EAX
+7561     # . prolog
+7562     55/push-EBP
+7563     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+7564     # . save registers
+7565     51/push-ECX
+7566     56/push-ESI
+7567     # ESI = word
+7568     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+7569     # var slice/ECX = {0, 0}
+7570     68/push  0/imm32/end
+7571     68/push  0/imm32/start
+7572     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+7573     # slice = next-token-from-slice(word->start, word->end, '/')
+7574     # . . push args
+7575     51/push-ECX
+7576     68/push  0x2f/imm32/slash
+7577     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+7578     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+7579     # . . call
+7580     e8/call  next-token-from-slice/disp32
+7581     # . . discard args
+7582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+7583     # value/EAX = parse-hex-int(slice)
+7584     # . . push args
+7585     51/push-ECX
+7586     # . . call
+7587     e8/call  parse-hex-int/disp32
+7588     # . . discard args
+7589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+7590 $parse-datum-of-word:end:
+7591     # . reclaim locals
+7592     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+7593     # . restore registers
+7594     5e/pop-to-ESI
+7595     59/pop-to-ECX
+7596     # . epilog
+7597     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+7598     5d/pop-to-EBP
+7599     c3/return
+7600 
+7601 == data
+7602 
+7603 _test-slice-negative-two:
+7604     2d/- 32/2
+7605 _test-slice-negative-two-end:
+7606     2f/slash 66/f 6f/o 6f/o
+7607 _test-slice-negative-two-metadata-end:
+7608 
+7609 _test-slice-three-zero:
+7610     33/3 30/0
+7611 _test-slice-three-zero-end:
+7612 
+7613 _test-slice-non-number-word:
+7614     78/x 79/y 7a/z
+7615 _test-slice-non-number-word-end:
+7616     2f/slash
+7617 _test-slice-non-number-word-metadata-end:
+7618 
+7619 _test-input-stream:
+7620     # current write index
+7621     0/imm32
+7622     # current read index
+7623     0/imm32
+7624     # length
+7625     0x80/imm32
+7626     # data
+7627     00 00 00 00 00 00 00 00  # 8 bytes
+7628     00 00 00 00 00 00 00 00  # 8 bytes
+7629     00 00 00 00 00 00 00 00  # 8 bytes
+7630     00 00 00 00 00 00 00 00  # 8 bytes
+7631     00 00 00 00 00 00 00 00  # 8 bytes
+7632     00 00 00 00 00 00 00 00  # 8 bytes
+7633     00 00 00 00 00 00 00 00  # 8 bytes
+7634     00 00 00 00 00 00 00 00  # 8 bytes
+7635     00 00 00 00 00 00 00 00  # 8 bytes
+7636     00 00 00 00 00 00 00 00  # 8 bytes
+7637     00 00 00 00 00 00 00 00  # 8 bytes
+7638     00 00 00 00 00 00 00 00  # 8 bytes
+7639     00 00 00 00 00 00 00 00  # 8 bytes
+7640     00 00 00 00 00 00 00 00  # 8 bytes
+7641     00 00 00 00 00 00 00 00  # 8 bytes
+7642     00 00 00 00 00 00 00 00  # 8 bytes
+7643 
+7644 # a test buffered file for _test-input-stream
+7645 _test-input-buffered-file:
+7646     # file descriptor or (address stream)
+7647     _test-input-stream/imm32
+7648     # current write index
+7649     0/imm32
+7650     # current read index
+7651     0/imm32
+7652     # length
+7653     6/imm32
+7654     # data
+7655     00 00 00 00 00 00  # 6 bytes
+7656 
+7657 _test-output-stream:
+7658     # current write index
+7659     0/imm32
+7660     # current read index
+7661     0/imm32
+7662     # length
+7663     0x80/imm32
+7664     # data
+7665     00 00 00 00 00 00 00 00  # 8 bytes
+7666     00 00 00 00 00 00 00 00  # 8 bytes
+7667     00 00 00 00 00 00 00 00  # 8 bytes
+7668     00 00 00 00 00 00 00 00  # 8 bytes
+7669     00 00 00 00 00 00 00 00  # 8 bytes
+7670     00 00 00 00 00 00 00 00  # 8 bytes
+7671     00 00 00 00 00 00 00 00  # 8 bytes
+7672     00 00 00 00 00 00 00 00  # 8 bytes
+7673     00 00 00 00 00 00 00 00  # 8 bytes
+7674     00 00 00 00 00 00 00 00  # 8 bytes
+7675     00 00 00 00 00 00 00 00  # 8 bytes
+7676     00 00 00 00 00 00 00 00  # 8 bytes
+7677     00 00 00 00 00 00 00 00  # 8 bytes
+7678     00 00 00 00 00 00 00 00  # 8 bytes
+7679     00 00 00 00 00 00 00 00  # 8 bytes
+7680     00 00 00 00 00 00 00 00  # 8 bytes
+7681 
+7682 # a test buffered file for _test-output-stream
+7683 _test-output-buffered-file:
+7684     # file descriptor or (address stream)
+7685     _test-output-stream/imm32
+7686     # current write index
+7687     0/imm32
+7688     # current read index
+7689     0/imm32
+7690     # length
+7691     6/imm32
+7692     # data
+7693     00 00 00 00 00 00  # 6 bytes
+7694 
+7695 _test-slice-hexlike-non-number-word:
+7696     61/a 62/b 63/c 64/d
+7697 _test-slice-hexlike-non-number-word-end:
+7698     2f/slash
+7699     78/x 79/y 7a/z
+7700 _test-slice-hexlike-non-number-word-metadata-end:
+7701 
+7702 _test-slice-with-slash-prefix:
+7703   2f/slash 30/0 33/3
+7704 _test-slice-with-slash-prefix-end:
+7705 
+7706 # . . vim:nowrap:textwidth=0
 
diff --git a/html/subx/apps/subx-common.subx.html b/html/subx/apps/subx-common.subx.html new file mode 100644 index 00000000..01e40369 --- /dev/null +++ b/html/subx/apps/subx-common.subx.html @@ -0,0 +1,314 @@ + + + + +Mu - subx/apps/subx-common.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/apps/subx-common.subx +
+  1 # some common helpers shared by phases of the SubX converter
+  2 
+  3 == code
+  4 #   instruction                     effective address                                                   register    displacement    immediate
+  5 # . op          subop               mod             rm32          base        index         scale       r32
+  6 # . 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
+  7 
+  8 # (re)compute the bounds of the next word in the line
+  9 # return empty string on reaching end of file
+ 10 next-word:  # line : (address stream byte), out : (address slice)
+ 11     # . prolog
+ 12     55/push-EBP
+ 13     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 14     # . save registers
+ 15     50/push-EAX
+ 16     51/push-ECX
+ 17     56/push-ESI
+ 18     57/push-EDI
+ 19     # ESI = line
+ 20     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 21     # EDI = out
+ 22     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+ 23     # skip-chars-matching(line, ' ')
+ 24     # . . push args
+ 25     68/push  0x20/imm32/space
+ 26     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 27     # . . call
+ 28     e8/call  skip-chars-matching/disp32
+ 29     # . . discard args
+ 30     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 31 $next-word:check0:
+ 32     # if (line->read >= line->write) clear out and return
+ 33     # . EAX = line->read
+ 34     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+ 35     # . if (EAX < line->write) goto next check
+ 36     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
+ 37     7c/jump-if-lesser  $next-word:check1/disp8
+ 38     # . return out = {0, 0}
+ 39     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
+ 40     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
+ 41     eb/jump  $next-word:end/disp8
+ 42 $next-word:check1:
+ 43     # out->start = &line->data[line->read]
+ 44     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 45     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
+ 46     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+ 47     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
+ 48     # . EAX = line->data[line->read]
+ 49     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 50     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
+ 51     # . compare
+ 52     3d/compare-EAX-and  0x23/imm32/pound
+ 53     75/jump-if-not-equal  $next-word:not-comment/disp8
+ 54     # . out->end = &line->data[line->write]
+ 55     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+ 56     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
+ 57     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+ 58     # . line->read = line->write
+ 59     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
+ 60     # . return
+ 61     eb/jump  $next-word:end/disp8
+ 62 $next-word:not-comment:
+ 63     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
+ 64     # . . push args
+ 65     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 66     # . . call
+ 67     e8/call  skip-chars-not-matching-whitespace/disp32
+ 68     # . . discard args
+ 69     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 70     # out->end = &line->data[line->read]
+ 71     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 72     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
+ 73     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+ 74 $next-word:end:
+ 75     # . restore registers
+ 76     5f/pop-to-EDI
+ 77     5e/pop-to-ESI
+ 78     59/pop-to-ECX
+ 79     58/pop-to-EAX
+ 80     # . epilog
+ 81     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 82     5d/pop-to-EBP
+ 83     c3/return
+ 84 
+ 85 test-next-word:
+ 86     # . prolog
+ 87     55/push-EBP
+ 88     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 89     # setup
+ 90     # . clear-stream(_test-stream)
+ 91     # . . push args
+ 92     68/push  _test-stream/imm32
+ 93     # . . call
+ 94     e8/call  clear-stream/disp32
+ 95     # . . discard args
+ 96     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 97     # var slice/ECX = {0, 0}
+ 98     68/push  0/imm32/end
+ 99     68/push  0/imm32/start
+100     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+101     # write(_test-stream, "  ab")
+102     # . . push args
+103     68/push  "  ab"/imm32
+104     68/push  _test-stream/imm32
+105     # . . call
+106     e8/call  write/disp32
+107     # . . discard args
+108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+109     # next-word(_test-stream, slice)
+110     # . . push args
+111     51/push-ECX
+112     68/push  _test-stream/imm32
+113     # . . call
+114     e8/call  next-word/disp32
+115     # . . discard args
+116     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+117     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+118     # . check-ints-equal(slice->start - _test-stream, 14, msg)
+119     # . . push args
+120     68/push  "F - test-next-word: start"/imm32
+121     68/push  0xe/imm32
+122     # . . push slice->start - _test-stream
+123     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+124     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+125     50/push-EAX
+126     # . . call
+127     e8/call  check-ints-equal/disp32
+128     # . . discard args
+129     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+130     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
+131     # . check-ints-equal(slice->end - _test-stream, 16, msg)
+132     # . . push args
+133     68/push  "F - test-next-word: end"/imm32
+134     68/push  0x10/imm32
+135     # . . push slice->end - _test-stream
+136     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+137     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+138     50/push-EAX
+139     # . . call
+140     e8/call  check-ints-equal/disp32
+141     # . . discard args
+142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+143     # . epilog
+144     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+145     5d/pop-to-EBP
+146     c3/return
+147 
+148 test-next-word-returns-whole-comment:
+149     # . prolog
+150     55/push-EBP
+151     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+152     # setup
+153     # . clear-stream(_test-stream)
+154     # . . push args
+155     68/push  _test-stream/imm32
+156     # . . call
+157     e8/call  clear-stream/disp32
+158     # . . discard args
+159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+160     # var slice/ECX = {0, 0}
+161     68/push  0/imm32/end
+162     68/push  0/imm32/start
+163     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+164     # write(_test-stream, "  # a")
+165     # . . push args
+166     68/push  "  # a"/imm32
+167     68/push  _test-stream/imm32
+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     # next-word(_test-stream, slice)
+173     # . . push args
+174     51/push-ECX
+175     68/push  _test-stream/imm32
+176     # . . call
+177     e8/call  next-word/disp32
+178     # . . discard args
+179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+180     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+181     # . check-ints-equal(slice->start - _test-stream, 14, msg)
+182     # . . push args
+183     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
+184     68/push  0xe/imm32
+185     # . . push slice->start - _test-stream
+186     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+187     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+188     50/push-EAX
+189     # . . call
+190     e8/call  check-ints-equal/disp32
+191     # . . discard args
+192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+193     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
+194     # . check-ints-equal(slice->end - _test-stream, 17, msg)
+195     # . . push args
+196     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
+197     68/push  0x11/imm32
+198     # . . push slice->end - _test-stream
+199     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+200     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+201     50/push-EAX
+202     # . . call
+203     e8/call  check-ints-equal/disp32
+204     # . . discard args
+205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+206     # . epilog
+207     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+208     5d/pop-to-EBP
+209     c3/return
+210 
+211 test-next-word-returns-empty-string-on-eof:
+212     # . prolog
+213     55/push-EBP
+214     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+215     # setup
+216     # . clear-stream(_test-stream)
+217     # . . push args
+218     68/push  _test-stream/imm32
+219     # . . call
+220     e8/call  clear-stream/disp32
+221     # . . discard args
+222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+223     # var slice/ECX = {0, 0}
+224     68/push  0/imm32/end
+225     68/push  0/imm32/start
+226     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+227     # write nothing to _test-stream
+228     # next-word(_test-stream, slice)
+229     # . . push args
+230     51/push-ECX
+231     68/push  _test-stream/imm32
+232     # . . call
+233     e8/call  next-word/disp32
+234     # . . discard args
+235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+236     # check-ints-equal(slice->end - slice->start, 0, msg)
+237     # . . push args
+238     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
+239     68/push  0/imm32
+240     # . . push slice->end - slice->start
+241     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+242     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
+243     50/push-EAX
+244     # . . call
+245     e8/call  check-ints-equal/disp32
+246     # . . discard args
+247     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+248     # . epilog
+249     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+250     5d/pop-to-EBP
+251     c3/return
+
+ + + -- cgit 1.4.1-2-gfad0