From 6e181e7fd998a0d542cb531c929c905a243ea2f6 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 5 Feb 2019 23:30:12 -0800 Subject: 4953 --- html/subx/052kernel-string-equal.subx.html | 52 +- html/subx/054string-equal.subx.html | 38 +- html/subx/055stream.subx.html | 140 +++++ html/subx/055trace.subx.html | 424 ------------- html/subx/056trace.subx.html | 423 +++++++++++++ html/subx/056write.subx.html | 268 --------- html/subx/057stop.subx.html | 279 --------- html/subx/057write.subx.html | 230 ++++++++ html/subx/058read.subx.html | 432 -------------- html/subx/058stream-equal.subx.html | 737 +++++++++++++++++++++++ html/subx/059read-byte.subx.html | 369 ------------ html/subx/059stop.subx.html | 279 +++++++++ html/subx/060read.subx.html | 428 ++++++++++++++ html/subx/060write-stream.subx.html | 309 ---------- html/subx/061error.subx.html | 120 ---- html/subx/061read-byte.subx.html | 369 ++++++++++++ html/subx/062write-byte.subx.html | 298 ---------- html/subx/062write-stream.subx.html | 305 ++++++++++ html/subx/063error.subx.html | 120 ++++ html/subx/063hex.subx.html | 804 ------------------------- html/subx/064print-byte.subx.html | 169 ------ html/subx/064write-byte.subx.html | 283 +++++++++ html/subx/065hex.subx.html | 874 +++++++++++++++++++++++++++ html/subx/065write-buffered.subx.html | 286 --------- html/subx/066error-byte.subx.html | 176 ------ html/subx/066print-byte.subx.html | 167 ++++++ html/subx/067allocate.subx.html | 271 --------- html/subx/067write-buffered.subx.html | 284 +++++++++ html/subx/068error-byte.subx.html | 176 ++++++ html/subx/068new-stream.subx.html | 190 ------ html/subx/069allocate.subx.html | 271 +++++++++ html/subx/069read-line.subx.html | 377 ------------ html/subx/070new-stream.subx.html | 188 ++++++ html/subx/070slice.subx.html | 586 ------------------ html/subx/071next-token.subx.html | 914 ----------------------------- html/subx/071read-line.subx.html | 373 ++++++++++++ html/subx/072slice.subx.html | 584 ++++++++++++++++++ html/subx/073next-token.subx.html | 914 +++++++++++++++++++++++++++++ 38 files changed, 7190 insertions(+), 6317 deletions(-) create mode 100644 html/subx/055stream.subx.html delete mode 100644 html/subx/055trace.subx.html create mode 100644 html/subx/056trace.subx.html delete mode 100644 html/subx/056write.subx.html delete mode 100644 html/subx/057stop.subx.html create mode 100644 html/subx/057write.subx.html delete mode 100644 html/subx/058read.subx.html create mode 100644 html/subx/058stream-equal.subx.html delete mode 100644 html/subx/059read-byte.subx.html create mode 100644 html/subx/059stop.subx.html create mode 100644 html/subx/060read.subx.html delete mode 100644 html/subx/060write-stream.subx.html delete mode 100644 html/subx/061error.subx.html create mode 100644 html/subx/061read-byte.subx.html delete mode 100644 html/subx/062write-byte.subx.html create mode 100644 html/subx/062write-stream.subx.html create mode 100644 html/subx/063error.subx.html delete mode 100644 html/subx/063hex.subx.html delete mode 100644 html/subx/064print-byte.subx.html create mode 100644 html/subx/064write-byte.subx.html create mode 100644 html/subx/065hex.subx.html delete mode 100644 html/subx/065write-buffered.subx.html delete mode 100644 html/subx/066error-byte.subx.html create mode 100644 html/subx/066print-byte.subx.html delete mode 100644 html/subx/067allocate.subx.html create mode 100644 html/subx/067write-buffered.subx.html create mode 100644 html/subx/068error-byte.subx.html delete mode 100644 html/subx/068new-stream.subx.html create mode 100644 html/subx/069allocate.subx.html delete mode 100644 html/subx/069read-line.subx.html create mode 100644 html/subx/070new-stream.subx.html delete mode 100644 html/subx/070slice.subx.html delete mode 100644 html/subx/071next-token.subx.html create mode 100644 html/subx/071read-line.subx.html create mode 100644 html/subx/072slice.subx.html create mode 100644 html/subx/073next-token.subx.html (limited to 'html') diff --git a/html/subx/052kernel-string-equal.subx.html b/html/subx/052kernel-string-equal.subx.html index 86926d78..55d0546b 100644 --- a/html/subx/052kernel-string-equal.subx.html +++ b/html/subx/052kernel-string-equal.subx.html @@ -90,7 +90,7 @@ if ('onhashchange' in window) { 28 29 # compare a null-terminated ascii string with a more idiomatic length-prefixed byte array 30 # reason for the name: the only place we should have null-terminated ascii strings is from commandline args - 31 kernel-string-equal: # s : null-terminated ascii string, benchmark : length-prefixed ascii string -> EAX : boolean + 31 kernel-string-equal?: # s : null-terminated ascii string, benchmark : length-prefixed ascii string -> EAX : boolean 32 # pseudocode: 33 # initialize n = b->length 34 # initialize s1 = s @@ -125,9 +125,9 @@ if ('onhashchange' in window) { 63 # initialize loop counter i into ECX 64 b9/copy-to-ECX 0/imm32/exit 65 # while (i/ECX < n/EDX) - 66 $kernel-string-equal:loop: + 66 $kernel-string-equal?:loop: 67 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 68 74/jump-if-equal $kernel-string-equal:break/disp8 + 68 74/jump-if-equal $kernel-string-equal?:break/disp8 69 # c1/EAX, c2/EBX = *s, *benchmark 70 b8/copy-to-EAX 0/imm32 71 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX @@ -135,29 +135,29 @@ if ('onhashchange' in window) { 73 8a/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy byte at *ESI to lower byte of EBX 74 # if (c1 == 0) return false 75 3d/compare-EAX 0/imm32 - 76 74/jump-if-equal $kernel-string-equal:false/disp8 + 76 74/jump-if-equal $kernel-string-equal?:false/disp8 77 # if (c1 != c2) return false 78 39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX with EBX - 79 75/jump-if-not-equal $kernel-string-equal:false/disp8 + 79 75/jump-if-not-equal $kernel-string-equal?:false/disp8 80 # ++s1, ++s2, ++i 81 41/inc-ECX 82 46/inc-ESI 83 47/inc-EDI 84 # end while - 85 eb/jump $kernel-string-equal:loop/disp8 - 86 $kernel-string-equal:break: + 85 eb/jump $kernel-string-equal?:loop/disp8 + 86 $kernel-string-equal?:break: 87 # if (*s/EDI == 0) return true 88 b8/copy-to-EAX 0/imm32 89 8a/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy byte at *EDI to lower byte of EAX 90 3d/compare-EAX 0/imm32 - 91 75/jump-if-not-equal $kernel-string-equal:false/disp8 - 92 $kernel-string-equal:true: + 91 75/jump-if-not-equal $kernel-string-equal?:false/disp8 + 92 $kernel-string-equal?:true: 93 b8/copy-to-EAX 1/imm32 - 94 eb/jump $kernel-string-equal:end/disp8 + 94 eb/jump $kernel-string-equal?:end/disp8 95 # return false - 96 $kernel-string-equal:false: + 96 $kernel-string-equal?:false: 97 b8/copy-to-EAX 0/imm32 - 98 $kernel-string-equal:end: + 98 $kernel-string-equal?:end: 99 # . restore registers 100 5f/pop-to-EDI 101 5e/pop-to-ESI @@ -172,12 +172,12 @@ if ('onhashchange' in window) { 110 # - tests 111 112 test-compare-null-kernel-string-with-empty-array: -113 # EAX = kernel-string-equal(Null-kernel-string, "") +113 # EAX = kernel-string-equal?(Null-kernel-string, "") 114 # . . push args 115 68/push ""/imm32 116 68/push Null-kernel-string/imm32 117 # . . call -118 e8/call kernel-string-equal/disp32 +118 e8/call kernel-string-equal?/disp32 119 # . . discard args 120 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 121 # check-ints-equal(EAX, 1, msg) @@ -192,12 +192,12 @@ if ('onhashchange' in window) { 130 c3/return 131 132 test-compare-null-kernel-string-with-non-empty-array: -133 # EAX = kernel-string-equal(Null-kernel-string, "Abc") +133 # EAX = kernel-string-equal?(Null-kernel-string, "Abc") 134 # . . push args 135 68/push "Abc"/imm32 136 68/push Null-kernel-string/imm32 137 # . . call -138 e8/call kernel-string-equal/disp32 +138 e8/call kernel-string-equal?/disp32 139 # . . discard args 140 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 141 # check-ints-equal(EAX, 0, msg) @@ -212,12 +212,12 @@ if ('onhashchange' in window) { 150 c3/return 151 152 test-compare-kernel-string-with-equal-array: -153 # EAX = kernel-string-equal(_test-Abc-kernel-string, "Abc") +153 # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Abc") 154 # . . push args 155 68/push "Abc"/imm32 156 68/push _test-Abc-kernel-string/imm32 157 # . . call -158 e8/call kernel-string-equal/disp32 +158 e8/call kernel-string-equal?/disp32 159 # . . discard args 160 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 161 # check-ints-equal(EAX, 1, msg) @@ -232,12 +232,12 @@ if ('onhashchange' in window) { 170 c3/return 171 172 test-compare-kernel-string-with-inequal-array: -173 # EAX = kernel-string-equal(_test-Abc-kernel-string, "Adc") +173 # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Adc") 174 # . . push args 175 68/push "Adc"/imm32 176 68/push _test-Abc-kernel-string/imm32 177 # . . call -178 e8/call kernel-string-equal/disp32 +178 e8/call kernel-string-equal?/disp32 179 # . . discard args 180 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 181 # check-ints-equal(EAX, 0, msg) @@ -252,12 +252,12 @@ if ('onhashchange' in window) { 190 c3/return 191 192 test-compare-kernel-string-with-empty-array: -193 # EAX = kernel-string-equal(_test-Abc-kernel-string, "") +193 # EAX = kernel-string-equal?(_test-Abc-kernel-string, "") 194 # . . push args 195 68/push ""/imm32 196 68/push _test-Abc-kernel-string/imm32 197 # . . call -198 e8/call kernel-string-equal/disp32 +198 e8/call kernel-string-equal?/disp32 199 # . . discard args 200 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 201 # check-ints-equal(EAX, 0, msg) @@ -272,12 +272,12 @@ if ('onhashchange' in window) { 210 c3/return 211 212 test-compare-kernel-string-with-shorter-array: -213 # EAX = kernel-string-equal(_test-Abc-kernel-string, "Ab") +213 # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Ab") 214 # . . push args 215 68/push "Ab"/imm32 216 68/push _test-Abc-kernel-string/imm32 217 # . . call -218 e8/call kernel-string-equal/disp32 +218 e8/call kernel-string-equal?/disp32 219 # . . discard args 220 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 221 # check-ints-equal(EAX, 0, msg) @@ -292,12 +292,12 @@ if ('onhashchange' in window) { 230 c3/return 231 232 test-compare-kernel-string-with-longer-array: -233 # EAX = kernel-string-equal(_test-Abc-kernel-string, "Abcd") +233 # EAX = kernel-string-equal?(_test-Abc-kernel-string, "Abcd") 234 # . . push args 235 68/push "Abcd"/imm32 236 68/push _test-Abc-kernel-string/imm32 237 # . . call -238 e8/call kernel-string-equal/disp32 +238 e8/call kernel-string-equal?/disp32 239 # . . discard args 240 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 241 # check-ints-equal(EAX, 0, msg) diff --git a/html/subx/054string-equal.subx.html b/html/subx/054string-equal.subx.html index 2544aca2..be10a769 100644 --- a/html/subx/054string-equal.subx.html +++ b/html/subx/054string-equal.subx.html @@ -74,7 +74,7 @@ if ('onhashchange' in window) { 13 b8/copy-to-EAX 1/imm32/exit 14 cd/syscall 0x80/imm8 15 - 16 string-equal: # s : string, benchmark : string -> EAX : boolean + 16 string-equal?: # s : string, benchmark : string -> EAX : boolean 17 # pseudocode: 18 # if s->length != b->length return false 19 # for i = 0; i < s->length; ++i @@ -104,38 +104,38 @@ if ('onhashchange' in window) { 43 8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX 44 # compare s->length and b->length 45 39/compare 0/mod/indirect 3/rm32/EBX . . . 2/r32/EDX . . # compare *EBX with EDX - 46 75/jump-if-not-equal $string-equal:false/disp8 - 47 $string-equal:lengths: + 46 75/jump-if-not-equal $string-equal?:false/disp8 + 47 $string-equal?:lengths: 48 # var i/ECX : int = 0 49 b9/copy-to-ECX 0/imm32 50 # EBX = &b[i] 51 43/inc-EBX 52 # EAX = &s[i] 53 40/inc-EAX - 54 $string-equal:loop: + 54 $string-equal?:loop: 55 # if i >= s->length return true 56 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX - 57 7d/jump-if-greater-or-equal $string-equal:true/disp8 + 57 7d/jump-if-greater-or-equal $string-equal?:true/disp8 58 # if b[i] != s[i] return false 59 # ESI = s[i] 60 8b/copy 0/mod/indirect 0/rm32/EAX . . . 6/r32/ESI . . # copy *EAX to ESI 61 # compare b[i] with ESI 62 39/compare 0/mod/indirect 3/rm32/EBX . . . 6/r32/ESI . . # compare *EBX with ESI - 63 75/jump-if-not-equal $string-equal:false/disp8 + 63 75/jump-if-not-equal $string-equal?:false/disp8 64 # ++i 65 41/inc-ECX 66 40/inc-EAX 67 43/inc-EBX 68 # loop - 69 eb/jump $string-equal:loop/disp8 - 70 $string-equal:true: + 69 eb/jump $string-equal?:loop/disp8 + 70 $string-equal?:true: 71 # return true 72 b8/copy-to-EAX 1/imm32 - 73 eb/jump $string-equal:end/disp8 - 74 $string-equal:false: + 73 eb/jump $string-equal?:end/disp8 + 74 $string-equal?:false: 75 # return false 76 b8/copy-to-EAX 0/imm32 - 77 $string-equal:end: + 77 $string-equal?:end: 78 # . restore registers 79 5e/pop-to-ESI 80 5b/pop-to-EBX @@ -149,12 +149,12 @@ if ('onhashchange' in window) { 88 # - tests 89 90 test-compare-empty-with-empty-string: - 91 # EAX = string-equal("", "") + 91 # EAX = string-equal?("", "") 92 # . . push args 93 68/push ""/imm32 94 68/push ""/imm32 95 # . . call - 96 e8/call string-equal/disp32 + 96 e8/call string-equal?/disp32 97 # . . discard args 98 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 99 # check-ints-equal(EAX, 1, msg) @@ -169,12 +169,12 @@ if ('onhashchange' in window) { 108 c3/return 109 110 test-compare-empty-with-non-empty-string: # also checks length-mismatch code path -111 # EAX = string-equal("", "Abc") +111 # EAX = string-equal?("", "Abc") 112 # . . push args 113 68/push "Abc"/imm32 114 68/push ""/imm32 115 # . . call -116 e8/call string-equal/disp32 +116 e8/call string-equal?/disp32 117 # . . discard args 118 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 119 # check-ints-equal(EAX, 0, msg) @@ -189,12 +189,12 @@ if ('onhashchange' in window) { 128 c3/return 129 130 test-compare-equal-strings: -131 # EAX = string-equal("Abc", "Abc") +131 # EAX = string-equal?("Abc", "Abc") 132 # . . push args 133 68/push "Abc"/imm32 134 68/push "Abc"/imm32 135 # . . call -136 e8/call string-equal/disp32 +136 e8/call string-equal?/disp32 137 # . . discard args 138 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 139 # check-ints-equal(EAX, 1, msg) @@ -209,12 +209,12 @@ if ('onhashchange' in window) { 148 c3/return 149 150 test-compare-inequal-strings-equal-lengths: -151 # EAX = string-equal("Abc", "Adc") +151 # EAX = string-equal?("Abc", "Adc") 152 # . . push args 153 68/push "Adc"/imm32 154 68/push "Abc"/imm32 155 # . . call -156 e8/call string-equal/disp32 +156 e8/call string-equal?/disp32 157 # . . discard args 158 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 159 # check-ints-equal(EAX, 0, msg) diff --git a/html/subx/055stream.subx.html b/html/subx/055stream.subx.html new file mode 100644 index 00000000..523bb996 --- /dev/null +++ b/html/subx/055stream.subx.html @@ -0,0 +1,140 @@ + + + + +Mu - subx/055stream.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/055stream.subx +
+ 1 # streams: data structure for operating on arrays in a stateful manner
+ 2 #
+ 3 # A stream looks like this:
+ 4 #   write : int  # index at which writes go
+ 5 #   read : int  # index that we've read until
+ 6 #   data : (array byte)  # prefixed by length as usual
+ 7 #
+ 8 # some primitives for operating on streams:
+ 9 #   - clear-stream (clears everything but the data length)
+10 #   - rewind-stream (resets read pointer)
+11 
+12 == code
+13 #   instruction                     effective address                                                   register    displacement    immediate
+14 # . op          subop               mod             rm32          base        index         scale       r32
+15 # . 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
+16 
+17 # main:
+18     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+19     # syscall(exit, Num-test-failures)
+20     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+21     b8/copy-to-EAX  1/imm32/exit
+22     cd/syscall  0x80/imm8
+23 
+24 clear-stream:  # f : (address stream) -> <void>
+25     # . prolog
+26     55/push-EBP
+27     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+28     # . save registers
+29     50/push-EAX
+30     51/push-ECX
+31     # EAX = f
+32     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+33     # ECX = f->length
+34     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EAX+8) to ECX
+35     # ECX = &f->data[f->length]
+36     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EAX+ECX+12 to ECX
+37     # f->write = 0
+38     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+39     # f->read = 0
+40     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         0/imm32           # copy to *(EAX+4)
+41     # EAX = f->data
+42     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xc/imm32         # add to EAX
+43     # while (true)
+44 $clear-stream:loop:
+45     # if EAX >= ECX break
+46     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+47     7d/jump-if-greater-or-equal  $clear-stream:end/disp8
+48     # *EAX = 0
+49     c6          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm8            # copy byte to *EAX
+50     # ++EAX
+51     40/increment-EAX
+52     eb/jump  $clear-stream:loop/disp8
+53 $clear-stream:end:
+54     # . restore registers
+55     59/pop-to-ECX
+56     58/pop-to-EAX
+57     # . epilog
+58     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+59     5d/pop-to-EBP
+60     c3/return
+61 
+62 rewind-stream:  # f : (address stream) -> <void>
+63     # . prolog
+64     55/push-EBP
+65     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+66     # . save registers
+67     50/push-EAX
+68     # EAX = f
+69     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+70     # f->read = 0
+71     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         0/imm32           # copy to *(EAX+4)
+72 $rewind-stream:end:
+73     # . restore registers
+74     58/pop-to-EAX
+75     # . epilog
+76     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+77     5d/pop-to-EBP
+78     c3/return
+
+ + + diff --git a/html/subx/055trace.subx.html b/html/subx/055trace.subx.html deleted file mode 100644 index ea9a152c..00000000 --- a/html/subx/055trace.subx.html +++ /dev/null @@ -1,424 +0,0 @@ - - - - -Mu - subx/055trace.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/055trace.subx -
-  1 # primitives for emitting traces to a trace stream, and for tests to check the trace stream
-  2 #
-  3 # A trace stream looks like this:
-  4 #   read : int  # index that we've read until
-  5 #   write : int  # index at which writes go
-  6 #   data : (array byte)  # prefixed by length as usual
-  7 # In a real trace the data will be in a special segment set aside for the purpose.
-  8 #
-  9 # primitives for operating on traces:
- 10 #   - initialize-trace-stream (update global variable)
- 11 #   - trace: stream, string
- 12 #   - die: stream (exit(1) if using real trace)
- 13 #   - check-trace-contains: stream, string/line, string/message (scans only from stream's read pointer, prints message to stderr on failure, updates stream's read pointer)
- 14 #   - rewind-reads: stream (resets read pointer)
- 15 #   - scan-to-next-line: stream (advance read pointer past next newline)
- 16 #
- 17 # Traces are very fundamental, so many of the helpers we create here won't be
- 18 # used elsewhere; we'll switch to more bounds-checked variants. But here we get
- 19 # bounds-checking for free; we allocate a completely disjoint segment for trace
- 20 # data, and overflowing it will generate a page fault.
- 21 
- 22 == data
- 23 
- 24 # We'll save the address of the trace segment here.
- 25 Trace-stream:
- 26     00 00 00 00
- 27 
- 28 # Fake trace-stream for tests.
- 29 # Also illustrates the layout of the real trace-stream (segment).
- 30 _test-trace-stream:
- 31     # current write index
- 32     00 00 00 00
- 33     # current read index
- 34     00 00 00 00
- 35     # length (= 8)
- 36     08 00 00 00
- 37     # data
- 38     00 00 00 00 00 00 00 00  # 8 bytes
- 39 
- 40 == code
- 41 #   instruction                     effective address                                                   register    displacement    immediate
- 42 # . op          subop               mod             rm32          base        index         scale       r32
- 43 # . 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
- 44 
- 45 # main:
- 46     # run-tests()
- 47     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 48     # syscall(exit, Num-test-failures)
- 49     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 50     b8/copy-to-EAX  1/imm32/exit
- 51     cd/syscall  0x80/imm8
- 52 
- 53 # Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream.
- 54 # The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
- 55 initialize-trace-stream:
- 56     # EAX = new-segment(0x1000)
- 57     # . . push args
- 58     68/push  0x1000/imm32/N
- 59     # . . call
- 60     e8/call  new-segment/disp32
- 61     # . . discard args
- 62     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 63     # copy EAX to *Trace-stream
- 64     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
- 65     # Trace-stream->length = 0x1000/N - 12
- 66     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         0xff4/imm32       # copy 0xff4 to *(EAX+8)
- 67     c3/return
- 68 
- 69 # Append a string to the given trace stream.
- 70 # Silently give up if it's already full. Or truncate the string if there isn't enough room.
- 71 trace:  # t : (address trace-stream), line : string
- 72     # . prolog
- 73     55/push-EBP
- 74     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 75     # . save registers
- 76     50/push-EAX
- 77     51/push-ECX
- 78     52/push-EDX
- 79     53/push-EBX
- 80     56/push-ESI
- 81     57/push-EDI
- 82     # EDI = t
- 83     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
- 84     # ESI = line
- 85     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
- 86     # ECX = t->write
- 87     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
- 88     # EDX = t->length
- 89     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(EDI+8) to EDX
- 90     # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
- 91     # . . push line
- 92     56/push-ESI
- 93     # . . push &t->data[t->length]
- 94     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
- 95     53/push-EBX
- 96     # . . push &t->data[t->write]
- 97     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
- 98     53/push-EBX
- 99     # . . call
-100     e8/call  _append-3/disp32
-101     # . . discard args
-102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-103     # if EAX == 0 return
-104     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EDX
-105     74/jump-if-equal  $trace:end/disp8
-106     # t->write += EAX
-107     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
-108     # refresh ECX = t->write
-109     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
-110     # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
-111     # . . push line
-112     68/push  Newline/imm32
-113     # . . push &t->data[t->length]
-114     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
-115     53/push-EBX
-116     # . . push &t->data[t->write]
-117     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
-118     53/push-EBX
-119     # . . call
-120     e8/call  _append-3/disp32
-121     # . . discard args
-122     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-123     # t->write += EAX
-124     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
-125 $trace:end:
-126     # . restore registers
-127     5f/pop-to-EDI
-128     5e/pop-to-ESI
-129     5b/pop-to-EBX
-130     5a/pop-to-EDX
-131     59/pop-to-ECX
-132     58/pop-to-EAX
-133     # . epilog
-134     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-135     5d/pop-to-EBP
-136     c3/return
-137 
-138 clear-trace-stream:  # t : (address trace-stream)
-139     # . prolog
-140     55/push-EBP
-141     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-142     # . save registers
-143     50/push-EAX
-144     51/push-ECX
-145     # EAX = t
-146     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
-147     # ECX = t->length
-148     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EAX+8) to ECX
-149     # ECX = &t->data[t->length]
-150     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EAX+ECX+12 to ECX
-151     # t->write = 0
-152     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
-153     # t->read = 0
-154     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         0/imm32           # copy to *(EAX+4)
-155     # EAX = t->data
-156     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xc/imm32         # add to EAX
-157     # while (true)
-158 $clear-trace-stream:loop:
-159     # if EAX >= ECX break
-160     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
-161     7d/jump-if-greater-or-equal  $clear-trace-stream:end/disp8
-162     # *EAX = 0
-163     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
-164     # EAX += 4
-165     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm32           # add to EAX
-166     eb/jump  $clear-trace-stream:loop/disp8
-167 $clear-trace-stream:end:
-168     # . restore registers
-169     59/pop-to-ECX
-170     58/pop-to-EAX
-171     # . epilog
-172     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-173     5d/pop-to-EBP
-174     c3/return
-175 
-176 # - tests
-177 
-178 test-trace-single:
-179     # clear-trace-stream(_test-trace-stream)
-180     # . . push args
-181     68/push  _test-trace-stream/imm32
-182     # . . call
-183     e8/call  clear-trace-stream/disp32
-184     # . . discard args
-185     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-186     # trace(_test-trace-stream, "Ab")
-187     # . . push args
-188     68/push  "Ab"/imm32
-189     68/push  _test-trace-stream/imm32
-190     # . . call
-191     e8/call  trace/disp32
-192     # . . discard args
-193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-194     # check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg)
-195     # . . push args
-196     68/push  "F - test-trace-single"/imm32
-197     68/push  0x0a6241/imm32/Ab-newline
-198     # . . push *_test-trace-stream->data
-199     b8/copy-to-EAX  _test-trace-stream/imm32
-200     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-201     # . . call
-202     e8/call  check-ints-equal/disp32
-203     # . . discard args
-204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-205     # end
-206     c3/return
-207 
-208 test-trace-appends:
-209     # clear-trace-stream(_test-trace-stream)
-210     # . . push args
-211     68/push  _test-trace-stream/imm32
-212     # . . call
-213     e8/call  clear-trace-stream/disp32
-214     # . . discard args
-215     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-216     # trace(_test-trace-stream, "C")
-217     # . . push args
-218     68/push  "C"/imm32
-219     68/push  _test-trace-stream/imm32
-220     # . . call
-221     e8/call  trace/disp32
-222     # . . discard args
-223     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-224     # trace(_test-trace-stream, "D")
-225     # . . push args
-226     68/push  "D"/imm32
-227     68/push  _test-trace-stream/imm32
-228     # . . call
-229     e8/call  trace/disp32
-230     # . . discard args
-231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-232     # check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg)
-233     # . . push args
-234     68/push  "F - test-trace-appends"/imm32
-235     68/push  0x0a440a43/imm32/C-newline-D-newline
-236     # . . push *_test-trace-stream->data
-237     b8/copy-to-EAX  _test-trace-stream/imm32
-238     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-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     # end
-244     c3/return
-245 
-246 test-trace-empty-line:
-247     # clear-trace-stream(_test-trace-stream)
-248     # . . push args
-249     68/push  _test-trace-stream/imm32
-250     # . . call
-251     e8/call  clear-trace-stream/disp32
-252     # . . discard args
-253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-254     # trace(_test-trace-stream, "")
-255     # . . push args
-256     68/push  ""/imm32
-257     68/push  _test-trace-stream/imm32
-258     # . . call
-259     e8/call  trace/disp32
-260     # . . discard args
-261     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-262     # check-ints-equal(*_test-trace-stream->data, 0, msg)
-263     # . . push args
-264     68/push  "F - test-trace-empty-line"/imm32
-265     68/push  0/imm32
-266     # . . push *_test-trace-stream->data
-267     b8/copy-to-EAX  _test-trace-stream/imm32
-268     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-269     # . . call
-270     e8/call  check-ints-equal/disp32
-271     # . . discard args
-272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-273     # end
-274     c3/return
-275 
-276 # - helpers
-277 
-278 # 3-argument variant of _append
-279 _append-3:  # out : address, outend : address, s : (array byte) -> num_bytes_appended/EAX
-280     # . prolog
-281     55/push-EBP
-282     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-283     # . save registers
-284     51/push-ECX
-285     # _append-4(out, outend, &s->data[0], &s->data[s->length]) -> num_bytes_appended/EAX
-286     # . . push &s->data[s->length]
-287     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
-288     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-289     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
-290     51/push-ECX
-291     # . . push &s->data[0]
-292     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
-293     51/push-ECX
-294     # . . push outend
-295     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-296     # . . push out
-297     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-298     # . . call
-299     e8/call  _append-4/disp32
-300     # . . discard args
-301     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-302 $_append-3:end:
-303     # . restore registers
-304     59/pop-to-ECX
-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 # 4-argument variant of _append
-311 _append-4:  # out : address, outend : address, in : address, inend : address -> num_bytes_appended/EAX
-312     # . prolog
-313     55/push-EBP
-314     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-315     # . save registers
-316     51/push-ECX
-317     52/push-EDX
-318     53/push-EBX
-319     56/push-ESI
-320     57/push-EDI
-321     # EAX/num_bytes_appended = 0
-322     b8/copy-to-EAX  0/imm32
-323     # EDI = out
-324     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
-325     # EDX = outend
-326     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
-327     # ESI = in
-328     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0x10/disp8      .                 # copy *(EBP+16) to ESI
-329     # ECX = inend
-330     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x14/disp8      .                 # copy *(EBP+20) to ECX
-331 $_append-4:loop:
-332     # if ESI/in >= ECX/inend break
-333     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ESI with ECX
-334     7d/jump-if-greater-or-equal  $_append-4:end/disp8
-335     # if EDI/out >= EDX/outend break  (for now silently ignore filled up buffer)
-336     39/compare                      3/mod/direct    7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDI with EDX
-337     7d/jump-if-greater-or-equal  $_append-4:end/disp8
-338     # copy one byte from ESI/in to EDI/out
-339     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/BL    .               .                 # copy byte at *ESI to BL
-340     88/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *EDI
-341     # updates
-342     40/increment-EAX
-343     46/increment-ESI
-344     47/increment-EDI
-345     eb/jump  $_append-4:loop/disp8
-346 $_append-4:end:
-347     # . restore registers
-348     5f/pop-to-EDI
-349     5e/pop-to-ESI
-350     5b/pop-to-EBX
-351     5a/pop-to-EDX
-352     59/pop-to-ECX
-353     # . epilog
-354     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-355     5d/pop-to-EBP
-356     c3/return
-357 
-358 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/056trace.subx.html b/html/subx/056trace.subx.html new file mode 100644 index 00000000..7bffc6b4 --- /dev/null +++ b/html/subx/056trace.subx.html @@ -0,0 +1,423 @@ + + + + +Mu - subx/056trace.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/056trace.subx +
+  1 # primitives for emitting traces to a 'trace' stream, and for tests to make assertions on its contents
+  2 #
+  3 # A trace stream looks like a regular stream:
+  4 #   write : int  # index at which writes go
+  5 #   read : int  # index that we've read until
+  6 #   data : (array byte)  # prefixed by length as usual
+  7 # In a real trace the data will be in a special segment set aside for the purpose.
+  8 #
+  9 # primitives for operating on traces:
+ 10 #   - initialize-trace-stream (update global variable)
+ 11 #   - trace: stream, string
+ 12 #   - die: stream (exit(1) if using real trace)
+ 13 #   - check-trace-contains: stream, string/line, string/message (scans only from stream's read pointer, prints message to stderr on failure, updates stream's read pointer)
+ 14 #   - scan-to-next-line: stream (advance read pointer past next newline)
+ 15 #
+ 16 # Traces are very fundamental, so many of the helpers we create here won't be
+ 17 # used elsewhere; we'll switch to more bounds-checked variants. But here we get
+ 18 # bounds-checking for free; we allocate a completely disjoint segment for trace
+ 19 # data, and overflowing it will generate a page fault.
+ 20 
+ 21 == data
+ 22 
+ 23 # We'll save the address of the trace segment here.
+ 24 Trace-stream:
+ 25     00 00 00 00
+ 26 
+ 27 # Fake trace-stream for tests.
+ 28 # Also illustrates the layout of the real trace-stream (segment).
+ 29 _test-trace-stream:
+ 30     # current write index
+ 31     00 00 00 00
+ 32     # current read index
+ 33     00 00 00 00
+ 34     # length (= 8)
+ 35     08 00 00 00
+ 36     # data
+ 37     00 00 00 00 00 00 00 00  # 8 bytes
+ 38 
+ 39 == code
+ 40 #   instruction                     effective address                                                   register    displacement    immediate
+ 41 # . op          subop               mod             rm32          base        index         scale       r32
+ 42 # . 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
+ 43 
+ 44 # main:
+ 45     # run-tests()
+ 46     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 47     # syscall(exit, Num-test-failures)
+ 48     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 49     b8/copy-to-EAX  1/imm32/exit
+ 50     cd/syscall  0x80/imm8
+ 51 
+ 52 # Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream.
+ 53 # The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
+ 54 initialize-trace-stream:
+ 55     # EAX = new-segment(0x1000)
+ 56     # . . push args
+ 57     68/push  0x1000/imm32/N
+ 58     # . . call
+ 59     e8/call  new-segment/disp32
+ 60     # . . discard args
+ 61     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 62     # copy EAX to *Trace-stream
+ 63     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy EAX to *Trace-stream
+ 64     # Trace-stream->length = 0x1000/N - 12
+ 65     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         0xff4/imm32       # copy 0xff4 to *(EAX+8)
+ 66     c3/return
+ 67 
+ 68 # Append a string to the given trace stream.
+ 69 # Silently give up if it's already full. Or truncate the string if there isn't enough room.
+ 70 trace:  # t : (address trace-stream), line : string
+ 71     # . prolog
+ 72     55/push-EBP
+ 73     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 74     # . save registers
+ 75     50/push-EAX
+ 76     51/push-ECX
+ 77     52/push-EDX
+ 78     53/push-EBX
+ 79     56/push-ESI
+ 80     57/push-EDI
+ 81     # EDI = t
+ 82     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+ 83     # ESI = line
+ 84     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+ 85     # ECX = t->write
+ 86     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+ 87     # EDX = t->length
+ 88     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(EDI+8) to EDX
+ 89     # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
+ 90     # . . push line
+ 91     56/push-ESI
+ 92     # . . push &t->data[t->length]
+ 93     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
+ 94     53/push-EBX
+ 95     # . . push &t->data[t->write]
+ 96     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
+ 97     53/push-EBX
+ 98     # . . call
+ 99     e8/call  _append-3/disp32
+100     # . . discard args
+101     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+102     # if EAX == 0 return
+103     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EDX
+104     74/jump-if-equal  $trace:end/disp8
+105     # t->write += EAX
+106     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
+107     # refresh ECX = t->write
+108     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+109     # EAX = _append-3(&t->data[t->write], &t->data[t->length], line)
+110     # . . push line
+111     68/push  Newline/imm32
+112     # . . push &t->data[t->length]
+113     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+EDX+12 to EBX
+114     53/push-EBX
+115     # . . push &t->data[t->write]
+116     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
+117     53/push-EBX
+118     # . . call
+119     e8/call  _append-3/disp32
+120     # . . discard args
+121     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+122     # t->write += EAX
+123     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
+124 $trace:end:
+125     # . restore registers
+126     5f/pop-to-EDI
+127     5e/pop-to-ESI
+128     5b/pop-to-EBX
+129     5a/pop-to-EDX
+130     59/pop-to-ECX
+131     58/pop-to-EAX
+132     # . epilog
+133     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+134     5d/pop-to-EBP
+135     c3/return
+136 
+137 clear-trace-stream:  # t : (address trace-stream)
+138     # . prolog
+139     55/push-EBP
+140     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+141     # . save registers
+142     50/push-EAX
+143     51/push-ECX
+144     # EAX = t
+145     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+146     # ECX = t->length
+147     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EAX+8) to ECX
+148     # ECX = &t->data[t->length]
+149     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EAX+ECX+12 to ECX
+150     # t->write = 0
+151     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+152     # t->read = 0
+153     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         0/imm32           # copy to *(EAX+4)
+154     # EAX = t->data
+155     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xc/imm32         # add to EAX
+156     # while (true)
+157 $clear-trace-stream:loop:
+158     # if EAX >= ECX break
+159     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+160     7d/jump-if-greater-or-equal  $clear-trace-stream:end/disp8
+161     # *EAX = 0
+162     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+163     # EAX += 4
+164     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm32           # add to EAX
+165     eb/jump  $clear-trace-stream:loop/disp8
+166 $clear-trace-stream:end:
+167     # . restore registers
+168     59/pop-to-ECX
+169     58/pop-to-EAX
+170     # . epilog
+171     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+172     5d/pop-to-EBP
+173     c3/return
+174 
+175 # - tests
+176 
+177 test-trace-single:
+178     # clear-trace-stream(_test-trace-stream)
+179     # . . push args
+180     68/push  _test-trace-stream/imm32
+181     # . . call
+182     e8/call  clear-trace-stream/disp32
+183     # . . discard args
+184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+185     # trace(_test-trace-stream, "Ab")
+186     # . . push args
+187     68/push  "Ab"/imm32
+188     68/push  _test-trace-stream/imm32
+189     # . . call
+190     e8/call  trace/disp32
+191     # . . discard args
+192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+193     # check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg)
+194     # . . push args
+195     68/push  "F - test-trace-single"/imm32
+196     68/push  0x0a6241/imm32/Ab-newline
+197     # . . push *_test-trace-stream->data
+198     b8/copy-to-EAX  _test-trace-stream/imm32
+199     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+200     # . . call
+201     e8/call  check-ints-equal/disp32
+202     # . . discard args
+203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+204     # end
+205     c3/return
+206 
+207 test-trace-appends:
+208     # clear-trace-stream(_test-trace-stream)
+209     # . . push args
+210     68/push  _test-trace-stream/imm32
+211     # . . call
+212     e8/call  clear-trace-stream/disp32
+213     # . . discard args
+214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+215     # trace(_test-trace-stream, "C")
+216     # . . push args
+217     68/push  "C"/imm32
+218     68/push  _test-trace-stream/imm32
+219     # . . call
+220     e8/call  trace/disp32
+221     # . . discard args
+222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+223     # trace(_test-trace-stream, "D")
+224     # . . push args
+225     68/push  "D"/imm32
+226     68/push  _test-trace-stream/imm32
+227     # . . call
+228     e8/call  trace/disp32
+229     # . . discard args
+230     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+231     # check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg)
+232     # . . push args
+233     68/push  "F - test-trace-appends"/imm32
+234     68/push  0x0a440a43/imm32/C-newline-D-newline
+235     # . . push *_test-trace-stream->data
+236     b8/copy-to-EAX  _test-trace-stream/imm32
+237     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+238     # . . call
+239     e8/call  check-ints-equal/disp32
+240     # . . discard args
+241     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+242     # end
+243     c3/return
+244 
+245 test-trace-empty-line:
+246     # clear-trace-stream(_test-trace-stream)
+247     # . . push args
+248     68/push  _test-trace-stream/imm32
+249     # . . call
+250     e8/call  clear-trace-stream/disp32
+251     # . . discard args
+252     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+253     # trace(_test-trace-stream, "")
+254     # . . push args
+255     68/push  ""/imm32
+256     68/push  _test-trace-stream/imm32
+257     # . . call
+258     e8/call  trace/disp32
+259     # . . discard args
+260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+261     # check-ints-equal(*_test-trace-stream->data, 0, msg)
+262     # . . push args
+263     68/push  "F - test-trace-empty-line"/imm32
+264     68/push  0/imm32
+265     # . . push *_test-trace-stream->data
+266     b8/copy-to-EAX  _test-trace-stream/imm32
+267     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+268     # . . call
+269     e8/call  check-ints-equal/disp32
+270     # . . discard args
+271     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+272     # end
+273     c3/return
+274 
+275 # - helpers
+276 
+277 # 3-argument variant of _append
+278 _append-3:  # out : address, outend : address, s : (array byte) -> num_bytes_appended/EAX
+279     # . prolog
+280     55/push-EBP
+281     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+282     # . save registers
+283     51/push-ECX
+284     # _append-4(out, outend, &s->data[0], &s->data[s->length]) -> num_bytes_appended/EAX
+285     # . . push &s->data[s->length]
+286     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
+287     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+288     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
+289     51/push-ECX
+290     # . . push &s->data[0]
+291     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
+292     51/push-ECX
+293     # . . push outend
+294     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+295     # . . push out
+296     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+297     # . . call
+298     e8/call  _append-4/disp32
+299     # . . discard args
+300     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+301 $_append-3:end:
+302     # . restore registers
+303     59/pop-to-ECX
+304     # . epilog
+305     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+306     5d/pop-to-EBP
+307     c3/return
+308 
+309 # 4-argument variant of _append
+310 _append-4:  # out : address, outend : address, in : address, inend : address -> num_bytes_appended/EAX
+311     # . prolog
+312     55/push-EBP
+313     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+314     # . save registers
+315     51/push-ECX
+316     52/push-EDX
+317     53/push-EBX
+318     56/push-ESI
+319     57/push-EDI
+320     # EAX/num_bytes_appended = 0
+321     b8/copy-to-EAX  0/imm32
+322     # EDI = out
+323     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+324     # EDX = outend
+325     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+326     # ESI = in
+327     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0x10/disp8      .                 # copy *(EBP+16) to ESI
+328     # ECX = inend
+329     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x14/disp8      .                 # copy *(EBP+20) to ECX
+330 $_append-4:loop:
+331     # if ESI/in >= ECX/inend break
+332     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ESI with ECX
+333     7d/jump-if-greater-or-equal  $_append-4:end/disp8
+334     # if EDI/out >= EDX/outend break  (for now silently ignore filled up buffer)
+335     39/compare                      3/mod/direct    7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDI with EDX
+336     7d/jump-if-greater-or-equal  $_append-4:end/disp8
+337     # copy one byte from ESI/in to EDI/out
+338     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/BL    .               .                 # copy byte at *ESI to BL
+339     88/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           3/r32/BL    .               .                 # copy byte at BL to *EDI
+340     # updates
+341     40/increment-EAX
+342     46/increment-ESI
+343     47/increment-EDI
+344     eb/jump  $_append-4:loop/disp8
+345 $_append-4:end:
+346     # . restore registers
+347     5f/pop-to-EDI
+348     5e/pop-to-ESI
+349     5b/pop-to-EBX
+350     5a/pop-to-EDX
+351     59/pop-to-ECX
+352     # . epilog
+353     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+354     5d/pop-to-EBP
+355     c3/return
+356 
+357 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/056write.subx.html b/html/subx/056write.subx.html deleted file mode 100644 index f9f81766..00000000 --- a/html/subx/056write.subx.html +++ /dev/null @@ -1,268 +0,0 @@ - - - - -Mu - subx/056write.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/056write.subx -
-  1 # write: like _write, but also support in-memory streams in addition to file
-  2 # descriptors.
-  3 #
-  4 # Our first dependency-injected and testable primitive. We can pass it either
-  5 # a file descriptor or an address to a stream. If a file descriptor is passed
-  6 # in, we _write to it using the right syscall. If a 'fake file descriptor' or
-  7 # stream is passed in, we append to the stream. This lets us redirect output
-  8 # in tests and check it later.
-  9 #
- 10 # We assume our data segment will never begin at an address shorter than
- 11 # 0x08000000, so any smaller arguments are assumed to be real file descriptors.
- 12 #
- 13 # A stream looks like this:
- 14 #   read: int  # index at which to read next
- 15 #   write: int  # index at which writes go
- 16 #   data: (array byte)  # prefixed by length as usual
- 17 
- 18 == code
- 19 #   instruction                     effective address                                                   register    displacement    immediate
- 20 # . op          subop               mod             rm32          base        index         scale       r32
- 21 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
- 22 
- 23 # main:
- 24     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 25     # syscall(exit, Num-test-failures)
- 26     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 27     b8/copy-to-EAX  1/imm32/exit
- 28     cd/syscall  0x80/imm8
- 29 
- 30 # TODO: come up with a way to signal when a write to disk fails
- 31 write:  # f : fd or (address stream), s : (address array byte) -> <void>
- 32     # . prolog
- 33     55/push-EBP
- 34     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 35     # if (f < 0x08000000) _write(f, s), return  # f can't be a user-mode address, so treat it as a kernel file descriptor
- 36     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         0x08000000/imm32  # compare *(EBP+8)
- 37     7d/jump-if-greater-or-equal  $write:fake/disp8
- 38     # . . push args
- 39     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 40     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 41     # . . call
- 42     e8/call  _write/disp32
- 43     # . . discard args
- 44     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 45     eb/jump  $write:end/disp8
- 46 $write:fake:
- 47     # otherwise, treat 'f' as a stream to append to
- 48     # . save registers
- 49     50/push-EAX
- 50     51/push-ECX
- 51     52/push-EDX
- 52     53/push-EBX
- 53     # ECX = f
- 54     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
- 55     # EDX = f->write
- 56     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
- 57     # EBX = f->length
- 58     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(ECX+8) to EBX
- 59     # EAX = _append-3(&f->data[f->write], &f->data[f->length], s)
- 60     # . . push s
- 61     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 62     # . . push &f->data[f->length]
- 63     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  3/index/EBX   .           3/r32/EBX   0xc/disp8       .                 # copy ECX+EBX+12 to EBX
- 64     53/push-EBX
- 65     # . . push &f->data[f->write]
- 66     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy ECX+EDX+12 to EBX
- 67     53/push-EBX
- 68     # . . call
- 69     e8/call  _append-3/disp32
- 70     # . . discard args
- 71     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 72     # f->write += EAX
- 73     01/add                          0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # add EAX to *ECX
- 74     # . restore registers
- 75     5b/pop-to-EBX
- 76     5a/pop-to-EDX
- 77     59/pop-to-ECX
- 78     58/pop-to-EAX
- 79 $write:end:
- 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 clear-stream:  # f : (address stream) -> <void>
- 86     # . prolog
- 87     55/push-EBP
- 88     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 89     # . save registers
- 90     50/push-EAX
- 91     51/push-ECX
- 92     # EAX = f
- 93     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
- 94     # ECX = f->length
- 95     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EAX+8) to ECX
- 96     # ECX = &f->data[f->length]
- 97     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/EAX  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EAX+ECX+12 to ECX
- 98     # f->write = 0
- 99     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
-100     # f->read = 0
-101     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         0/imm32           # copy to *(EAX+4)
-102     # EAX = f->data
-103     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xc/imm32         # add to EAX
-104     # while (true)
-105 $clear-stream:loop:
-106     # if EAX >= ECX break
-107     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
-108     7d/jump-if-greater-or-equal  $clear-stream:end/disp8
-109     # *EAX = 0
-110     c6          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm8            # copy byte to *EAX
-111     # ++EAX
-112     40/increment-EAX
-113     eb/jump  $clear-stream:loop/disp8
-114 $clear-stream:end:
-115     # . restore registers
-116     59/pop-to-ECX
-117     58/pop-to-EAX
-118     # . epilog
-119     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-120     5d/pop-to-EBP
-121     c3/return
-122 
-123 test-write-single:
-124     # clear-stream(_test-stream)
-125     # . . push args
-126     68/push  _test-stream/imm32
-127     # . . call
-128     e8/call  clear-stream/disp32
-129     # . . discard args
-130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-131     # write(_test-stream, "Ab")
-132     # . . push args
-133     68/push  "Ab"/imm32
-134     68/push  _test-stream/imm32
-135     # . . call
-136     e8/call  write/disp32
-137     # . . discard args
-138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-139     # check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg)
-140     # . . push args
-141     68/push  "F - test-write-single"/imm32
-142     68/push  0x006241/imm32/Ab
-143     # . . push *_test-stream->data
-144     b8/copy-to-EAX  _test-stream/imm32
-145     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-146     # . . call
-147     e8/call  check-ints-equal/disp32
-148     # . . discard args
-149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-150     # end
-151     c3/return
-152 
-153 test-write-appends:
-154     # clear-stream(_test-stream)
-155     # . . push args
-156     68/push  _test-stream/imm32
-157     # . . call
-158     e8/call  clear-stream/disp32
-159     # . . discard args
-160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-161     # write(_test-stream, "C")
-162     # . . push args
-163     68/push  "C"/imm32
-164     68/push  _test-stream/imm32
-165     # . . call
-166     e8/call  write/disp32
-167     # . . discard args
-168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-169     # write(_test-stream, "D")
-170     # . . push args
-171     68/push  "D"/imm32
-172     68/push  _test-stream/imm32
-173     # . . call
-174     e8/call  write/disp32
-175     # . . discard args
-176     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-177     # check-ints-equal(*_test-stream->data, 43/C 44/D 00 00, msg)
-178     # . . push args
-179     68/push  "F - test-write-appends"/imm32
-180     68/push  0x00004443/imm32/C-D
-181     # . . push *_test-stream->data
-182     b8/copy-to-EAX  _test-stream/imm32
-183     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-184     # . . call
-185     e8/call  check-ints-equal/disp32
-186     # . . discard args
-187     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-188     # end
-189     c3/return
-190 
-191 == data
-192 
-193 _test-stream:
-194     # current write index
-195     00 00 00 00
-196     # current read index
-197     00 00 00 00
-198     # length (= 8)
-199     08 00 00 00
-200     # data
-201     00 00 00 00 00 00 00 00  # 8 bytes
-202 
-203 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/057stop.subx.html b/html/subx/057stop.subx.html deleted file mode 100644 index 3278961b..00000000 --- a/html/subx/057stop.subx.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - -Mu - subx/057stop.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/057stop.subx -
-  1 # stop: dependency-injected wrapper around the exit() syscall
-  2 #
-  3 # We'd like to be able to write tests for functions that call exit(), and to
-  4 # make assertions about whether they exit() or not in a given situation. To
-  5 # achieve this we'll call exit() via a smarter wrapper called 'stop'.
-  6 #
-  7 # In the context of a test, calling a function X that calls 'stop' (directly
-  8 # or through further intervening calls) will unwind the stack until X returns,
-  9 # so that we can say check any further assertions after the execution of X. To
- 10 # achieve this end, we'll pass the return address of X as a 'target' argument
- 11 # into X, plumbing it through to 'stop'. When 'stop' gets a non-null target it
- 12 # unwinds the stack until the target. If it gets a null target it calls
- 13 # exit().
- 14 #
- 15 # We'd also like to get the exit status out of 'stop', so we'll combine the
- 16 # input target with an output status parameter into a type called 'exit-descriptor'.
- 17 #
- 18 # So the exit-descriptor looks like this:
- 19 #   target : address  # return address for 'stop' to unwind to
- 20 #   value : int  # exit status stop was called with
- 21 #
- 22 # 'stop' thus takes two parameters: an exit-descriptor and the exit status.
- 23 #
- 24 # 'stop' won't bother cleaning up any other processor state besides the stack,
- 25 # such as registers. Only ESP will have a well-defined value after 'stop'
- 26 # returns. (This is a poor man's setjmp/longjmp, if you know what that is.)
- 27 #
- 28 # Before you can call any function that may call 'stop', you need to pass in an
- 29 # exit-descriptor to it. To create an exit-descriptor use 'tailor-exit-descriptor'
- 30 # below. It's not the most pleasant abstraction in the world.
- 31 #
- 32 # An exit-descriptor's target is its input, computed during 'tailor-exit-descriptor'.
- 33 # Its value is its output, computed during stop and available to the test.
- 34 
- 35 == code
- 36 #   instruction                     effective address                                                   register    displacement    immediate
- 37 # . op          subop               mod             rm32          base        index         scale       r32
- 38 # . 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
- 39 
- 40 # main:
- 41     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 42 #?     e8/call test-stop-skips-returns-on-exit/disp32
- 43     # syscall(exit, Num-test-failures)
- 44     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 45     b8/copy-to-EAX  1/imm32/exit
- 46     cd/syscall  0x80/imm8
- 47 
- 48 # Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to
- 49 # the stack.
- 50 # Ugly that we need to know the size of args, but so it goes.
- 51 tailor-exit-descriptor:  # ed : (address exit-descriptor), nbytes : int -> <void>
- 52     # . prolog
- 53     55/push-EBP
- 54     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 55     # . save registers
- 56     50/push-EAX
- 57     51/push-ECX
- 58     # EAX = nbytes
- 59     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
- 60     # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor.
- 61     # The return address for a call in the caller's body will be at:
- 62     #   X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address)
- 63     #   X-12 if the caller takes 8 bytes of args
- 64     #   ..and so on
- 65     # That's the value we need to return: X-nbytes-4
- 66     #
- 67     # However, we also need to account for the perturbance to ESP caused by the
- 68     # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4
- 69     # bytes for the return address and 4 bytes to push EBP above.
- 70     # So EBP at this point is X-16.
- 71     #
- 72     # So the return address for the next call in the caller is:
- 73     #   EBP+8 if the caller takes 4 bytes of args
- 74     #   EBP+4 if the caller takes 8 bytes of args
- 75     #   EBP if the caller takes 12 bytes of args
- 76     #   EBP-4 if the caller takes 16 bytes of args
- 77     #   ..and so on
- 78     # That's EBP+12-nbytes.
- 79     # option 1: 6 + 3 bytes
- 80 #?     2d/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           .           .               8/imm32           # subtract from EAX
- 81 #?     8d/copy-address                 0/mod/indirect  4/rm32/sib    5/base/EBP  0/index/EAX   .           0/r32/EAX   .               .                 # copy EBP+EAX to EAX
- 82     # option 2: 2 + 4 bytes
- 83     f7          3/subop/negate      3/mod/direct    0/rm32/EAX    .           .             .           .           .               .                 # negate EAX
- 84     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    5/base/EBP  0/index/EAX   .           0/r32/EAX   0xc/disp8         .               # copy EBP+EAX+12 to EAX
- 85     # copy EAX to ed->target
- 86     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
- 87     89/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ECX
- 88     # initialize ed->value
- 89     c7          0/subop/copy        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # copy to *(ECX+4)
- 90 $tailor-exit-descriptor:end:
- 91     # . restore registers
- 92     59/pop-to-ECX
- 93     58/pop-to-EAX
- 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 stop:  # ed : (address exit-descriptor), value : int
-100     # no prolog; one way or another, we're going to clobber registers
-101     # EAX = ed
-102     8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   4/disp8         .                 # copy *(ESP+4) to EAX
-103     # exit(value) if ed->target == 0
-104     81          7/subop/compare     0/mod/indirect  0/rm32/EAX    .           .             .           .           .               0/imm32           # compare *EAX
-105     75/jump-if-not-equal  $stop:fake/disp8
-106     # syscall(exit, value)
-107     8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           3/r32/EBX   8/disp8         .                 # copy *(ESP+8) to EBX
-108     b8/copy-to-EAX  1/imm32/exit
-109     cd/syscall  0x80/imm8
-110 $stop:fake:
-111     # ed->value = value+1
-112     8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   8/disp8         .                 # copy *(ESP+8) to ECX
-113     41/inc-ECX
-114     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(EAX+4)
-115     # non-local jump to ed->target
-116     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy *EAX to ESP
-117 $stop:end:
-118     c3/return  # doesn't return to caller
-119 
-120 test-stop-skips-returns-on-exit:
-121     # This looks like the standard prolog, but is here for different reasons.
-122     # A function calling 'stop' can't rely on EBP persisting past the call.
-123     #
-124     # Use EBP here as a stable base to refer to locals and arguments from in the
-125     # presence of push/pop/call instructions.
-126     # *Don't* use EBP as a way to restore ESP.
-127     55/push-EBP
-128     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-129     # Make room for an exit descriptor on the stack. That's almost always the
-130     # right place for it, available only as long as it's legal to use. Once this
-131     # containing function returns we'll need a new exit descriptor.
-132     # var ed/EAX : (address exit-descriptor)
-133     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
-134     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
-135     # Size the exit-descriptor precisely for the next call below, to _test-stop-1.
-136     # tailor-exit-descriptor(ed, 4)
-137     # . . push args
-138     68/push  4/imm32/nbytes-of-args-for-_test-stop-1
-139     50/push-EAX
-140     # . . call
-141     e8/call  tailor-exit-descriptor/disp32
-142     # . . discard args
-143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-144     # . _test-stop-1(ed)
-145     # . . push args
-146     50/push-EAX
-147     # . . call
-148     e8/call  _test-stop-1/disp32
-149     # registers except ESP may be clobbered at this point
-150     # restore args
-151     58/pop-to-EAX
-152     # check that _test-stop-1 tried to call exit(1)
-153     # check-ints-equal(ed->value, 2, msg)  # i.e. stop was called with value 1
-154     # . . push args
-155     68/push  "F - test-stop-skips-returns-on-exit"/imm32
-156     68/push  2/imm32
-157     # . . push ed->value
-158     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-159     # . . call
-160     e8/call  check-ints-equal/disp32
-161     # . . discard args
-162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-163     # . epilog
-164     # don't restore ESP from EBP; manually reclaim locals
-165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-166     5d/pop-to-EBP
-167     c3/return
-168 
-169 _test-stop-1:  # ed : (address exit-descriptor)
-170     # . prolog
-171     55/push-EBP
-172     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-173     # _test-stop-2(ed)
-174     # . . push args
-175     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-176     # . . call
-177     e8/call  _test-stop-2/disp32
-178     # should never get past this point
-179 $_test-stop-1:dead-end:
-180     # . . discard args
-181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-182     # signal test failed: check-ints-equal(1, 0, msg)
-183     # . . push args
-184     68/push  "F - test-stop-skips-returns-on-exit"/imm32
-185     68/push  0/imm32
-186     68/push  1/imm32
-187     # . . call
-188     e8/call  check-ints-equal/disp32
-189     # . . discard args
-190     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-191     # . epilog
-192     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-193     5d/pop-to-EBP
-194     c3/return
-195 
-196 _test-stop-2:  # ed : (address exit-descriptor)
-197     # . prolog
-198     55/push-EBP
-199     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-200     # . stop(ed, 1)
-201     # . . push args
-202     68/push  1/imm32
-203     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-204     # . . call
-205     e8/call  stop/disp32
-206     # should never get past this point
-207 $_test-stop-2:dead-end:
-208     # . epilog
-209     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-210     5d/pop-to-EBP
-211     c3/return
-212 
-213 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/057write.subx.html b/html/subx/057write.subx.html new file mode 100644 index 00000000..fc3af24d --- /dev/null +++ b/html/subx/057write.subx.html @@ -0,0 +1,230 @@ + + + + +Mu - subx/057write.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/057write.subx +
+  1 # write: like _write, but also support in-memory streams in addition to file
+  2 # descriptors.
+  3 #
+  4 # Our first dependency-injected and testable primitive. We can pass it either
+  5 # a file descriptor or an address to a stream. If a file descriptor is passed
+  6 # in, we _write to it using the right syscall. If a 'fake file descriptor' or
+  7 # stream is passed in, we append to the stream. This lets us redirect output
+  8 # in tests and check it later.
+  9 #
+ 10 # We assume our data segment will never begin at an address shorter than
+ 11 # 0x08000000, so any smaller arguments are assumed to be real file descriptors.
+ 12 #
+ 13 # A stream looks like this:
+ 14 #   read: int  # index at which to read next
+ 15 #   write: int  # index at which writes go
+ 16 #   data: (array byte)  # prefixed by length as usual
+ 17 
+ 18 == code
+ 19 #   instruction                     effective address                                                   register    displacement    immediate
+ 20 # . op          subop               mod             rm32          base        index         scale       r32
+ 21 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
+ 22 
+ 23 # main:
+ 24     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 25     # syscall(exit, Num-test-failures)
+ 26     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 27     b8/copy-to-EAX  1/imm32/exit
+ 28     cd/syscall  0x80/imm8
+ 29 
+ 30 # TODO: come up with a way to signal when a write to disk fails
+ 31 write:  # f : fd or (address stream), s : (address array byte) -> <void>
+ 32     # . prolog
+ 33     55/push-EBP
+ 34     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 35     # if (f < 0x08000000) _write(f, s), return  # f can't be a user-mode address, so treat it as a kernel file descriptor
+ 36     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         0x08000000/imm32  # compare *(EBP+8)
+ 37     7d/jump-if-greater-or-equal  $write:fake/disp8
+ 38     # . . push args
+ 39     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 40     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 41     # . . call
+ 42     e8/call  _write/disp32
+ 43     # . . discard args
+ 44     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 45     eb/jump  $write:end/disp8
+ 46 $write:fake:
+ 47     # otherwise, treat 'f' as a stream to append to
+ 48     # . save registers
+ 49     50/push-EAX
+ 50     51/push-ECX
+ 51     52/push-EDX
+ 52     53/push-EBX
+ 53     # ECX = f
+ 54     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+ 55     # EDX = f->write
+ 56     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+ 57     # EBX = f->length
+ 58     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(ECX+8) to EBX
+ 59     # EAX = _append-3(&f->data[f->write], &f->data[f->length], s)
+ 60     # . . push s
+ 61     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 62     # . . push &f->data[f->length]
+ 63     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  3/index/EBX   .           3/r32/EBX   0xc/disp8       .                 # copy ECX+EBX+12 to EBX
+ 64     53/push-EBX
+ 65     # . . push &f->data[f->write]
+ 66     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           3/r32/EBX   0xc/disp8       .                 # copy ECX+EDX+12 to EBX
+ 67     53/push-EBX
+ 68     # . . call
+ 69     e8/call  _append-3/disp32
+ 70     # . . discard args
+ 71     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 72     # f->write += EAX
+ 73     01/add                          0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # add EAX to *ECX
+ 74     # . restore registers
+ 75     5b/pop-to-EBX
+ 76     5a/pop-to-EDX
+ 77     59/pop-to-ECX
+ 78     58/pop-to-EAX
+ 79 $write:end:
+ 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-write-single:
+ 86     # clear-stream(_test-stream)
+ 87     # . . push args
+ 88     68/push  _test-stream/imm32
+ 89     # . . call
+ 90     e8/call  clear-stream/disp32
+ 91     # . . discard args
+ 92     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 93     # write(_test-stream, "Ab")
+ 94     # . . push args
+ 95     68/push  "Ab"/imm32
+ 96     68/push  _test-stream/imm32
+ 97     # . . call
+ 98     e8/call  write/disp32
+ 99     # . . discard args
+100     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+101     # check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg)
+102     # . . push args
+103     68/push  "F - test-write-single"/imm32
+104     68/push  0x006241/imm32/Ab
+105     # . . push *_test-stream->data
+106     b8/copy-to-EAX  _test-stream/imm32
+107     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+108     # . . call
+109     e8/call  check-ints-equal/disp32
+110     # . . discard args
+111     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+112     # end
+113     c3/return
+114 
+115 test-write-appends:
+116     # clear-stream(_test-stream)
+117     # . . push args
+118     68/push  _test-stream/imm32
+119     # . . call
+120     e8/call  clear-stream/disp32
+121     # . . discard args
+122     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+123     # write(_test-stream, "C")
+124     # . . push args
+125     68/push  "C"/imm32
+126     68/push  _test-stream/imm32
+127     # . . call
+128     e8/call  write/disp32
+129     # . . discard args
+130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+131     # write(_test-stream, "D")
+132     # . . push args
+133     68/push  "D"/imm32
+134     68/push  _test-stream/imm32
+135     # . . call
+136     e8/call  write/disp32
+137     # . . discard args
+138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+139     # check-ints-equal(*_test-stream->data, 43/C 44/D 00 00, msg)
+140     # . . push args
+141     68/push  "F - test-write-appends"/imm32
+142     68/push  0x00004443/imm32/C-D
+143     # . . push *_test-stream->data
+144     b8/copy-to-EAX  _test-stream/imm32
+145     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+146     # . . call
+147     e8/call  check-ints-equal/disp32
+148     # . . discard args
+149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+150     # end
+151     c3/return
+152 
+153 == data
+154 
+155 _test-stream:
+156     # current write index
+157     00 00 00 00
+158     # current read index
+159     00 00 00 00
+160     # length (= 8)
+161     08 00 00 00
+162     # data
+163     00 00 00 00 00 00 00 00  # 8 bytes
+164 
+165 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/058read.subx.html b/html/subx/058read.subx.html deleted file mode 100644 index 27b20a0e..00000000 --- a/html/subx/058read.subx.html +++ /dev/null @@ -1,432 +0,0 @@ - - - - -Mu - subx/058read.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/058read.subx -
-  1 # read: analogously to write, support reading from in-memory streams in
-  2 # addition to file descriptors.
-  3 #
-  4 # We can pass it either a file descriptor or an address to a stream. If a
-  5 # file descriptor is passed in, we _read from it using the right syscall. If a
-  6 # stream is passed in (a fake file descriptor), we read from it instead. This
-  7 # lets us initialize input for tests.
-  8 #
-  9 # A little counter-intuitively, the output of 'read' ends up in.. a stream. So
- 10 # tests end up doing a redundant copy. Why? Well, consider the alternatives:
- 11 #
- 12 #   a) Reading into a string, and returning a pointer to the end of the read
- 13 #   region, or a count of bytes written. Now this count or end pointer must be
- 14 #   managed separately by the caller, which can be error-prone.
- 15 #
- 16 #   b) Having 'read' return a buffer that it allocates. But there's no way to
- 17 #   know in advance how large to make the buffer. If you read less than the
- 18 #   size of the buffer you again end up needing to manage initialized vs
- 19 #   uninitialized memory.
- 20 #
- 21 #   c) Creating more helpful variants like 'read-byte' or 'read-until' which
- 22 #   also can take a file descriptor or stream, just like 'write'. But such
- 23 #   primitives don't exist in the Linux kernel, so we'd be implementing them
- 24 #   somehow, either with more internal buffering or by making multiple
- 25 #   syscalls.
- 26 #
- 27 # Reading into a stream avoids these problems. The buffer is externally
- 28 # provided and the caller has control over where it's allocated, its lifetime,
- 29 # and so on. The buffer's read and write pointers are internal to it so it's
- 30 # easier to keep in a consistent state. And it can now be passed directly to
- 31 # helpers like 'read-byte' or 'read-until' that only need to support streams,
- 32 # never file descriptors.
- 33 #
- 34 # Like with 'write', we assume our data segment will never begin at an address
- 35 # shorter than 0x08000000, so any smaller arguments are assumed to be real
- 36 # file descriptors.
- 37 #
- 38 # As a reminder, a stream looks like this:
- 39 #   write: int  # index at which to write to next
- 40 #   read: int  # index at which to read next
- 41 #   data: (array byte)  # prefixed by length as usual
- 42 
- 43 == code
- 44 #   instruction                     effective address                                                   register    displacement    immediate
- 45 # . op          subop               mod             rm32          base        index         scale       r32
- 46 # . 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
- 47 
- 48 # main:
- 49     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 50     # syscall(exit, Num-test-failures)
- 51     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 52     b8/copy-to-EAX  1/imm32/exit
- 53     cd/syscall  0x80/imm8
- 54 
- 55 read:  # f : fd or (address stream), s : (address stream) -> num-bytes-read/EAX
- 56     # . prolog
- 57     55/push-EBP
- 58     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 59     # if (f < 0x08000000) return _read(f, s)  # f can't be a user-mode address, so treat it as a kernel file descriptor
- 60     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         0x08000000/imm32  # compare *(EBP+8)
- 61     7d/jump-if-greater-or-equal  $read:fake/disp8
- 62     # . . push args
- 63     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 64     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 65     # . . call
- 66     e8/call  _read/disp32
- 67     # . . discard args
- 68     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 69     # return
- 70     eb/jump  $read:end/disp8
- 71 $read:fake:
- 72     # otherwise, treat 'f' as a stream to scan from
- 73     # . save registers
- 74     56/push-ESI
- 75     57/push-EDI
- 76     # ESI = f
- 77     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
- 78     # EDI = s
- 79     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to ESI
- 80     # EAX = _append-4(out = &s->data[s->write], outend = &s->data[s->length],
- 81     #                 in  = &f->data[f->read],  inend  = &f->data[f->write])
- 82     # . . push &f->data[f->write]
- 83     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
- 84     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
- 85     50/push-EAX
- 86     # . . push &f->data[f->read]
- 87     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
- 88     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
- 89     50/push-EAX
- 90     # . . push &s->data[s->length]
- 91     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EDI+8) to EAX
- 92     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
- 93     50/push-EAX
- 94     # . . push &s->data[s->write]
- 95     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy *EDI to EAX
- 96     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
- 97     50/push-EAX
- 98     # . . call
- 99     e8/call  _append-4/disp32
-100     # . . discard args
-101     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-102     # s->write += EAX
-103     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
-104     # f->read += EAX
-105     01/add                          1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # add EAX to *(ESI+4)
-106     # . restore registers
-107     5f/pop-to-EDI
-108     5e/pop-to-ESI
-109 $read:end:
-110     # . epilog
-111     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-112     5d/pop-to-EBP
-113     c3/return
-114 
-115 # - helpers
-116 
-117 # idea: a clear-if-empty method on streams that clears only if f->read == f->write
-118 # Unclear how I'd use it, though. Callers seem to need the check anyway.
-119 # Maybe a better helper would be 'empty-stream?'
-120 
-121 _read:  # fd : int, s : (address stream) -> num-bytes-read/EAX
-122     # . prolog
-123     55/push-EBP
-124     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-125     # . save registers
-126     51/push-ECX
-127     52/push-EDX
-128     53/push-EBX
-129     56/push-ESI
-130     # ESI = s
-131     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
-132     # EAX = s->write
-133     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-134     # EDX = s->length
-135     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(ESI+8) to EDX
-136     # syscall(read, fd, &s->data[s->write], s->length - s->write)
-137     # . . fd : EBX
-138     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(EBP+8) to EBX
-139     # . . data : ECX = &s->data[s->write]
-140     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
-141     # . . size : EDX = s->length - s->write
-142     29/subtract                     3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from EDX
-143     # . . syscall
-144     b8/copy-to-EAX  3/imm32/read
-145     cd/syscall  0x80/imm8
-146     # add the result EAX to s->write
-147     01/add                          0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # add EAX to *ESI
-148 $_read:end:
-149     # . restore registers
-150     5e/pop-to-ESI
-151     5b/pop-to-EBX
-152     5a/pop-to-EDX
-153     59/pop-to-ECX
-154     # . epilog
-155     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-156     5d/pop-to-EBP
-157     c3/return
-158 
-159     # Two options:
-160     #   1 (what we have above):
-161     #     ECX = s
-162     #     EAX = s->write
-163     #     EDX = s->length
-164     #     # syscall
-165     #     ECX = lea ECX+EAX+12
-166     #     EDX = sub EDX EAX
-167     #
-168     #   2:
-169     #     ECX = s
-170     #     EDX = s->length
-171     #     ECX = &s->data
-172     #     # syscall
-173     #     ECX = add ECX, s->write
-174     #     EDX = sub EDX, s->write
-175     #
-176     # Not much to choose between the two? Option 2 performs a duplicate load to
-177     # use one less register, but doesn't increase the amount of spilling (ECX
-178     # and EDX must be used, and EAX must be clobbered anyway).
-179 
-180 # - tests
-181 
-182 test-read-single:
-183     # - write a single character into _test-stream, then read from its buffered-file
-184     # clear-stream(_test-stream)
-185     # . . push args
-186     68/push  _test-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     # clear-stream(_test-stream-buffer)
-192     # . . push args
-193     68/push  _test-stream-buffer/imm32
-194     # . . call
-195     e8/call  clear-stream/disp32
-196     # . . discard args
-197     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-198     # write(_test-stream, "Ab")
-199     # . . push args
-200     68/push  "Ab"/imm32
-201     68/push  _test-stream/imm32
-202     # . . call
-203     e8/call  write/disp32
-204     # . . discard args
-205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-206     # read(_test-stream, _test-stream-buffer)
-207     # . . push args
-208     68/push  _test-stream-buffer/imm32
-209     68/push  _test-stream/imm32
-210     # . . call
-211     e8/call  read/disp32
-212     # . . discard args
-213     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-214     # check-ints-equal(EAX, 2, msg)
-215     # . . push args
-216     68/push  "F - test-read-single: return EAX"/imm32
-217     68/push  2/imm32
-218     50/push-EAX
-219     # . . call
-220     e8/call  check-ints-equal/disp32
-221     # . . discard args
-222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-223     # check-ints-equal(*_test-stream-buffer->data, 41/A 62/b 00 00, msg)
-224     # . . push args
-225     68/push  "F - test-read-single"/imm32
-226     68/push  0x006241/imm32/Ab
-227     # . . push *_test-stream-buffer->data
-228     b8/copy-to-EAX  _test-stream-buffer/imm32
-229     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-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     # end
-235     c3/return
-236 
-237 test-read-is-stateful:
-238     # - make two consecutive reads, check that their results are appended
-239     # clear-stream(_test-stream)
-240     # . . push args
-241     68/push  _test-stream/imm32
-242     # . . call
-243     e8/call  clear-stream/disp32
-244     # . . discard args
-245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-246     # clear-stream(_test-stream-buffer)
-247     # . . push args
-248     68/push  _test-stream-buffer/imm32
-249     # . . call
-250     e8/call  clear-stream/disp32
-251     # . . discard args
-252     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-253     # write(_test-stream, "C")
-254     # . . push args
-255     68/push  "C"/imm32
-256     68/push  _test-stream/imm32
-257     # . . call
-258     e8/call  write/disp32
-259     # . . discard args
-260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-261     # read(_test-stream, _test-stream-buffer)
-262     # . . push args
-263     68/push  _test-stream-buffer/imm32
-264     68/push  _test-stream/imm32
-265     # . . call
-266     e8/call  read/disp32
-267     # . . discard args
-268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-269     # write(_test-stream, "D")
-270     # . . push args
-271     68/push  "D"/imm32
-272     68/push  _test-stream/imm32
-273     # . . call
-274     e8/call  write/disp32
-275     # . . discard args
-276     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-277     # read(_test-stream, _test-stream-buffer)
-278     # . . push args
-279     68/push  _test-stream-buffer/imm32
-280     68/push  _test-stream/imm32
-281     # . . call
-282     e8/call  read/disp32
-283     # . . discard args
-284     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-285     # check-ints-equal(*_test-stream-buffer->data, 43/C 44/D 00 00, msg)
-286     # . . push args
-287     68/push  "F - test-read-is-stateful"/imm32
-288     68/push  0x00004443/imm32/C-D
-289     # . . push *_test-stream-buffer->data
-290     b8/copy-to-EAX  _test-stream-buffer/imm32
-291     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-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     # end
-297     c3/return
-298 
-299 test-read-returns-0-on-end-of-file:
-300     # - read after hitting end-of-file, check that result is 0
-301     # setup
-302     # . clear-stream(_test-stream)
-303     # . . push args
-304     68/push  _test-stream/imm32
-305     # . . call
-306     e8/call  clear-stream/disp32
-307     # . . discard args
-308     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-309     # . clear-stream(_test-stream-buffer)
-310     # . . push args
-311     68/push  _test-stream-buffer/imm32
-312     # . . call
-313     e8/call  clear-stream/disp32
-314     # . . discard args
-315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-316     # . write(_test-stream, "Ab")
-317     # . . push args
-318     68/push  "Ab"/imm32
-319     68/push  _test-stream/imm32
-320     # . . call
-321     e8/call  write/disp32
-322     # . . discard args
-323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-324     # first read gets to end-of-file
-325     # . read(_test-stream, _test-stream-buffer)
-326     # . . push args
-327     68/push  _test-stream-buffer/imm32
-328     68/push  _test-stream/imm32
-329     # . . call
-330     e8/call  read/disp32
-331     # . . discard args
-332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-333     # second read
-334     # . read(_test-stream, _test-stream-buffer)
-335     # . . push args
-336     68/push  _test-stream-buffer/imm32
-337     68/push  _test-stream/imm32
-338     # . . call
-339     e8/call  read/disp32
-340     # . . discard args
-341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-342     # check-ints-equal(EAX, 0, msg)
-343     # . . push args
-344     68/push  "F - test-read-returns-0-on-end-of-file"/imm32
-345     68/push  0/imm32
-346     50/push-EAX
-347     # . . call
-348     e8/call  check-ints-equal/disp32
-349     # . . discard args
-350     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-351     # end
-352     c3/return
-353 
-354 == data
-355 
-356 _test-stream-buffer:
-357     # current write index
-358     00 00 00 00
-359     # current read index
-360     00 00 00 00
-361     # length (= 8)
-362     08 00 00 00
-363     # data
-364     00 00 00 00 00 00 00 00  # 8 bytes
-365 
-366 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/058stream-equal.subx.html b/html/subx/058stream-equal.subx.html new file mode 100644 index 00000000..368a7f46 --- /dev/null +++ b/html/subx/058stream-equal.subx.html @@ -0,0 +1,737 @@ + + + + +Mu - subx/058stream-equal.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/058stream-equal.subx +
+  1 # some primitives for checking stream contents
+  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 # main:
+  9 #?     e8/call test-next-stream-line-equal-stops-at-newline/disp32
+ 10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 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 # compare all the data in a stream (ignoring the read pointer)
+ 17 stream-data-equal?:  # f : (address stream), s : (address string) -> 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     52/push-EDX
+ 24     56/push-ESI
+ 25     57/push-EDI
+ 26     # ESI = f
+ 27     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 28     # max/EDX = f->data + f->write
+ 29     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+ 30     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           2/r32/EDX   0xc/disp8       .                 # copy ESI+EAX+12 to EDX
+ 31     # currf/ESI = f->data
+ 32     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               0xc/imm32         # add to ESI
+ 33     # EDI = s
+ 34     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+ 35     # if (f->write != s->length) return false;
+ 36     39/compare                      0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # compare *EDI and EAX
+ 37     75/jump-if-not-equal  $stream-data-equal?:false/disp8
+ 38     # currs/EDI = s->data
+ 39     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm32           # add to EDI
+ 40     # EAX = ECX = 0
+ 41     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 42     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+ 43 $stream-data-equal?:loop:
+ 44     # if (curr >= max) return true
+ 45     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # compare ESI with EDX
+ 46     7d/jump-if-greater-or-equal  $stream-data-equal?:true/disp8
+ 47     # AL = *currs
+ 48     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
+ 49     # CL = *curr
+ 50     8a/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/CL    .               .                 # copy byte at *EDI to CL
+ 51     # if (EAX != ECX) return false
+ 52     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX and ECX
+ 53     75/jump-if-not-equal  $stream-data-equal?:false/disp8
+ 54     # ++f
+ 55     46/increment-ESI
+ 56     # ++curr
+ 57     47/increment-EDI
+ 58     eb/jump $stream-data-equal?:loop/disp8
+ 59 $stream-data-equal?:false:
+ 60     b8/copy-to-EAX  0/imm32
+ 61     eb/jump  $stream-data-equal?:end/disp8
+ 62 $stream-data-equal?:true:
+ 63     b8/copy-to-EAX  1/imm32
+ 64 $stream-data-equal?:end:
+ 65     # . restore registers
+ 66     5f/pop-to-EDI
+ 67     5e/pop-to-ESI
+ 68     5a/pop-to-EDX
+ 69     59/pop-to-ECX
+ 70     # . epilog
+ 71     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 72     5d/pop-to-EBP
+ 73     c3/return
+ 74 
+ 75 test-stream-data-equal:
+ 76     # . prolog
+ 77     55/push-EBP
+ 78     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 79     # clear-stream(_test-stream)
+ 80     # . . push args
+ 81     68/push  _test-stream/imm32
+ 82     # . . call
+ 83     e8/call  clear-stream/disp32
+ 84     # . . discard args
+ 85     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 86     # write(_test-stream, "Abc")
+ 87     # . . push args
+ 88     68/push  "Abc"/imm32
+ 89     68/push  _test-stream/imm32
+ 90     # . . call
+ 91     e8/call  write/disp32
+ 92     # . . discard args
+ 93     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 94     # EAX = stream-data-equal?(_test-stream, "Abc")
+ 95     # . . push args
+ 96     68/push  "Abc"/imm32
+ 97     68/push  _test-stream/imm32
+ 98     # . . call
+ 99     e8/call  stream-data-equal?/disp32
+100     # . . discard args
+101     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+102     # check-ints-equal(EAX, 1, msg)
+103     # . . push args
+104     68/push  "F - test-stream-data-equal"/imm32
+105     68/push  1/imm32
+106     50/push-EAX
+107     # . . call
+108     e8/call  check-ints-equal/disp32
+109     # . . discard args
+110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+111     # . epilog
+112     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+113     5d/pop-to-EBP
+114     c3/return
+115 
+116 test-stream-data-equal-2:
+117     # . prolog
+118     55/push-EBP
+119     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+120     # clear-stream(_test-stream)
+121     # . . push args
+122     68/push  _test-stream/imm32
+123     # . . call
+124     e8/call  clear-stream/disp32
+125     # . . discard args
+126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+127     # write(_test-stream, "Abc")
+128     # . . push args
+129     68/push  "Abc"/imm32
+130     68/push  _test-stream/imm32
+131     # . . call
+132     e8/call  write/disp32
+133     # . . discard args
+134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+135     # EAX = stream-data-equal?(_test-stream, "Abd")
+136     # . . push args
+137     68/push  "Abd"/imm32
+138     68/push  _test-stream/imm32
+139     # . . call
+140     e8/call  stream-data-equal?/disp32
+141     # . . discard args
+142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+143     # check-ints-equal(EAX, 0, msg)
+144     # . . push args
+145     68/push  "F - test-stream-data-equal-2"/imm32
+146     68/push  0/imm32
+147     50/push-EAX
+148     # . . call
+149     e8/call  check-ints-equal/disp32
+150     # . . discard args
+151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+152     # . epilog
+153     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+154     5d/pop-to-EBP
+155     c3/return
+156 
+157 test-stream-data-equal-length-check:
+158     # . prolog
+159     55/push-EBP
+160     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+161     # clear-stream(_test-stream)
+162     # . . push args
+163     68/push  _test-stream/imm32
+164     # . . call
+165     e8/call  clear-stream/disp32
+166     # . . discard args
+167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+168     # write(_test-stream, "Abc")
+169     # . . push args
+170     68/push  "Abc"/imm32
+171     68/push  _test-stream/imm32
+172     # . . call
+173     e8/call  write/disp32
+174     # . . discard args
+175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+176     # EAX = stream-data-equal?(_test-stream, "Abcd")
+177     # . . push args
+178     68/push  "Abcd"/imm32
+179     68/push  _test-stream/imm32
+180     # . . call
+181     e8/call  stream-data-equal?/disp32
+182     # . . discard args
+183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+184     # check-ints-equal(EAX, 0, msg)
+185     # . . push args
+186     68/push  "F - test-stream-data-equal-length-check"/imm32
+187     68/push  0/imm32
+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     # . epilog
+194     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+195     5d/pop-to-EBP
+196     c3/return
+197 
+198 # helper for later tests
+199 check-stream-equal:  # f : (address stream), s : (address string), msg : (address string)
+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     50/push-EAX
+205     # EAX = stream-data-equal?(f, s)
+206     # . . push args
+207     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+208     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+209     # . . call
+210     e8/call  stream-data-equal?/disp32
+211     # . . discard args
+212     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+213     # check-ints-equal(EAX, 1, msg)
+214     # . . push args
+215     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+216     68/push  1/imm32
+217     50/push-EAX
+218     # . . call
+219     e8/call  check-ints-equal/disp32
+220     # . . discard args
+221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+222     # . restore registers
+223     58/pop-to-EAX
+224     # . epilog
+225     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+226     5d/pop-to-EBP
+227     c3/return
+228 
+229 # scan the next line until newline starting from f->read and compare it with
+230 # 's' (ignoring the trailing newline)
+231 # on success, set f->read to after the next newline
+232 # on failure, leave f->read unmodified
+233 # this function is usually used only in tests, so we repeatedly write f->read
+234 next-stream-line-equal?:  # f : (address stream), s : (address string) -> EAX : boolean
+235     # pseudocode:
+236     #   currf = f->read  # bound: f->write
+237     #   currs = 0  # bound : s->length
+238     #   while true
+239     #     if currf >= f->write
+240     #       return currs >= s->length
+241     #     if f[currf] == '\n'
+242     #       ++currf
+243     #       return currs >= s->length
+244     #     if currs >= s->length return false  # the current line of f still has data to match
+245     #     if f[currf] != s[currs] return false
+246     #     ++currf
+247     #     ++currs
+248     #
+249     # collapsing the two branches that can return true:
+250     #   currf = f->read  # bound: f->write
+251     #   currs = 0  # bound : s->length
+252     #   while true
+253     #     if currf >= f->write break
+254     #     if f[currf] == '\n' break
+255     #     if currs >= s->length return false  # the current line of f still has data to match
+256     #     if f[currf] != s[currs] return false
+257     #     ++currf
+258     #     ++currs
+259     #   ++currf  # skip '\n'
+260     #   return currs >= s->length
+261     # Here the final `++currf` is sometimes unnecessary (if we're already at the end of the stream)
+262     #
+263     # registers:
+264     #   f : ESI
+265     #   s : EDI
+266     #   currf : ECX
+267     #   currs : EDX
+268     #   f[currf] : EAX
+269     #   s[currs] : EBX
+270     #
+271     # . prolog
+272     55/push-EBP
+273     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+274     # . save registers
+275     51/push-ECX
+276     52/push-EDX
+277     56/push-ESI
+278     57/push-EDI
+279     # ESI = f
+280     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+281     # currf/ECX = f->read
+282     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+283     # EDI = s
+284     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+285     # currs/EDX = 0
+286     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
+287     # EAX = EBX = 0
+288     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+289     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+290 $next-stream-line-equal?:loop:
+291     # if (currf >= f->write) break
+292     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ECX with *ESI
+293     7d/jump-if-greater-or-equal  $next-stream-line-equal?:break/disp8
+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
+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
+301     7d/jump-if-greater-or-equal  $next-stream-line-equal?:false/disp8
+302     # BL = *(s->data + currs)
+303     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           3/r32/BL    4/disp8         .                 # copy byte at *(EDI+EDX+4) to BL
+304     # if (EAX != EBX) return false
+305     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # compare EAX and EBX
+306     75/jump-if-not-equal  $next-stream-line-equal?:false/disp8
+307     # ++currf
+308     41/increment-ECX
+309     # ++currs
+310     42/increment-EDX
+311     eb/jump $next-stream-line-equal?:loop/disp8
+312 $next-stream-line-equal?:break:
+313     # ++currf
+314     41/increment-ECX
+315     # if currs >= s->length return true
+316     3b/compare                      0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # compare EDX with *EDI
+317     7c/jump-if-lesser  $next-stream-line-equal?:false/disp8
+318 $next-stream-line-equal?:true:
+319     b8/copy-to-EAX  1/imm32
+320     # persist f->read on success
+321     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
+322     eb/jump  $next-stream-line-equal?:end/disp8
+323 $next-stream-line-equal?:false:
+324     b8/copy-to-EAX  0/imm32
+325 $next-stream-line-equal?:end:
+326     # . restore registers
+327     5f/pop-to-EDI
+328     5e/pop-to-ESI
+329     5a/pop-to-EDX
+330     59/pop-to-ECX
+331     # . epilog
+332     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+333     5d/pop-to-EBP
+334     c3/return
+335 
+336 test-next-stream-line-equal-stops-at-newline:
+337     # . prolog
+338     55/push-EBP
+339     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+340     # clear-stream(_test-stream)
+341     # . . push args
+342     68/push  _test-stream/imm32
+343     # . . call
+344     e8/call  clear-stream/disp32
+345     # . . discard args
+346     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+347     # write(_test-stream, "Abc\ndef")
+348     # . write(_test-stream, "Abc")
+349     # . . push args
+350     68/push  "Abc"/imm32
+351     68/push  _test-stream/imm32
+352     # . . call
+353     e8/call  write/disp32
+354     # . . discard args
+355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+356     # . write(_test-stream, Newline)
+357     # . . push args
+358     68/push  Newline/imm32
+359     68/push  _test-stream/imm32
+360     # . . call
+361     e8/call  write/disp32
+362     # . . discard args
+363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+364     # . write(_test-stream, "def")
+365     # . . push args
+366     68/push  "def"/imm32
+367     68/push  _test-stream/imm32
+368     # . . call
+369     e8/call  write/disp32
+370     # . . discard args
+371     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+372     # EAX = next-stream-line-equal?(_test-stream, "Abc")
+373     # . . push args
+374     68/push  "Abc"/imm32
+375     68/push  _test-stream/imm32
+376     # . . call
+377     e8/call  next-stream-line-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-next-stream-line-equal-stops-at-newline"/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 test-next-stream-line-equal-stops-at-newline-2:
+395     # . prolog
+396     55/push-EBP
+397     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+398     # clear-stream(_test-stream)
+399     # . . push args
+400     68/push  _test-stream/imm32
+401     # . . call
+402     e8/call  clear-stream/disp32
+403     # . . discard args
+404     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+405     # write(_test-stream, "Abc\ndef")
+406     # . write(_test-stream, "Abc")
+407     # . . push args
+408     68/push  "Abc"/imm32
+409     68/push  _test-stream/imm32
+410     # . . call
+411     e8/call  write/disp32
+412     # . . discard args
+413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+414     # . write(_test-stream, Newline)
+415     # . . push args
+416     68/push  Newline/imm32
+417     68/push  _test-stream/imm32
+418     # . . call
+419     e8/call  write/disp32
+420     # . . discard args
+421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+422     # . write(_test-stream, "def")
+423     # . . push args
+424     68/push  "def"/imm32
+425     68/push  _test-stream/imm32
+426     # . . call
+427     e8/call  write/disp32
+428     # . . discard args
+429     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+430     # EAX = next-stream-line-equal?(_test-stream, "def")
+431     # . . push args
+432     68/push  "def"/imm32
+433     68/push  _test-stream/imm32
+434     # . . call
+435     e8/call  next-stream-line-equal?/disp32
+436     # . . discard args
+437     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+438     # check-ints-equal(EAX, 0, msg)
+439     # . . push args
+440     68/push  "F - test-next-stream-line-equal-stops-at-newline-2"/imm32
+441     68/push  0/imm32
+442     50/push-EAX
+443     # . . call
+444     e8/call  check-ints-equal/disp32
+445     # . . discard args
+446     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+447     # . epilog
+448     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+449     5d/pop-to-EBP
+450     c3/return
+451 
+452 test-next-stream-line-equal-skips-newline:
+453     # . prolog
+454     55/push-EBP
+455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+456     # clear-stream(_test-stream)
+457     # . . push args
+458     68/push  _test-stream/imm32
+459     # . . call
+460     e8/call  clear-stream/disp32
+461     # . . discard args
+462     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+463     # write(_test-stream, "Abc\ndef\n")
+464     # . write(_test-stream, "Abc")
+465     # . . push args
+466     68/push  "Abc"/imm32
+467     68/push  _test-stream/imm32
+468     # . . call
+469     e8/call  write/disp32
+470     # . . discard args
+471     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+472     # . write(_test-stream, Newline)
+473     # . . push args
+474     68/push  Newline/imm32
+475     68/push  _test-stream/imm32
+476     # . . call
+477     e8/call  write/disp32
+478     # . . discard args
+479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+480     # . write(_test-stream, "def")
+481     # . . push args
+482     68/push  "def"/imm32
+483     68/push  _test-stream/imm32
+484     # . . call
+485     e8/call  write/disp32
+486     # . . discard args
+487     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+488     # . write(_test-stream, Newline)
+489     # . . push args
+490     68/push  Newline/imm32
+491     68/push  _test-stream/imm32
+492     # . . call
+493     e8/call  write/disp32
+494     # . . discard args
+495     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+496     # next-stream-line-equal?(_test-stream, "Abc")
+497     # . . push args
+498     68/push  "Abc"/imm32
+499     68/push  _test-stream/imm32
+500     # . . call
+501     e8/call  next-stream-line-equal?/disp32
+502     # . . discard args
+503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+504     # EAX = next-stream-line-equal?(_test-stream, "def")
+505     # . . push args
+506     68/push  "def"/imm32
+507     68/push  _test-stream/imm32
+508     # . . call
+509     e8/call  next-stream-line-equal?/disp32
+510     # . . discard args
+511     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+512     # check-ints-equal(EAX, 1, msg)
+513     # . . push args
+514     68/push  "F - test-next-stream-line-equal-skips-newline"/imm32
+515     68/push  1/imm32
+516     50/push-EAX
+517     # . . call
+518     e8/call  check-ints-equal/disp32
+519     # . . discard args
+520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+521     # . epilog
+522     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+523     5d/pop-to-EBP
+524     c3/return
+525 
+526 test-next-stream-line-equal-handles-final-line:
+527     # . prolog
+528     55/push-EBP
+529     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+530     # clear-stream(_test-stream)
+531     # . . push args
+532     68/push  _test-stream/imm32
+533     # . . call
+534     e8/call  clear-stream/disp32
+535     # . . discard args
+536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+537     # write(_test-stream, "Abc\ndef")
+538     # . write(_test-stream, "Abc")
+539     # . . push args
+540     68/push  "Abc"/imm32
+541     68/push  _test-stream/imm32
+542     # . . call
+543     e8/call  write/disp32
+544     # . . discard args
+545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+546     # . write(_test-stream, Newline)
+547     # . . push args
+548     68/push  Newline/imm32
+549     68/push  _test-stream/imm32
+550     # . . call
+551     e8/call  write/disp32
+552     # . . discard args
+553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+554     # . write(_test-stream, "def")
+555     # . . push args
+556     68/push  "def"/imm32
+557     68/push  _test-stream/imm32
+558     # . . call
+559     e8/call  write/disp32
+560     # . . discard args
+561     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+562     # next-stream-line-equal?(_test-stream, "Abc")
+563     # . . push args
+564     68/push  "Abc"/imm32
+565     68/push  _test-stream/imm32
+566     # . . call
+567     e8/call  next-stream-line-equal?/disp32
+568     # . . discard args
+569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+570     # EAX = next-stream-line-equal?(_test-stream, "def")
+571     # . . push args
+572     68/push  "def"/imm32
+573     68/push  _test-stream/imm32
+574     # . . call
+575     e8/call  next-stream-line-equal?/disp32
+576     # . . discard args
+577     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+578     # check-ints-equal(EAX, 1, msg)
+579     # . . push args
+580     68/push  "F - test-next-stream-line-equal-skips-newline"/imm32
+581     68/push  1/imm32
+582     50/push-EAX
+583     # . . call
+584     e8/call  check-ints-equal/disp32
+585     # . . discard args
+586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+587     # . epilog
+588     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+589     5d/pop-to-EBP
+590     c3/return
+591 
+592 test-next-stream-line-equal-always-fails-after-eof:
+593     # . prolog
+594     55/push-EBP
+595     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+596     # clear-stream(_test-stream)
+597     # . . push args
+598     68/push  _test-stream/imm32
+599     # . . call
+600     e8/call  clear-stream/disp32
+601     # . . discard args
+602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+603     # write nothing
+604     # EAX = next-stream-line-equal?(_test-stream, "")
+605     # . . push args
+606     68/push  ""/imm32
+607     68/push  _test-stream/imm32
+608     # . . call
+609     e8/call  next-stream-line-equal?/disp32
+610     # . . discard args
+611     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+612     # check-ints-equal(EAX, 0, msg)
+613     # . . push args
+614     68/push  "F - test-next-stream-line-equal-always-fails-after-eof"/imm32
+615     68/push  1/imm32
+616     50/push-EAX
+617     # . . call
+618     e8/call  check-ints-equal/disp32
+619     # . . discard args
+620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+621     # EAX = next-stream-line-equal?(_test-stream, "")
+622     # . . push args
+623     68/push  ""/imm32
+624     68/push  _test-stream/imm32
+625     # . . call
+626     e8/call  next-stream-line-equal?/disp32
+627     # . . discard args
+628     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+629     # check-ints-equal(EAX, 0, msg)
+630     # . . push args
+631     68/push  "F - test-next-stream-line-equal-always-fails-after-eof/2"/imm32
+632     68/push  1/imm32
+633     50/push-EAX
+634     # . . call
+635     e8/call  check-ints-equal/disp32
+636     # . . discard args
+637     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+638     # . epilog
+639     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+640     5d/pop-to-EBP
+641     c3/return
+642 
+643 # helper for later tests
+644 check-next-stream-line-equal:
+645     # . prolog
+646     55/push-EBP
+647     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+648     # . save registers
+649     50/push-EAX
+650     # EAX = next-stream-line-equal?(f, s)
+651     # . . push args
+652     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+653     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+654     # . . call
+655     e8/call  next-stream-line-equal?/disp32
+656     # . . discard args
+657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+658     # check-ints-equal(EAX, 1, msg)
+659     # . . push args
+660     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+661     68/push  1/imm32
+662     50/push-EAX
+663     # . . call
+664     e8/call  check-ints-equal/disp32
+665     # . . discard args
+666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+667     # . restore registers
+668     58/pop-to-EAX
+669     # . epilog
+670     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+671     5d/pop-to-EBP
+672     c3/return
+
+ + + diff --git a/html/subx/059read-byte.subx.html b/html/subx/059read-byte.subx.html deleted file mode 100644 index 9d8cff7c..00000000 --- a/html/subx/059read-byte.subx.html +++ /dev/null @@ -1,369 +0,0 @@ - - - - -Mu - subx/059read-byte.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/059read-byte.subx -
-  1 # read-byte: one higher-level abstraction atop 'read'.
-  2 #
-  3 # There are many situations where 'read' is a lot to manage, and we need
-  4 # to abstract some details away. One of them is when we want to read a file
-  5 # character by character. In this situation we follow C's FILE data structure,
-  6 # which manages the underlying file descriptor together with the buffer it
-  7 # reads into. We call our version 'buffered-file'. Should be useful with other
-  8 # primitives as well, in later layers.
-  9 
- 10 == data
- 11 
- 12 # The buffered file for standard input. Also illustrates the layout for
- 13 # buffered-file.
- 14 Stdin:
- 15     # file descriptor or (address stream)
- 16     00 00 00 00  # 0 = standard input
- 17     # current write index
- 18     00 00 00 00
- 19     # current read index
- 20     00 00 00 00
- 21     # length (8)
- 22     08 00 00 00
- 23     # data
- 24     00 00 00 00 00 00 00 00  # 8 bytes
- 25 
- 26 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
- 27 # I don't want to type in 1024 bytes here.
- 28 
- 29 == code
- 30 #   instruction                     effective address                                                   register    displacement    immediate
- 31 # . op          subop               mod             rm32          base        index         scale       r32
- 32 # . 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
- 33 
- 34 # main:
- 35     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 36 #?     e8/call test-read-byte-multiple/disp32
- 37 #?     e8/call test-read-byte-refills-buffer/disp32
- 38     # syscall(exit, Num-test-failures)
- 39     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 40     b8/copy-to-EAX  1/imm32/exit
- 41     cd/syscall  0x80/imm8
- 42 
- 43 # return next byte value in EAX, with top 3 bytes cleared.
- 44 # On EOF, return 0xffffffff.
- 45 read-byte:  # f : (address buffered-file) -> byte-or-eof/EAX
- 46     # . prolog
- 47     55/push-EBP
- 48     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 49     # . save registers
- 50     51/push-ECX
- 51     56/push-ESI
- 52     # ESI = f
- 53     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
- 54     # ECX = f->read
- 55     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(ESI+8) to ECX
- 56     # if (f->read >= f->write) populate stream from file
- 57     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # compare ECX with *(ESI+4)
- 58     7c/jump-if-lesser  $read-byte:from-stream/disp8
- 59     # . clear-stream(stream = f+4)
- 60     # . . push args
- 61     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy ESI+4 to EAX
- 62     50/push-EAX
- 63     # . . call
- 64     e8/call  clear-stream/disp32
- 65     # . . discard args
- 66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 67     # . f->read must now be 0; update its cache at ECX
- 68     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
- 69     # . EAX = read(f->fd, stream = f+4)
- 70     # . . push args
- 71     50/push-EAX
- 72     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
- 73     # . . call
- 74     e8/call  read/disp32
- 75     # . . discard args
- 76     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 77     # if EAX = 0 return 0xffffffff
- 78     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
- 79     75/jump-if-not-equal  $read-byte:from-stream/disp8
- 80     b8/copy-to-EAX  0xffffffff/imm32
- 81     eb/jump  $read-byte:end/disp8
- 82 $read-byte:from-stream:
- 83     # read byte from stream
- 84     # AL = f->data[f->read]
- 85     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 86     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0x10/disp8      .                 # copy byte at *(ESI+ECX+16) to AL
- 87     # ++f->read
- 88     ff          0/subop/increment   1/mod/*+disp8   6/rm32/ESI    .           .             .           .           8/disp8         .                 # increment *(ESI+8)
- 89 $read-byte:end:
- 90     # . restore registers
- 91     5e/pop-to-ESI
- 92     59/pop-to-ECX
- 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 # - tests
- 99 
-100 test-read-byte-single:
-101     # - check that read-byte returns first byte of 'file'
-102     # setup
-103     # . clear-stream(_test-stream)
-104     # . . push args
-105     68/push  _test-stream/imm32
-106     # . . call
-107     e8/call  clear-stream/disp32
-108     # . . discard args
-109     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-110     # . clear-stream(_test-buffered-file+4)
-111     # . . push args
-112     b8/copy-to-EAX  _test-buffered-file/imm32
-113     05/add-to-EAX  4/imm32
-114     50/push-EAX
-115     # . . call
-116     e8/call  clear-stream/disp32
-117     # . . discard args
-118     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-119     # . write(_test-stream, "Ab")
-120     # . . push args
-121     68/push  "Ab"/imm32
-122     68/push  _test-stream/imm32
-123     # . . call
-124     e8/call  write/disp32
-125     # . . discard args
-126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-127     # read-byte(_test-buffered-file)
-128     # . . push args
-129     68/push  _test-buffered-file/imm32
-130     # . . call
-131     e8/call  read-byte/disp32
-132     # . . discard args
-133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-134     # check-ints-equal(EAX, 'A', msg)
-135     # . . push args
-136     68/push  "F - test-read-byte-single"/imm32
-137     68/push  0x41/imm32
-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     # . end
-144     c3/return
-145 
-146 test-read-byte-multiple:
-147     # - call read-byte twice, check that second call returns second byte
-148     # setup
-149     # . clear-stream(_test-stream)
-150     # . . push args
-151     68/push  _test-stream/imm32
-152     # . . call
-153     e8/call  clear-stream/disp32
-154     # . . discard args
-155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-156     # . clear-stream(_test-buffered-file+4)
-157     # . . push args
-158     b8/copy-to-EAX  _test-buffered-file/imm32
-159     05/add-to-EAX  4/imm32
-160     50/push-EAX
-161     # . . call
-162     e8/call  clear-stream/disp32
-163     # . . discard args
-164     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-165     # . write(_test-stream, "Ab")
-166     # . . push args
-167     68/push  "Ab"/imm32
-168     68/push  _test-stream/imm32
-169     # . . call
-170     e8/call  write/disp32
-171     # . . discard args
-172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-173     # read-byte(_test-buffered-file)
-174     # . . push args
-175     68/push  _test-buffered-file/imm32
-176     # . . call
-177     e8/call  read-byte/disp32
-178     # . . discard args
-179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-180     # read-byte(_test-buffered-file)
-181     # . . push args
-182     68/push  _test-buffered-file/imm32
-183     # . . call
-184     e8/call  read-byte/disp32
-185     # . . discard args
-186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-187     # check-ints-equal(EAX, 'b', msg)
-188     # . . push args
-189     68/push  "F - test-read-byte-multiple"/imm32
-190     68/push  0x62/imm32
-191     50/push-EAX
-192     # . . call
-193     e8/call  check-ints-equal/disp32
-194     # . . discard args
-195     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-196     # . end
-197     c3/return
-198 
-199 test-read-byte-end-of-file:
-200     # - call read-byte on an empty 'file', check that it returns 0xffffffff
-201     # setup
-202     # . clear-stream(_test-stream)
-203     # . . push args
-204     68/push  _test-stream/imm32
-205     # . . call
-206     e8/call  clear-stream/disp32
-207     # . . discard args
-208     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-209     # . clear-stream(_test-buffered-file+4)
-210     # . . push args
-211     b8/copy-to-EAX  _test-buffered-file/imm32
-212     05/add-to-EAX  4/imm32
-213     50/push-EAX
-214     # . . call
-215     e8/call  clear-stream/disp32
-216     # . . discard args
-217     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-218     # read-byte(_test-buffered-file)
-219     # . . push args
-220     68/push  _test-buffered-file/imm32
-221     # . . call
-222     e8/call  read-byte/disp32
-223     # . . discard args
-224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-225     # check-ints-equal(EAX, 0xffffffff, msg)
-226     # . . push args
-227     68/push  "F - test-read-byte-end-of-file"/imm32
-228     68/push  0xffffffff/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     # . end
-235     c3/return
-236 
-237 test-read-byte-refills-buffer:
-238     # - consume buffered-file's buffer, check that next read-byte still works
-239     # setup
-240     # . clear-stream(_test-stream)
-241     # . . push args
-242     68/push  _test-stream/imm32
-243     # . . call
-244     e8/call  clear-stream/disp32
-245     # . . discard args
-246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-247     # . clear-stream(_test-buffered-file+4)
-248     # . . push args
-249     b8/copy-to-EAX  _test-buffered-file/imm32
-250     05/add-to-EAX  4/imm32
-251     50/push-EAX
-252     # . . call
-253     e8/call  clear-stream/disp32
-254     # . . discard args
-255     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-256     # . write(_test-stream, "Abcdefgh")
-257     # . . push args
-258     68/push  "Abcdefgh"/imm32
-259     68/push  _test-stream/imm32
-260     # . . call
-261     e8/call  write/disp32
-262     # . . discard args
-263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-264     # pretend buffer is full
-265     # . _test-buffered-file->read = 6  # >= _test-buffered-file->length
-266     b8/copy-to-EAX  _test-buffered-file/imm32
-267     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         6/imm32           # copy to *(EAX+8)
-268     # read-byte(_test-buffered-file)
-269     # . . push args
-270     68/push  _test-buffered-file/imm32
-271     # . . call
-272     e8/call  read-byte/disp32
-273     # . . discard args
-274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-275     # check-ints-equal(EAX, 'A', msg)
-276     # . . push args
-277     68/push  "F - test-read-byte-refills-buffer"/imm32
-278     68/push  0x41/imm32
-279     50/push-EAX
-280     # . . call
-281     e8/call  check-ints-equal/disp32
-282     # . . discard args
-283     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-284     # . end
-285     c3/return
-286 
-287 == data
-288 
-289 # a test buffered file for _test-stream
-290 _test-buffered-file:
-291     # file descriptor or (address stream)
-292     _test-stream/imm32
-293     # current write index
-294     00 00 00 00
-295     # current read index
-296     00 00 00 00
-297     # length (6)
-298     06 00 00 00
-299     # data
-300     00 00 00 00 00 00  # 6 bytes
-301 
-302 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/059stop.subx.html b/html/subx/059stop.subx.html new file mode 100644 index 00000000..1d1aae8c --- /dev/null +++ b/html/subx/059stop.subx.html @@ -0,0 +1,279 @@ + + + + +Mu - subx/059stop.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/059stop.subx +
+  1 # stop: dependency-injected wrapper around the exit() syscall
+  2 #
+  3 # We'd like to be able to write tests for functions that call exit(), and to
+  4 # make assertions about whether they exit() or not in a given situation. To
+  5 # achieve this we'll call exit() via a smarter wrapper called 'stop'.
+  6 #
+  7 # In the context of a test, calling a function X that calls 'stop' (directly
+  8 # or through further intervening calls) will unwind the stack until X returns,
+  9 # so that we can say check any further assertions after the execution of X. To
+ 10 # achieve this end, we'll pass the return address of X as a 'target' argument
+ 11 # into X, plumbing it through to 'stop'. When 'stop' gets a non-null target it
+ 12 # unwinds the stack until the target. If it gets a null target it calls
+ 13 # exit().
+ 14 #
+ 15 # We'd also like to get the exit status out of 'stop', so we'll combine the
+ 16 # input target with an output status parameter into a type called 'exit-descriptor'.
+ 17 #
+ 18 # So the exit-descriptor looks like this:
+ 19 #   target : address  # return address for 'stop' to unwind to
+ 20 #   value : int  # exit status stop was called with
+ 21 #
+ 22 # 'stop' thus takes two parameters: an exit-descriptor and the exit status.
+ 23 #
+ 24 # 'stop' won't bother cleaning up any other processor state besides the stack,
+ 25 # such as registers. Only ESP will have a well-defined value after 'stop'
+ 26 # returns. (This is a poor man's setjmp/longjmp, if you know what that is.)
+ 27 #
+ 28 # Before you can call any function that may call 'stop', you need to pass in an
+ 29 # exit-descriptor to it. To create an exit-descriptor use 'tailor-exit-descriptor'
+ 30 # below. It's not the most pleasant abstraction in the world.
+ 31 #
+ 32 # An exit-descriptor's target is its input, computed during 'tailor-exit-descriptor'.
+ 33 # Its value is its output, computed during stop and available to the test.
+ 34 
+ 35 == code
+ 36 #   instruction                     effective address                                                   register    displacement    immediate
+ 37 # . op          subop               mod             rm32          base        index         scale       r32
+ 38 # . 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
+ 39 
+ 40 # main:
+ 41     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 42 #?     e8/call test-stop-skips-returns-on-exit/disp32
+ 43     # syscall(exit, Num-test-failures)
+ 44     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 45     b8/copy-to-EAX  1/imm32/exit
+ 46     cd/syscall  0x80/imm8
+ 47 
+ 48 # Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to
+ 49 # the stack.
+ 50 # Ugly that we need to know the size of args, but so it goes.
+ 51 tailor-exit-descriptor:  # ed : (address exit-descriptor), nbytes : int -> <void>
+ 52     # . prolog
+ 53     55/push-EBP
+ 54     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 55     # . save registers
+ 56     50/push-EAX
+ 57     51/push-ECX
+ 58     # EAX = nbytes
+ 59     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
+ 60     # Let X be the value of ESP in the caller, before the call to tailor-exit-descriptor.
+ 61     # The return address for a call in the caller's body will be at:
+ 62     #   X-8 if the caller takes 4 bytes of args for the exit-descriptor (add 4 bytes for the return address)
+ 63     #   X-12 if the caller takes 8 bytes of args
+ 64     #   ..and so on
+ 65     # That's the value we need to return: X-nbytes-4
+ 66     #
+ 67     # However, we also need to account for the perturbance to ESP caused by the
+ 68     # call to tailor-exit-descriptor. It pushes 8 bytes of args followed by 4
+ 69     # bytes for the return address and 4 bytes to push EBP above.
+ 70     # So EBP at this point is X-16.
+ 71     #
+ 72     # So the return address for the next call in the caller is:
+ 73     #   EBP+8 if the caller takes 4 bytes of args
+ 74     #   EBP+4 if the caller takes 8 bytes of args
+ 75     #   EBP if the caller takes 12 bytes of args
+ 76     #   EBP-4 if the caller takes 16 bytes of args
+ 77     #   ..and so on
+ 78     # That's EBP+12-nbytes.
+ 79     # option 1: 6 + 3 bytes
+ 80 #?     2d/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           .           .               8/imm32           # subtract from EAX
+ 81 #?     8d/copy-address                 0/mod/indirect  4/rm32/sib    5/base/EBP  0/index/EAX   .           0/r32/EAX   .               .                 # copy EBP+EAX to EAX
+ 82     # option 2: 2 + 4 bytes
+ 83     f7          3/subop/negate      3/mod/direct    0/rm32/EAX    .           .             .           .           .               .                 # negate EAX
+ 84     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    5/base/EBP  0/index/EAX   .           0/r32/EAX   0xc/disp8         .               # copy EBP+EAX+12 to EAX
+ 85     # copy EAX to ed->target
+ 86     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+ 87     89/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ECX
+ 88     # initialize ed->value
+ 89     c7          0/subop/copy        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # copy to *(ECX+4)
+ 90 $tailor-exit-descriptor:end:
+ 91     # . restore registers
+ 92     59/pop-to-ECX
+ 93     58/pop-to-EAX
+ 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 stop:  # ed : (address exit-descriptor), value : int
+100     # no prolog; one way or another, we're going to clobber registers
+101     # EAX = ed
+102     8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   4/disp8         .                 # copy *(ESP+4) to EAX
+103     # exit(value) if ed->target == 0
+104     81          7/subop/compare     0/mod/indirect  0/rm32/EAX    .           .             .           .           .               0/imm32           # compare *EAX
+105     75/jump-if-not-equal  $stop:fake/disp8
+106     # syscall(exit, value)
+107     8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           3/r32/EBX   8/disp8         .                 # copy *(ESP+8) to EBX
+108     b8/copy-to-EAX  1/imm32/exit
+109     cd/syscall  0x80/imm8
+110 $stop:fake:
+111     # ed->value = value+1
+112     8b/copy                         1/mod/*+disp8   4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   8/disp8         .                 # copy *(ESP+8) to ECX
+113     41/inc-ECX
+114     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(EAX+4)
+115     # non-local jump to ed->target
+116     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy *EAX to ESP
+117 $stop:end:
+118     c3/return  # doesn't return to caller
+119 
+120 test-stop-skips-returns-on-exit:
+121     # This looks like the standard prolog, but is here for different reasons.
+122     # A function calling 'stop' can't rely on EBP persisting past the call.
+123     #
+124     # Use EBP here as a stable base to refer to locals and arguments from in the
+125     # presence of push/pop/call instructions.
+126     # *Don't* use EBP as a way to restore ESP.
+127     55/push-EBP
+128     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+129     # Make room for an exit descriptor on the stack. That's almost always the
+130     # right place for it, available only as long as it's legal to use. Once this
+131     # containing function returns we'll need a new exit descriptor.
+132     # var ed/EAX : (address exit-descriptor)
+133     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+134     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
+135     # Size the exit-descriptor precisely for the next call below, to _test-stop-1.
+136     # tailor-exit-descriptor(ed, 4)
+137     # . . push args
+138     68/push  4/imm32/nbytes-of-args-for-_test-stop-1
+139     50/push-EAX
+140     # . . call
+141     e8/call  tailor-exit-descriptor/disp32
+142     # . . discard args
+143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+144     # . _test-stop-1(ed)
+145     # . . push args
+146     50/push-EAX
+147     # . . call
+148     e8/call  _test-stop-1/disp32
+149     # registers except ESP may be clobbered at this point
+150     # restore args
+151     58/pop-to-EAX
+152     # check that _test-stop-1 tried to call exit(1)
+153     # check-ints-equal(ed->value, 2, msg)  # i.e. stop was called with value 1
+154     # . . push args
+155     68/push  "F - test-stop-skips-returns-on-exit"/imm32
+156     68/push  2/imm32
+157     # . . push ed->value
+158     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+159     # . . call
+160     e8/call  check-ints-equal/disp32
+161     # . . discard args
+162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+163     # . epilog
+164     # don't restore ESP from EBP; manually reclaim locals
+165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+166     5d/pop-to-EBP
+167     c3/return
+168 
+169 _test-stop-1:  # ed : (address exit-descriptor)
+170     # . prolog
+171     55/push-EBP
+172     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+173     # _test-stop-2(ed)
+174     # . . push args
+175     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+176     # . . call
+177     e8/call  _test-stop-2/disp32
+178     # should never get past this point
+179 $_test-stop-1:dead-end:
+180     # . . discard args
+181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+182     # signal test failed: check-ints-equal(1, 0, msg)
+183     # . . push args
+184     68/push  "F - test-stop-skips-returns-on-exit"/imm32
+185     68/push  0/imm32
+186     68/push  1/imm32
+187     # . . call
+188     e8/call  check-ints-equal/disp32
+189     # . . discard args
+190     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+191     # . epilog
+192     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+193     5d/pop-to-EBP
+194     c3/return
+195 
+196 _test-stop-2:  # ed : (address exit-descriptor)
+197     # . prolog
+198     55/push-EBP
+199     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+200     # . stop(ed, 1)
+201     # . . push args
+202     68/push  1/imm32
+203     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+204     # . . call
+205     e8/call  stop/disp32
+206     # should never get past this point
+207 $_test-stop-2:dead-end:
+208     # . epilog
+209     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+210     5d/pop-to-EBP
+211     c3/return
+212 
+213 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/060read.subx.html b/html/subx/060read.subx.html new file mode 100644 index 00000000..2b216667 --- /dev/null +++ b/html/subx/060read.subx.html @@ -0,0 +1,428 @@ + + + + +Mu - subx/060read.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/060read.subx +
+  1 # read: analogously to write, support reading from in-memory streams in
+  2 # addition to file descriptors.
+  3 #
+  4 # We can pass it either a file descriptor or an address to a stream. If a
+  5 # file descriptor is passed in, we _read from it using the right syscall. If a
+  6 # stream is passed in (a fake file descriptor), we read from it instead. This
+  7 # lets us initialize input for tests.
+  8 #
+  9 # A little counter-intuitively, the output of 'read' ends up in.. a stream. So
+ 10 # tests end up doing a redundant copy. Why? Well, consider the alternatives:
+ 11 #
+ 12 #   a) Reading into a string, and returning a pointer to the end of the read
+ 13 #   region, or a count of bytes written. Now this count or end pointer must be
+ 14 #   managed separately by the caller, which can be error-prone.
+ 15 #
+ 16 #   b) Having 'read' return a buffer that it allocates. But there's no way to
+ 17 #   know in advance how large to make the buffer. If you read less than the
+ 18 #   size of the buffer you again end up needing to manage initialized vs
+ 19 #   uninitialized memory.
+ 20 #
+ 21 #   c) Creating more helpful variants like 'read-byte' or 'read-until' which
+ 22 #   also can take a file descriptor or stream, just like 'write'. But such
+ 23 #   primitives don't exist in the Linux kernel, so we'd be implementing them
+ 24 #   somehow, either with more internal buffering or by making multiple
+ 25 #   syscalls.
+ 26 #
+ 27 # Reading into a stream avoids these problems. The buffer is externally
+ 28 # provided and the caller has control over where it's allocated, its lifetime,
+ 29 # and so on. The buffer's read and write pointers are internal to it so it's
+ 30 # easier to keep in a consistent state. And it can now be passed directly to
+ 31 # helpers like 'read-byte' or 'read-until' that only need to support streams,
+ 32 # never file descriptors.
+ 33 #
+ 34 # Like with 'write', we assume our data segment will never begin at an address
+ 35 # shorter than 0x08000000, so any smaller arguments are assumed to be real
+ 36 # file descriptors.
+ 37 #
+ 38 # As a reminder, a stream looks like this:
+ 39 #   write: int  # index at which to write to next
+ 40 #   read: int  # index at which to read next
+ 41 #   data: (array byte)  # prefixed by length as usual
+ 42 
+ 43 == code
+ 44 #   instruction                     effective address                                                   register    displacement    immediate
+ 45 # . op          subop               mod             rm32          base        index         scale       r32
+ 46 # . 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
+ 47 
+ 48 # main:
+ 49     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 50     # syscall(exit, Num-test-failures)
+ 51     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 52     b8/copy-to-EAX  1/imm32/exit
+ 53     cd/syscall  0x80/imm8
+ 54 
+ 55 read:  # f : fd or (address stream), s : (address stream) -> num-bytes-read/EAX
+ 56     # . prolog
+ 57     55/push-EBP
+ 58     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 59     # if (f < 0x08000000) return _read(f, s)  # f can't be a user-mode address, so treat it as a kernel file descriptor
+ 60     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         0x08000000/imm32  # compare *(EBP+8)
+ 61     7d/jump-if-greater-or-equal  $read:fake/disp8
+ 62     # . . push args
+ 63     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 64     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 65     # . . call
+ 66     e8/call  _read/disp32
+ 67     # . . discard args
+ 68     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 69     # return
+ 70     eb/jump  $read:end/disp8
+ 71 $read:fake:
+ 72     # otherwise, treat 'f' as a stream to scan from
+ 73     # . save registers
+ 74     56/push-ESI
+ 75     57/push-EDI
+ 76     # ESI = f
+ 77     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 78     # EDI = s
+ 79     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+ 80     # EAX = _append-4(out = &s->data[s->write], outend = &s->data[s->length],
+ 81     #                 in  = &f->data[f->read],  inend  = &f->data[f->write])
+ 82     # . . push &f->data[f->write]
+ 83     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+ 84     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
+ 85     50/push-EAX
+ 86     # . . push &f->data[f->read]
+ 87     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+ 88     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
+ 89     50/push-EAX
+ 90     # . . push &s->data[s->length]
+ 91     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EDI+8) to EAX
+ 92     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
+ 93     50/push-EAX
+ 94     # . . push &s->data[s->write]
+ 95     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy *EDI to EAX
+ 96     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
+ 97     50/push-EAX
+ 98     # . . call
+ 99     e8/call  _append-4/disp32
+100     # . . discard args
+101     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+102     # s->write += EAX
+103     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
+104     # f->read += EAX
+105     01/add                          1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # add EAX to *(ESI+4)
+106     # . restore registers
+107     5f/pop-to-EDI
+108     5e/pop-to-ESI
+109 $read:end:
+110     # . epilog
+111     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+112     5d/pop-to-EBP
+113     c3/return
+114 
+115 # - helpers
+116 
+117 # idea: a clear-if-empty method on streams that clears only if f->read == f->write
+118 # Unclear how I'd use it, though. Callers seem to need the check anyway.
+119 # Maybe a better helper would be 'empty-stream?'
+120 
+121 _read:  # fd : int, s : (address stream) -> num-bytes-read/EAX
+122     # . prolog
+123     55/push-EBP
+124     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+125     # . save registers
+126     51/push-ECX
+127     52/push-EDX
+128     53/push-EBX
+129     56/push-ESI
+130     # ESI = s
+131     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+132     # EAX = s->write
+133     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+134     # EDX = s->length
+135     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(ESI+8) to EDX
+136     # syscall(read, fd, &s->data[s->write], s->length - s->write)
+137     # . . fd : EBX
+138     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(EBP+8) to EBX
+139     # . . data : ECX = &s->data[s->write]
+140     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
+141     # . . size : EDX = s->length - s->write
+142     29/subtract                     3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from EDX
+143     # . . syscall
+144     b8/copy-to-EAX  3/imm32/read
+145     cd/syscall  0x80/imm8
+146     # add the result EAX to s->write
+147     01/add                          0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # add EAX to *ESI
+148 $_read:end:
+149     # . restore registers
+150     5e/pop-to-ESI
+151     5b/pop-to-EBX
+152     5a/pop-to-EDX
+153     59/pop-to-ECX
+154     # . epilog
+155     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+156     5d/pop-to-EBP
+157     c3/return
+158 
+159     # Two options:
+160     #   1 (what we have above):
+161     #     ECX = s
+162     #     EAX = s->write
+163     #     EDX = s->length
+164     #     # syscall
+165     #     ECX = lea ECX+EAX+12
+166     #     EDX = sub EDX EAX
+167     #
+168     #   2:
+169     #     ECX = s
+170     #     EDX = s->length
+171     #     ECX = &s->data
+172     #     # syscall
+173     #     ECX = add ECX, s->write
+174     #     EDX = sub EDX, s->write
+175     #
+176     # Not much to choose between the two? Option 2 performs a duplicate load to
+177     # use one less register, but doesn't increase the amount of spilling (ECX
+178     # and EDX must be used, and EAX must be clobbered anyway).
+179 
+180 # - tests
+181 
+182 test-read-single:
+183     # - write a single character into _test-stream, then read from its buffered-file
+184     # clear-stream(_test-stream)
+185     # . . push args
+186     68/push  _test-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     # clear-stream(_test-stream-buffer)
+192     # . . push args
+193     68/push  _test-stream-buffer/imm32
+194     # . . call
+195     e8/call  clear-stream/disp32
+196     # . . discard args
+197     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+198     # write(_test-stream, "Ab")
+199     # . . push args
+200     68/push  "Ab"/imm32
+201     68/push  _test-stream/imm32
+202     # . . call
+203     e8/call  write/disp32
+204     # . . discard args
+205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+206     # read(_test-stream, _test-stream-buffer)
+207     # . . push args
+208     68/push  _test-stream-buffer/imm32
+209     68/push  _test-stream/imm32
+210     # . . call
+211     e8/call  read/disp32
+212     # . . discard args
+213     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+214     # check-ints-equal(EAX, 2, msg)
+215     # . . push args
+216     68/push  "F - test-read-single: return EAX"/imm32
+217     68/push  2/imm32
+218     50/push-EAX
+219     # . . call
+220     e8/call  check-ints-equal/disp32
+221     # . . discard args
+222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+223     # check-stream-equal(_test-stream-buffer, "Ab", msg)
+224     # . . push args
+225     68/push  "F - test-read-single"/imm32
+226     68/push  "Ab"/imm32
+227     68/push  _test-stream-buffer/imm32
+228     # . . call
+229     e8/call  check-stream-equal/disp32
+230     # . . discard args
+231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+232     # end
+233     c3/return
+234 
+235 test-read-is-stateful:
+236     # - make two consecutive reads, check that their results are appended
+237     # clear-stream(_test-stream)
+238     # . . push args
+239     68/push  _test-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     # clear-stream(_test-stream-buffer)
+245     # . . push args
+246     68/push  _test-stream-buffer/imm32
+247     # . . call
+248     e8/call  clear-stream/disp32
+249     # . . discard args
+250     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+251     # write(_test-stream, "C")
+252     # . . push args
+253     68/push  "C"/imm32
+254     68/push  _test-stream/imm32
+255     # . . call
+256     e8/call  write/disp32
+257     # . . discard args
+258     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+259     # read(_test-stream, _test-stream-buffer)
+260     # . . push args
+261     68/push  _test-stream-buffer/imm32
+262     68/push  _test-stream/imm32
+263     # . . call
+264     e8/call  read/disp32
+265     # . . discard args
+266     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+267     # write(_test-stream, "D")
+268     # . . push args
+269     68/push  "D"/imm32
+270     68/push  _test-stream/imm32
+271     # . . call
+272     e8/call  write/disp32
+273     # . . discard args
+274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+275     # read(_test-stream, _test-stream-buffer)
+276     # . . push args
+277     68/push  _test-stream-buffer/imm32
+278     68/push  _test-stream/imm32
+279     # . . call
+280     e8/call  read/disp32
+281     # . . discard args
+282     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+283     # check-stream-equal(_test-stream-buffer, "CD", msg)
+284     # . . push args
+285     68/push  "F - test-read-is-stateful"/imm32
+286     68/push  "CD"/imm32
+287     68/push  _test-stream-buffer/imm32
+288     # . . call
+289     e8/call  check-stream-equal/disp32
+290     # . . discard args
+291     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+292     # end
+293     c3/return
+294 
+295 test-read-returns-0-on-end-of-file:
+296     # - read after hitting end-of-file, check that result is 0
+297     # setup
+298     # . clear-stream(_test-stream)
+299     # . . push args
+300     68/push  _test-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     # . clear-stream(_test-stream-buffer)
+306     # . . push args
+307     68/push  _test-stream-buffer/imm32
+308     # . . call
+309     e8/call  clear-stream/disp32
+310     # . . discard args
+311     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+312     # . write(_test-stream, "Ab")
+313     # . . push args
+314     68/push  "Ab"/imm32
+315     68/push  _test-stream/imm32
+316     # . . call
+317     e8/call  write/disp32
+318     # . . discard args
+319     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+320     # first read gets to end-of-file
+321     # . read(_test-stream, _test-stream-buffer)
+322     # . . push args
+323     68/push  _test-stream-buffer/imm32
+324     68/push  _test-stream/imm32
+325     # . . call
+326     e8/call  read/disp32
+327     # . . discard args
+328     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+329     # second read
+330     # . read(_test-stream, _test-stream-buffer)
+331     # . . push args
+332     68/push  _test-stream-buffer/imm32
+333     68/push  _test-stream/imm32
+334     # . . call
+335     e8/call  read/disp32
+336     # . . discard args
+337     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+338     # check-ints-equal(EAX, 0, msg)
+339     # . . push args
+340     68/push  "F - test-read-returns-0-on-end-of-file"/imm32
+341     68/push  0/imm32
+342     50/push-EAX
+343     # . . call
+344     e8/call  check-ints-equal/disp32
+345     # . . discard args
+346     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+347     # end
+348     c3/return
+349 
+350 == data
+351 
+352 _test-stream-buffer:
+353     # current write index
+354     00 00 00 00
+355     # current read index
+356     00 00 00 00
+357     # length (= 8)
+358     08 00 00 00
+359     # data
+360     00 00 00 00 00 00 00 00  # 8 bytes
+361 
+362 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/060write-stream.subx.html b/html/subx/060write-stream.subx.html deleted file mode 100644 index ffda2352..00000000 --- a/html/subx/060write-stream.subx.html +++ /dev/null @@ -1,309 +0,0 @@ - - - - -Mu - subx/060write-stream.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/060write-stream.subx -
-  1 # write-stream: like write, but write streams rather than strings
-  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 # main:
-  9     # manual test
- 10 #?     # write-stream(stdout, _test-stream2)
- 11 #?     68/push  _test-stream2/imm32
- 12 #?     68/push  1/imm32/stdout
- 13 #?     e8/call write-stream/disp32
- 14     # automatic test
- 15 #?     e8/call test-write-stream-appends/disp32
- 16     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 17     # syscall(exit, Num-test-failures)
- 18     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 19     b8/copy-to-EAX  1/imm32/exit
- 20     cd/syscall  0x80/imm8
- 21 
- 22 write-stream:  # f : fd or (address stream), s : (address stream) -> <void>
- 23     # . prolog
- 24     55/push-EBP
- 25     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 26     # if (f < 0x08000000) _write-stream(f, s), return  # f can't be a user-mode address, so treat it as a kernel file descriptor
- 27     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         0x08000000/imm32  # compare *(EBP+8)
- 28     7d/jump-if-greater-or-equal  $write-stream:fake/disp8
- 29     # . . push args
- 30     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 31     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 32     # . . call
- 33     e8/call  _write-stream/disp32
- 34     # . . discard args
- 35     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 36     eb/jump  $write-stream:end/disp8
- 37 $write-stream:fake:
- 38     # otherwise, treat 'f' as a stream to append to
- 39     # . save registers
- 40     50/push-EAX
- 41     56/push-ESI
- 42     57/push-EDI
- 43     # EDI = f
- 44     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
- 45     # ESI = s
- 46     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
- 47     # EAX = _append-4(&f->data[f->write], &f->data[f->length], &s->data[s->read], &s->data[s->write])
- 48     # . . push &s->data[s->write]
- 49     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
- 50     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
- 51     50/push-EAX
- 52     # . . push &s->data[s->read]
- 53     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
- 54     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
- 55     50/push-EAX
- 56     # . . push &f->data[f->length]
- 57     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EDI+8) to EAX
- 58     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
- 59     50/push-EAX
- 60     # . . push &f->data[f->write]
- 61     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy *EDI to EAX
- 62     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
- 63     50/push-EAX
- 64     # . . call
- 65     e8/call  _append-4/disp32
- 66     # . . discard args
- 67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
- 68     # f->write += EAX
- 69     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
- 70     # s->read += EAX
- 71     01/add                          1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # add EAX to *(ESI+4)
- 72     # . restore registers
- 73     5f/pop-to-EDI
- 74     5e/pop-to-ESI
- 75     58/pop-to-EAX
- 76 $write-stream:end:
- 77     # . epilog
- 78     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 79     5d/pop-to-EBP
- 80     c3/return
- 81 
- 82 _write-stream:  # fd : int, s : (address stream) -> <void>
- 83     # . prolog
- 84     55/push-EBP
- 85     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 86     # . save registers
- 87     50/push-EAX
- 88     51/push-ECX
- 89     52/push-EDX
- 90     53/push-EBX
- 91     56/push-ESI
- 92     57/push-EDI
- 93     # ESI = s
- 94     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
- 95     # EDI = s->read
- 96     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           7/r32/EDI   4/disp8         .                 # copy *(ESI+4) to EDI
- 97     # EDX = s->write
- 98     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
- 99     # syscall(write, fd, &s->data[s->read], s->write - s->read)
-100     # . . fd : EBX
-101     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(EBP+8) to EBX
-102     # . . data : ECX = &s->data[s->read]
-103     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  7/index/EDI   .           1/r32/ECX   0xc/disp8       .                 # copy ESI+EDI+12 to ECX
-104     # . . size : EDX = s->write - s->read
-105     29/subtract                     3/mod/direct    2/rm32/EDX    .           .             .           7/r32/EDI   .               .                 # subtract EDI from EDX
-106     # . . syscall
-107     b8/copy-to-EAX  4/imm32/write
-108     cd/syscall  0x80/imm8
-109     # . restore registers
-110     5f/pop-to-EDI
-111     5e/pop-to-ESI
-112     5b/pop-to-EBX
-113     5a/pop-to-EDX
-114     59/pop-to-ECX
-115     58/pop-to-EAX
-116     # . epilog
-117     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-118     5d/pop-to-EBP
-119     c3/return
-120 
-121 test-write-stream-single:
-122     # setup
-123     # . clear-stream(_test-stream)
-124     # . . push args
-125     68/push  _test-stream/imm32
-126     # . . call
-127     e8/call  clear-stream/disp32
-128     # . . discard args
-129     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-130     # . clear-stream(_test-stream2)
-131     # . . push args
-132     68/push  _test-stream2/imm32
-133     # . . call
-134     e8/call  clear-stream/disp32
-135     # . . discard args
-136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-137     # . write(_test-stream2, "Ab")
-138     # . . push args
-139     68/push  "Ab"/imm32
-140     68/push  _test-stream2/imm32
-141     # . . call
-142     e8/call  write/disp32
-143     # . . discard args
-144     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-145     # write-stream(_test-stream, _test-stream2)
-146     # . . push args
-147     68/push  _test-stream2/imm32
-148     68/push  _test-stream/imm32
-149     # . . call
-150     e8/call  write-stream/disp32
-151     # . . discard args
-152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-153     # check-ints-equal(*_test-stream->data, 41/A 62/b 00 00, msg)
-154     # . . push args
-155     68/push  "F - test-write-stream-single"/imm32
-156     68/push  0x006241/imm32/Ab
-157     # . . push *_test-stream->data
-158     b8/copy-to-EAX  _test-stream/imm32
-159     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-160     # . . call
-161     e8/call  check-ints-equal/disp32
-162     # . . discard args
-163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-164     # . end
-165     c3/return
-166 
-167 test-write-stream-appends:
-168     # setup
-169     # . clear-stream(_test-stream)
-170     # . . push args
-171     68/push  _test-stream/imm32
-172     # . . call
-173     e8/call  clear-stream/disp32
-174     # . . discard args
-175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-176     # . clear-stream(_test-stream2)
-177     # . . push args
-178     68/push  _test-stream2/imm32
-179     # . . call
-180     e8/call  clear-stream/disp32
-181     # . . discard args
-182     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-183     # . write(_test-stream2, "C")
-184     # . . push args
-185     68/push  "C"/imm32
-186     68/push  _test-stream2/imm32
-187     # . . call
-188     e8/call  write/disp32
-189     # . . discard args
-190     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-191     # first write
-192     # . write-stream(_test-stream, _test-stream2)
-193     # . . push args
-194     68/push  _test-stream2/imm32
-195     68/push  _test-stream/imm32
-196     # . . call
-197     e8/call  write-stream/disp32
-198     # . . discard args
-199     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-200     # second write
-201     # . write(_test-stream2, "D")
-202     # . . push args
-203     68/push  "D"/imm32
-204     68/push  _test-stream2/imm32
-205     # . . call
-206     e8/call  write/disp32
-207     # . . discard args
-208     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-209     # . write-stream(_test-stream, _test-stream2)
-210     # . . push args
-211     68/push  _test-stream2/imm32
-212     68/push  _test-stream/imm32
-213     # . . call
-214     e8/call  write-stream/disp32
-215     # . . discard args
-216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-217     # check-ints-equal(*_test-stream->data, 43/C 44/D 00 00, msg)
-218     # . . push args
-219     68/push  "F - test-write-stream-appends"/imm32
-220     68/push  0x00004443/imm32/C-D
-221     # . . push *_test-stream->data
-222     b8/copy-to-EAX  _test-stream/imm32
-223     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-224     # . . call
-225     e8/call  check-ints-equal/disp32
-226     # . . discard args
-227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-228     # . end
-229     c3/return
-230 
-231 == data
-232 
-233 _test-stream2:
-234     # current write index
-235     04 00 00 00
-236     # current read index
-237     01 00 00 00
-238     # length (= 8)
-239     08 00 00 00
-240     # data
-241     41 42 43 44 00 00 00 00  # 8 bytes
-242 
-243 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/061error.subx.html b/html/subx/061error.subx.html deleted file mode 100644 index f52ea308..00000000 --- a/html/subx/061error.subx.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - -Mu - subx/061error.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/061error.subx -
- 1 # Print an error message and exit.
- 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 # main:
- 9     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
-10     # syscall(exit, Num-test-failures)
-11     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
-12     b8/copy-to-EAX  1/imm32/exit
-13     cd/syscall  0x80/imm8
-14 
-15 # write(out, "Error: "+msg+"\n") then stop(ed, 1)
-16 error:  # ed : (address exit-descriptor), out : fd or (address stream), msg : (address array byte) -> <void>
-17     # . prolog
-18     55/push-EBP
-19     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-20     # write(out, "Error: ")
-21     # . . push args
-22     68/push  "Error: "/imm32
-23     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-24     # . . call
-25     e8/call  write/disp32
-26     # . . discard args
-27     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-28     # write(out, msg)
-29     # . . push args
-30     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
-31     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-32     # . . call
-33     e8/call  write/disp32
-34     # . . discard args
-35     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-36     # write(out, Newline)
-37     # . . push args
-38     68/push  Newline/imm32
-39     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-40     # . . call
-41     e8/call  write/disp32
-42     # . . discard args
-43     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-44     # stop(ed, 1)
-45     # . . push args
-46     68/push  1/imm32
-47     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-48     # . . call
-49     e8/call  stop/disp32
-50     # should never get past this point
-51 $error:dead-end:
-52     # . epilog
-53     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-54     5d/pop-to-EBP
-55     c3/return
-56 
-57 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/061read-byte.subx.html b/html/subx/061read-byte.subx.html new file mode 100644 index 00000000..3a6c68b3 --- /dev/null +++ b/html/subx/061read-byte.subx.html @@ -0,0 +1,369 @@ + + + + +Mu - subx/061read-byte.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/061read-byte.subx +
+  1 # read-byte: one higher-level abstraction atop 'read'.
+  2 #
+  3 # There are many situations where 'read' is a lot to manage, and we need
+  4 # to abstract some details away. One of them is when we want to read a file
+  5 # character by character. In this situation we follow C's FILE data structure,
+  6 # which manages the underlying file descriptor together with the buffer it
+  7 # reads into. We call our version 'buffered-file'. Should be useful with other
+  8 # primitives as well, in later layers.
+  9 
+ 10 == data
+ 11 
+ 12 # The buffered file for standard input. Also illustrates the layout for
+ 13 # buffered-file.
+ 14 Stdin:
+ 15     # file descriptor or (address stream)
+ 16     00 00 00 00  # 0 = standard input
+ 17     # current write index
+ 18     00 00 00 00
+ 19     # current read index
+ 20     00 00 00 00
+ 21     # length (8)
+ 22     08 00 00 00
+ 23     # data
+ 24     00 00 00 00 00 00 00 00  # 8 bytes
+ 25 
+ 26 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
+ 27 # I don't want to type in 1024 bytes here.
+ 28 
+ 29 == code
+ 30 #   instruction                     effective address                                                   register    displacement    immediate
+ 31 # . op          subop               mod             rm32          base        index         scale       r32
+ 32 # . 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
+ 33 
+ 34 # main:
+ 35     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 36 #?     e8/call test-read-byte-multiple/disp32
+ 37 #?     e8/call test-read-byte-refills-buffer/disp32
+ 38     # syscall(exit, Num-test-failures)
+ 39     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 40     b8/copy-to-EAX  1/imm32/exit
+ 41     cd/syscall  0x80/imm8
+ 42 
+ 43 # return next byte value in EAX, with top 3 bytes cleared.
+ 44 # On EOF, return 0xffffffff.
+ 45 read-byte:  # f : (address buffered-file) -> byte-or-eof/EAX
+ 46     # . prolog
+ 47     55/push-EBP
+ 48     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 49     # . save registers
+ 50     51/push-ECX
+ 51     56/push-ESI
+ 52     # ESI = f
+ 53     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 54     # ECX = f->read
+ 55     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(ESI+8) to ECX
+ 56     # if (f->read >= f->write) populate stream from file
+ 57     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # compare ECX with *(ESI+4)
+ 58     7c/jump-if-lesser  $read-byte:from-stream/disp8
+ 59     # . clear-stream(stream = f+4)
+ 60     # . . push args
+ 61     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy ESI+4 to EAX
+ 62     50/push-EAX
+ 63     # . . call
+ 64     e8/call  clear-stream/disp32
+ 65     # . . discard args
+ 66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 67     # . f->read must now be 0; update its cache at ECX
+ 68     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+ 69     # . EAX = read(f->fd, stream = f+4)
+ 70     # . . push args
+ 71     50/push-EAX
+ 72     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+ 73     # . . call
+ 74     e8/call  read/disp32
+ 75     # . . discard args
+ 76     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 77     # if EAX = 0 return 0xffffffff
+ 78     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
+ 79     75/jump-if-not-equal  $read-byte:from-stream/disp8
+ 80     b8/copy-to-EAX  0xffffffff/imm32
+ 81     eb/jump  $read-byte:end/disp8
+ 82 $read-byte:from-stream:
+ 83     # read byte from stream
+ 84     # AL = f->data[f->read]
+ 85     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 86     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0x10/disp8      .                 # copy byte at *(ESI+ECX+16) to AL
+ 87     # ++f->read
+ 88     ff          0/subop/increment   1/mod/*+disp8   6/rm32/ESI    .           .             .           .           8/disp8         .                 # increment *(ESI+8)
+ 89 $read-byte:end:
+ 90     # . restore registers
+ 91     5e/pop-to-ESI
+ 92     59/pop-to-ECX
+ 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 # - tests
+ 99 
+100 test-read-byte-single:
+101     # - check that read-byte returns first byte of 'file'
+102     # setup
+103     # . clear-stream(_test-stream)
+104     # . . push args
+105     68/push  _test-stream/imm32
+106     # . . call
+107     e8/call  clear-stream/disp32
+108     # . . discard args
+109     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+110     # . clear-stream(_test-buffered-file+4)
+111     # . . push args
+112     b8/copy-to-EAX  _test-buffered-file/imm32
+113     05/add-to-EAX  4/imm32
+114     50/push-EAX
+115     # . . call
+116     e8/call  clear-stream/disp32
+117     # . . discard args
+118     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+119     # . write(_test-stream, "Ab")
+120     # . . push args
+121     68/push  "Ab"/imm32
+122     68/push  _test-stream/imm32
+123     # . . call
+124     e8/call  write/disp32
+125     # . . discard args
+126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+127     # read-byte(_test-buffered-file)
+128     # . . push args
+129     68/push  _test-buffered-file/imm32
+130     # . . call
+131     e8/call  read-byte/disp32
+132     # . . discard args
+133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+134     # check-ints-equal(EAX, 'A', msg)
+135     # . . push args
+136     68/push  "F - test-read-byte-single"/imm32
+137     68/push  0x41/imm32
+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     # . end
+144     c3/return
+145 
+146 test-read-byte-multiple:
+147     # - call read-byte twice, check that second call returns second byte
+148     # setup
+149     # . clear-stream(_test-stream)
+150     # . . push args
+151     68/push  _test-stream/imm32
+152     # . . call
+153     e8/call  clear-stream/disp32
+154     # . . discard args
+155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+156     # . clear-stream(_test-buffered-file+4)
+157     # . . push args
+158     b8/copy-to-EAX  _test-buffered-file/imm32
+159     05/add-to-EAX  4/imm32
+160     50/push-EAX
+161     # . . call
+162     e8/call  clear-stream/disp32
+163     # . . discard args
+164     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+165     # . write(_test-stream, "Ab")
+166     # . . push args
+167     68/push  "Ab"/imm32
+168     68/push  _test-stream/imm32
+169     # . . call
+170     e8/call  write/disp32
+171     # . . discard args
+172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+173     # read-byte(_test-buffered-file)
+174     # . . push args
+175     68/push  _test-buffered-file/imm32
+176     # . . call
+177     e8/call  read-byte/disp32
+178     # . . discard args
+179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+180     # read-byte(_test-buffered-file)
+181     # . . push args
+182     68/push  _test-buffered-file/imm32
+183     # . . call
+184     e8/call  read-byte/disp32
+185     # . . discard args
+186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+187     # check-ints-equal(EAX, 'b', msg)
+188     # . . push args
+189     68/push  "F - test-read-byte-multiple"/imm32
+190     68/push  0x62/imm32
+191     50/push-EAX
+192     # . . call
+193     e8/call  check-ints-equal/disp32
+194     # . . discard args
+195     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+196     # . end
+197     c3/return
+198 
+199 test-read-byte-end-of-file:
+200     # - call read-byte on an empty 'file', check that it returns 0xffffffff
+201     # setup
+202     # . clear-stream(_test-stream)
+203     # . . push args
+204     68/push  _test-stream/imm32
+205     # . . call
+206     e8/call  clear-stream/disp32
+207     # . . discard args
+208     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+209     # . clear-stream(_test-buffered-file+4)
+210     # . . push args
+211     b8/copy-to-EAX  _test-buffered-file/imm32
+212     05/add-to-EAX  4/imm32
+213     50/push-EAX
+214     # . . call
+215     e8/call  clear-stream/disp32
+216     # . . discard args
+217     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+218     # read-byte(_test-buffered-file)
+219     # . . push args
+220     68/push  _test-buffered-file/imm32
+221     # . . call
+222     e8/call  read-byte/disp32
+223     # . . discard args
+224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+225     # check-ints-equal(EAX, 0xffffffff, msg)
+226     # . . push args
+227     68/push  "F - test-read-byte-end-of-file"/imm32
+228     68/push  0xffffffff/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     # . end
+235     c3/return
+236 
+237 test-read-byte-refills-buffer:
+238     # - consume buffered-file's buffer, check that next read-byte still works
+239     # setup
+240     # . clear-stream(_test-stream)
+241     # . . push args
+242     68/push  _test-stream/imm32
+243     # . . call
+244     e8/call  clear-stream/disp32
+245     # . . discard args
+246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+247     # . clear-stream(_test-buffered-file+4)
+248     # . . push args
+249     b8/copy-to-EAX  _test-buffered-file/imm32
+250     05/add-to-EAX  4/imm32
+251     50/push-EAX
+252     # . . call
+253     e8/call  clear-stream/disp32
+254     # . . discard args
+255     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+256     # . write(_test-stream, "Abcdefgh")
+257     # . . push args
+258     68/push  "Abcdefgh"/imm32
+259     68/push  _test-stream/imm32
+260     # . . call
+261     e8/call  write/disp32
+262     # . . discard args
+263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+264     # pretend buffer is full
+265     # . _test-buffered-file->read = 6  # >= _test-buffered-file->length
+266     b8/copy-to-EAX  _test-buffered-file/imm32
+267     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         6/imm32           # copy to *(EAX+8)
+268     # read-byte(_test-buffered-file)
+269     # . . push args
+270     68/push  _test-buffered-file/imm32
+271     # . . call
+272     e8/call  read-byte/disp32
+273     # . . discard args
+274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+275     # check-ints-equal(EAX, 'A', msg)
+276     # . . push args
+277     68/push  "F - test-read-byte-refills-buffer"/imm32
+278     68/push  0x41/imm32
+279     50/push-EAX
+280     # . . call
+281     e8/call  check-ints-equal/disp32
+282     # . . discard args
+283     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+284     # . end
+285     c3/return
+286 
+287 == data
+288 
+289 # a test buffered file for _test-stream
+290 _test-buffered-file:
+291     # file descriptor or (address stream)
+292     _test-stream/imm32
+293     # current write index
+294     00 00 00 00
+295     # current read index
+296     00 00 00 00
+297     # length (6)
+298     06 00 00 00
+299     # data
+300     00 00 00 00 00 00  # 6 bytes
+301 
+302 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/062write-byte.subx.html b/html/subx/062write-byte.subx.html deleted file mode 100644 index 3032ba85..00000000 --- a/html/subx/062write-byte.subx.html +++ /dev/null @@ -1,298 +0,0 @@ - - - - -Mu - subx/062write-byte.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/062write-byte.subx -
-  1 # write-byte: write a single byte to a buffered-file. The write may be buffered.
-  2 # flush: write out any buffered writes to disk.
-  3 #
-  4 # TODO: Come up with a way to signal failure to write to disk. This is hard
-  5 # since the failure may impact previous calls that were buffered.
-  6 
-  7 == data
-  8 
-  9 # The buffered file for standard output.
- 10 Stdout:
- 11     # file descriptor or (address stream)
- 12     01 00 00 00  # 1 = standard output
- 13     # current write index
- 14     00 00 00 00
- 15     # current read index
- 16     00 00 00 00
- 17     # length (8)
- 18     08 00 00 00
- 19     # data
- 20     00 00 00 00 00 00 00 00  # 8 bytes
- 21 
- 22 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
- 23 # I don't want to type in 1024 bytes here.
- 24 
- 25 == code
- 26 #   instruction                     effective address                                                   register    displacement    immediate
- 27 # . op          subop               mod             rm32          base        index         scale       r32
- 28 # . 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
- 29 
- 30 # main:
- 31     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 32     # syscall(exit, Num-test-failures)
- 33     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 34     b8/copy-to-EAX  1/imm32/exit
- 35     cd/syscall  0x80/imm8
- 36 
- 37 # Write lower byte of 'n' to 'f'.
- 38 write-byte:  # f : (address buffered-file), n : int -> <void>
- 39     # . prolog
- 40     55/push-EBP
- 41     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 42     # . save registers
- 43     51/push-ECX
- 44     57/push-EDI
- 45     # EDI = f
- 46     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
- 47     # ECX = f->write
- 48     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
- 49     # if (f->write >= f->length) flush and clear f's stream
- 50     3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   0xc/disp8       .                 # compare ECX with *(EDI+12)
- 51     7c/jump-if-lesser  $write-byte:to-stream/disp8
- 52     # . flush(f)
- 53     # . . push args
- 54     57/push-EDI
- 55     # . . call
- 56     e8/call  flush/disp32
- 57     # . . discard args
- 58     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 59     # . clear-stream(stream = f+4)
- 60     # . . push args
- 61     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
- 62     50/push-EAX
- 63     # . . call
- 64     e8/call  clear-stream/disp32
- 65     # . . discard args
- 66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 67     # . f->write must now be 0; update its cache at ECX
- 68     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
- 69 $write-byte:to-stream:
- 70     # write to stream
- 71     # f->data[f->write] = LSB(n)
- 72     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 73     8a/copy-byte                    1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/AL    0xc/disp8       .                 # copy byte at *(EBP+12) to AL
- 74     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           0/r32/AL    0x10/disp8      .                 # copy AL to *(EDI+ECX+16)
- 75     # ++f->write
- 76     ff          0/subop/increment   1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         .                 # increment *(EDI+4)
- 77 $write-byte:end:
- 78     # . restore registers
- 79     5f/pop-to-EDI
- 80     59/pop-to-ECX
- 81     # . epilog
- 82     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 83     5d/pop-to-EBP
- 84     c3/return
- 85 
- 86 flush:  # f : (address buffered-file) -> <void>
- 87     # . prolog
- 88     55/push-EBP
- 89     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 90     # . save registers
- 91     50/push-EAX
- 92     51/push-ECX
- 93     # EAX = f
- 94     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
- 95     # write-stream(f->fd, data = f+4)
- 96       # . . push args
- 97     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
- 98     51/push-ECX
- 99     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
-100       # . . call
-101     e8/call  write-stream/disp32
-102       # . . discard args
-103     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-104 $flush:end:
-105     # . restore registers
-106     59/pop-to-ECX
-107     58/pop-to-EAX
-108     # . epilog
-109     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-110     5d/pop-to-EBP
-111     c3/return
-112 
-113 # - tests
-114 
-115 test-write-byte-single:
-116     # - check that write-byte writes to first byte of 'file'
-117     # setup
-118     # . clear-stream(_test-stream)
-119     # . . push args
-120     68/push  _test-stream/imm32
-121     # . . call
-122     e8/call  clear-stream/disp32
-123     # . . discard args
-124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-125     # . clear-stream(_test-buffered-file+4)
-126     # . . push args
-127     b8/copy-to-EAX  _test-buffered-file/imm32
-128     05/add-to-EAX  4/imm32
-129     50/push-EAX
-130     # . . call
-131     e8/call  clear-stream/disp32
-132     # . . discard args
-133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-134     # write-byte(_test-buffered-file, 'A')
-135     # . . push args
-136     68/push  0x41/imm32
-137     68/push  _test-buffered-file/imm32
-138     # . . call
-139     e8/call  write-byte/disp32
-140     # . . discard args
-141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-142     # flush(_test-buffered-file)
-143     # . . push args
-144     68/push  _test-buffered-file/imm32
-145     # . . call
-146     e8/call  flush/disp32
-147     # . . discard args
-148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-149     # check-ints-equal(*_test-stream->data, 'A', msg)
-150     # . . push args
-151     68/push  "F - test-write-byte-single"/imm32
-152     68/push  0x41/imm32
-153     # . . push *_test-stream->data
-154     b8/copy-to-EAX  _test-stream/imm32
-155     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-156     # . . call
-157     e8/call  check-ints-equal/disp32
-158     # . . discard args
-159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-160     # . end
-161     c3/return
-162 
-163 test-write-byte-multiple-flushes:
-164     # - check that write-byte correctly flushes buffered data
-165     # setup
-166     # . clear-stream(_test-stream)
-167     # . . push args
-168     68/push  _test-stream/imm32
-169     # . . call
-170     e8/call  clear-stream/disp32
-171     # . . discard args
-172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-173     # . clear-stream(_test-buffered-file+4)
-174     # . . push args
-175     b8/copy-to-EAX  _test-buffered-file/imm32
-176     05/add-to-EAX  4/imm32
-177     50/push-EAX
-178     # . . call
-179     e8/call  clear-stream/disp32
-180     # . . discard args
-181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-182     # fill up the buffer for _test-buffered-file
-183     # . write(_test-buffered-file+4, 'abcdef')
-184     # . . push args
-185     68/push  "abcdef"/imm32
-186     b8/copy-to-EAX  _test-buffered-file/imm32
-187     05/add-to-EAX  4/imm32
-188     50/push-EAX
-189     # . . call
-190     e8/call  write/disp32
-191     # . . discard args
-192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-193     # write-byte(_test-buffered-file, 'g')
-194     # . . push args
-195     68/push  0x67/imm32
-196     68/push  _test-buffered-file/imm32
-197     # . . call
-198     e8/call  write-byte/disp32
-199     # . . discard args
-200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-201     # flush(_test-buffered-file)
-202     # . . push args
-203     68/push  _test-buffered-file/imm32
-204     # . . call
-205     e8/call  flush/disp32
-206     # . . discard args
-207     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-208     # check-ints-equal(_test-stream->data[0..3], 'abcd', msg)
-209     # . . push args
-210     68/push  "F - test-write-byte-multiple-flushes: 1"/imm32
-211     68/push  0x64636261/imm32
-212     # . . push *_test-stream->data
-213     b8/copy-to-EAX  _test-stream/imm32
-214     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-215     # . . call
-216     e8/call  check-ints-equal/disp32
-217     # . . discard args
-218     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-219     # check-ints-equal(_test-stream->data[4..8], 'efg', msg)
-220     # . . push args
-221     68/push  "F - test-write-byte-multiple-flushes"/imm32
-222     68/push  0x00676665/imm32
-223     # . . push *_test-stream->data
-224     b8/copy-to-EAX  _test-stream/imm32
-225     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0x10/disp8      .                 # push *(EAX+16)
-226     # . . call
-227     e8/call  check-ints-equal/disp32
-228     # . . discard args
-229     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-230     # . end
-231     c3/return
-232 
-233 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/062write-stream.subx.html b/html/subx/062write-stream.subx.html new file mode 100644 index 00000000..9d88e104 --- /dev/null +++ b/html/subx/062write-stream.subx.html @@ -0,0 +1,305 @@ + + + + +Mu - subx/062write-stream.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/062write-stream.subx +
+  1 # write-stream: like write, but write streams rather than strings
+  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 # main:
+  9     # manual test
+ 10 #?     # write-stream(stdout, _test-stream2)
+ 11 #?     68/push  _test-stream2/imm32
+ 12 #?     68/push  1/imm32/stdout
+ 13 #?     e8/call write-stream/disp32
+ 14     # automatic test
+ 15 #?     e8/call test-write-stream-single/disp32
+ 16     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 17     # syscall(exit, Num-test-failures)
+ 18     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 19     b8/copy-to-EAX  1/imm32/exit
+ 20     cd/syscall  0x80/imm8
+ 21 
+ 22 write-stream:  # f : fd or (address stream), s : (address stream) -> <void>
+ 23     # . prolog
+ 24     55/push-EBP
+ 25     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 26     # if (f < 0x08000000) _write-stream(f, s), return  # f can't be a user-mode address, so treat it as a kernel file descriptor
+ 27     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         0x08000000/imm32  # compare *(EBP+8)
+ 28     7d/jump-if-greater-or-equal  $write-stream:fake/disp8
+ 29     # . . push args
+ 30     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 31     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 32     # . . call
+ 33     e8/call  _write-stream/disp32
+ 34     # . . discard args
+ 35     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 36     eb/jump  $write-stream:end/disp8
+ 37 $write-stream:fake:
+ 38     # otherwise, treat 'f' as a stream to append to
+ 39     # . save registers
+ 40     50/push-EAX
+ 41     56/push-ESI
+ 42     57/push-EDI
+ 43     # EDI = f
+ 44     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+ 45     # ESI = s
+ 46     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+ 47     # EAX = _append-4(&f->data[f->write], &f->data[f->length], &s->data[s->read], &s->data[s->write])
+ 48     # . . push &s->data[s->write]
+ 49     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+ 50     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
+ 51     50/push-EAX
+ 52     # . . push &s->data[s->read]
+ 53     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+ 54     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
+ 55     50/push-EAX
+ 56     # . . push &f->data[f->length]
+ 57     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EDI+8) to EAX
+ 58     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
+ 59     50/push-EAX
+ 60     # . . push &f->data[f->write]
+ 61     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy *EDI to EAX
+ 62     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
+ 63     50/push-EAX
+ 64     # . . call
+ 65     e8/call  _append-4/disp32
+ 66     # . . discard args
+ 67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+ 68     # f->write += EAX
+ 69     01/add                          0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to *EDI
+ 70     # s->read += EAX
+ 71     01/add                          1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # add EAX to *(ESI+4)
+ 72     # . restore registers
+ 73     5f/pop-to-EDI
+ 74     5e/pop-to-ESI
+ 75     58/pop-to-EAX
+ 76 $write-stream:end:
+ 77     # . epilog
+ 78     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 79     5d/pop-to-EBP
+ 80     c3/return
+ 81 
+ 82 _write-stream:  # fd : int, s : (address stream) -> <void>
+ 83     # . prolog
+ 84     55/push-EBP
+ 85     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 86     # . save registers
+ 87     50/push-EAX
+ 88     51/push-ECX
+ 89     52/push-EDX
+ 90     53/push-EBX
+ 91     56/push-ESI
+ 92     57/push-EDI
+ 93     # ESI = s
+ 94     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+ 95     # EDI = s->read
+ 96     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           7/r32/EDI   4/disp8         .                 # copy *(ESI+4) to EDI
+ 97     # EDX = s->write
+ 98     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+ 99     # syscall(write, fd, &s->data[s->read], s->write - s->read)
+100     # . . fd : EBX
+101     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(EBP+8) to EBX
+102     # . . data : ECX = &s->data[s->read]
+103     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  7/index/EDI   .           1/r32/ECX   0xc/disp8       .                 # copy ESI+EDI+12 to ECX
+104     # . . size : EDX = s->write - s->read
+105     29/subtract                     3/mod/direct    2/rm32/EDX    .           .             .           7/r32/EDI   .               .                 # subtract EDI from EDX
+106     # . . syscall
+107     b8/copy-to-EAX  4/imm32/write
+108     cd/syscall  0x80/imm8
+109     # . restore registers
+110     5f/pop-to-EDI
+111     5e/pop-to-ESI
+112     5b/pop-to-EBX
+113     5a/pop-to-EDX
+114     59/pop-to-ECX
+115     58/pop-to-EAX
+116     # . epilog
+117     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+118     5d/pop-to-EBP
+119     c3/return
+120 
+121 test-write-stream-single:
+122     # setup
+123     # . clear-stream(_test-stream)
+124     # . . push args
+125     68/push  _test-stream/imm32
+126     # . . call
+127     e8/call  clear-stream/disp32
+128     # . . discard args
+129     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+130     # . clear-stream(_test-stream2)
+131     # . . push args
+132     68/push  _test-stream2/imm32
+133     # . . call
+134     e8/call  clear-stream/disp32
+135     # . . discard args
+136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+137     # . write(_test-stream2, "Ab")
+138     # . . push args
+139     68/push  "Ab"/imm32
+140     68/push  _test-stream2/imm32
+141     # . . call
+142     e8/call  write/disp32
+143     # . . discard args
+144     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+145     # write-stream(_test-stream, _test-stream2)
+146     # . . push args
+147     68/push  _test-stream2/imm32
+148     68/push  _test-stream/imm32
+149     # . . call
+150     e8/call  write-stream/disp32
+151     # . . discard args
+152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+153     # check-stream-equal(_test-stream, "Ab", msg)
+154     # . . push args
+155     68/push  "F - test-write-stream-single"/imm32
+156     68/push  "Ab"/imm32
+157     68/push  _test-stream/imm32
+158     # . . call
+159     e8/call  check-stream-equal/disp32
+160     # . . discard args
+161     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+162     # . end
+163     c3/return
+164 
+165 test-write-stream-appends:
+166     # setup
+167     # . clear-stream(_test-stream)
+168     # . . push args
+169     68/push  _test-stream/imm32
+170     # . . call
+171     e8/call  clear-stream/disp32
+172     # . . discard args
+173     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+174     # . clear-stream(_test-stream2)
+175     # . . push args
+176     68/push  _test-stream2/imm32
+177     # . . call
+178     e8/call  clear-stream/disp32
+179     # . . discard args
+180     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+181     # . write(_test-stream2, "C")
+182     # . . push args
+183     68/push  "C"/imm32
+184     68/push  _test-stream2/imm32
+185     # . . call
+186     e8/call  write/disp32
+187     # . . discard args
+188     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+189     # first write
+190     # . write-stream(_test-stream, _test-stream2)
+191     # . . push args
+192     68/push  _test-stream2/imm32
+193     68/push  _test-stream/imm32
+194     # . . call
+195     e8/call  write-stream/disp32
+196     # . . discard args
+197     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+198     # second write
+199     # . write(_test-stream2, "D")
+200     # . . push args
+201     68/push  "D"/imm32
+202     68/push  _test-stream2/imm32
+203     # . . call
+204     e8/call  write/disp32
+205     # . . discard args
+206     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+207     # . write-stream(_test-stream, _test-stream2)
+208     # . . push args
+209     68/push  _test-stream2/imm32
+210     68/push  _test-stream/imm32
+211     # . . call
+212     e8/call  write-stream/disp32
+213     # . . discard args
+214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+215     # check-stream-equal(_test-stream, "CD", msg)
+216     # . . push args
+217     68/push  "F - test-write-stream-appends"/imm32
+218     68/push  "CD"/imm32
+219     68/push  _test-stream/imm32
+220     # . . call
+221     e8/call  check-stream-equal/disp32
+222     # . . discard args
+223     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+224     # . end
+225     c3/return
+226 
+227 == data
+228 
+229 _test-stream2:
+230     # current write index
+231     04 00 00 00
+232     # current read index
+233     01 00 00 00
+234     # length (= 8)
+235     08 00 00 00
+236     # data
+237     41 42 43 44 00 00 00 00  # 8 bytes
+238 
+239 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/063error.subx.html b/html/subx/063error.subx.html new file mode 100644 index 00000000..df3cc424 --- /dev/null +++ b/html/subx/063error.subx.html @@ -0,0 +1,120 @@ + + + + +Mu - subx/063error.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/063error.subx +
+ 1 # Print an error message and exit.
+ 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 # main:
+ 9     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+10     # syscall(exit, Num-test-failures)
+11     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+12     b8/copy-to-EAX  1/imm32/exit
+13     cd/syscall  0x80/imm8
+14 
+15 # write(out, "Error: "+msg+"\n") then stop(ed, 1)
+16 error:  # ed : (address exit-descriptor), out : fd or (address stream), msg : (address array byte) -> <void>
+17     # . prolog
+18     55/push-EBP
+19     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+20     # write(out, "Error: ")
+21     # . . push args
+22     68/push  "Error: "/imm32
+23     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+24     # . . call
+25     e8/call  write/disp32
+26     # . . discard args
+27     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+28     # write(out, msg)
+29     # . . push args
+30     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+31     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+32     # . . call
+33     e8/call  write/disp32
+34     # . . discard args
+35     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+36     # write(out, Newline)
+37     # . . push args
+38     68/push  Newline/imm32
+39     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+40     # . . call
+41     e8/call  write/disp32
+42     # . . discard args
+43     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+44     # stop(ed, 1)
+45     # . . push args
+46     68/push  1/imm32
+47     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+48     # . . call
+49     e8/call  stop/disp32
+50     # should never get past this point
+51 $error:dead-end:
+52     # . epilog
+53     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+54     5d/pop-to-EBP
+55     c3/return
+56 
+57 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/063hex.subx.html b/html/subx/063hex.subx.html deleted file mode 100644 index 24ed734a..00000000 --- a/html/subx/063hex.subx.html +++ /dev/null @@ -1,804 +0,0 @@ - - - - -Mu - subx/063hex.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/063hex.subx -
-  1 # some utilities for converting numbers to/from hex
-  2 # lowercase letters only for now
-  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 # main:
- 10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 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 is-hex-int?:  # in : (address slice) -> bool/EAX
- 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     52/push-EDX
- 23     53/push-EBX
- 24     # ECX = s
- 25     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
- 26     # EDX = s->end
- 27     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
- 28     # curr/ECX = s->start
- 29     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # copy *ECX to ECX
- 30     # if s is empty return false
- 31     b8/copy-to-EAX  0/imm32/false
- 32     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
- 33     7d/jump-if-greater-or-equal  $is-hex-int?:end/disp8
- 34     # skip past leading '0x'
- 35 $is-hex-int?:initial-0:
- 36     # . if (*curr != '0') jump to loop
- 37     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
- 38     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/BL    .               .                 # copy byte at *ECX to BL
- 39     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x30/imm32/0      # compare EBX
- 40     75/jump-if-not-equal  $is-hex-int?:loop/disp8
- 41     # . ++curr
- 42     41/increment-ECX
- 43 $is-hex-int?:initial-0x:
- 44     # . if (curr >= in->end) return true
- 45     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
- 46     7d/jump-if-greater-or-equal  $is-hex-int?:true/disp8
- 47     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
- 48     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
- 49     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/BL    .               .                 # copy byte at *ECX to BL
- 50     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x78/imm32/x      # compare EBX
- 51     75/jump-if-not-equal  $is-hex-int?:loop/disp8
- 52     # . ++curr
- 53     41/increment-ECX
- 54 $is-hex-int?:loop:
- 55     # if (curr >= in->end) return true
- 56     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
- 57     7d/jump-if-greater-or-equal  $is-hex-int?:true/disp8
- 58     # EAX = is-hex-digit?(*curr)
- 59     # . . push args
- 60     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
- 61     50/push-EAX
- 62     # . . call
- 63     e8/call  is-hex-digit?/disp32
- 64     # . . discard args
- 65     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 66     # if EAX == false return false
- 67     3d/compare-with-EAX  0/imm32
- 68     74/jump-if-equal  $is-hex-int?:end/disp8
- 69     # ++curr
- 70     41/increment-ECX
- 71     # loop
- 72     eb/jump  $is-hex-int?:loop/disp8
- 73 $is-hex-int?:true:
- 74     # return true
- 75     b8/copy-to-EAX  1/imm32/true
- 76 $is-hex-int?:end:
- 77     # . restore registers
- 78     5b/pop-to-EBX
- 79     5a/pop-to-EDX
- 80     59/pop-to-ECX
- 81     # . epilog
- 82     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 83     5d/pop-to-EBP
- 84     c3/return
- 85 
- 86 test-is-hex-int:
- 87     # . prolog
- 88     55/push-EBP
- 89     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 90     # var slice/ECX = "34"
- 91     68/push  _test-slice-hex-int-end/imm32
- 92     68/push  _test-slice-hex-int/imm32
- 93     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 94     # EAX = is-hex-int?(slice)
- 95     # . . push args
- 96     51/push-ECX
- 97     # . . call
- 98     e8/call  is-hex-int?/disp32
- 99     # . . discard args
-100     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-101     # check-ints-equal(EAX, 1, msg)
-102     # . . push args
-103     68/push  "F - test-is-hex-int"/imm32
-104     68/push  1/imm32/true
-105     50/push-EAX
-106     # . . call
-107     e8/call  check-ints-equal/disp32
-108     # . . discard args
-109     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-110     # . epilog
-111     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-112     5d/pop-to-EBP
-113     c3/return
-114 
-115 test-is-hex-int-handles-letters:
-116     # . prolog
-117     55/push-EBP
-118     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-119     # var slice/ECX = "34a"
-120     68/push  _test-slice-hex-int-letters-end/imm32
-121     68/push  _test-slice-hex-int-letters/imm32
-122     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-123     # EAX = is-hex-int?(slice)
-124     # . . push args
-125     51/push-ECX
-126     # . . call
-127     e8/call  is-hex-int?/disp32
-128     # . . discard args
-129     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-130     # check-ints-equal(EAX, 1, msg)
-131     # . . push args
-132     68/push  "F - test-is-hex-int-handles-letters"/imm32
-133     68/push  1/imm32/true
-134     50/push-EAX
-135     # . . call
-136     e8/call  check-ints-equal/disp32
-137     # . . discard args
-138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-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 test-is-hex-int-with-trailing-char:
-145     # . prolog
-146     55/push-EBP
-147     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-148     # var slice/ECX = "34q"
-149     68/push  _test-slice-digits-and-char-end/imm32
-150     68/push  _test-slice-digits-and-char/imm32
-151     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-152     # EAX = is-hex-int?(slice)
-153     # . . push args
-154     51/push-ECX
-155     # . . call
-156     e8/call  is-hex-int?/disp32
-157     # . . discard args
-158     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-159     # check-ints-equal(EAX, 0, msg)
-160     # . . push args
-161     68/push  "F - test-is-hex-int-with-trailing-char"/imm32
-162     68/push  0/imm32/false
-163     50/push-EAX
-164     # . . call
-165     e8/call  check-ints-equal/disp32
-166     # . . discard args
-167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-168     # . epilog
-169     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-170     5d/pop-to-EBP
-171     c3/return
-172 
-173 test-is-hex-int-with-leading-char:
-174     # . prolog
-175     55/push-EBP
-176     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-177     # var slice/ECX = "q34"
-178     68/push  _test-slice-char-and-digits-end/imm32
-179     68/push  _test-slice-char-and-digits/imm32
-180     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-181     # EAX = is-hex-int?(slice)
-182     # . . push args
-183     51/push-ECX
-184     # . . call
-185     e8/call  is-hex-int?/disp32
-186     # . . discard args
-187     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-188     # check-ints-equal(EAX, 0, msg)
-189     # . . push args
-190     68/push  "F - test-is-hex-int-with-leading-char"/imm32
-191     68/push  0/imm32/false
-192     50/push-EAX
-193     # . . call
-194     e8/call  check-ints-equal/disp32
-195     # . . discard args
-196     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-197     # . epilog
-198     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-199     5d/pop-to-EBP
-200     c3/return
-201 
-202 test-is-hex-int-empty:
-203     # . prolog
-204     55/push-EBP
-205     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-206     # var slice/ECX = ""
-207     68/push  _test-slice-empty-end/imm32
-208     68/push  _test-slice-empty/imm32
-209     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-210     # EAX = is-hex-int?(slice)
-211     # . . push args
-212     51/push-ECX
-213     # . . call
-214     e8/call  is-hex-int?/disp32
-215     # . . discard args
-216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-217     # check-ints-equal(EAX, 0, msg)
-218     # . . push args
-219     68/push  "F - test-is-hex-int-empty"/imm32
-220     68/push  0/imm32/false
-221     50/push-EAX
-222     # . . call
-223     e8/call  check-ints-equal/disp32
-224     # . . discard args
-225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-226     # . epilog
-227     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-228     5d/pop-to-EBP
-229     c3/return
-230 
-231 test-is-hex-int-handles-0x-prefix:
-232     # . prolog
-233     55/push-EBP
-234     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-235     # var slice/ECX = "0x3a"
-236     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
-237     68/push  _test-slice-hex-int-with-0x-prefix/imm32
-238     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-239     # EAX = is-hex-int?(slice)
-240     # . . push args
-241     51/push-ECX
-242     # . . call
-243     e8/call  is-hex-int?/disp32
-244     # . . discard args
-245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-246     # check-ints-equal(EAX, 1, msg)
-247     # . . push args
-248     68/push  "F - test-is-hex-int-handles-0x-prefix"/imm32
-249     68/push  1/imm32/true
-250     50/push-EAX
-251     # . . call
-252     e8/call  check-ints-equal/disp32
-253     # . . discard args
-254     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-255     # . epilog
-256     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-257     5d/pop-to-EBP
-258     c3/return
-259 
-260 parse-hex-int:  # in : (address slice) -> result/EAX
-261     # . prolog
-262     55/push-EBP
-263     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-264     # . save registers
-265     51/push-ECX
-266     52/push-EDX
-267     53/push-EBX
-268     56/push-ESI
-269     # result/EBX = 0
-270     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-271     # ECX = s
-272     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
-273     # EDX = s->end
-274     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
-275     # curr/ECX = s->start
-276     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # copy *ECX to ECX
-277     # negate?/ESI = false
-278     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
-279 $parse-hex-int:negative:
-280     # . if (*curr == '-') negate = true
-281     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-282     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-283     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x2d/imm32/-      # compare EAX
-284     75/jump-if-not-equal  $parse-hex-int:initial-0/disp8
-285     # . ++curr
-286     41/increment-ECX
-287     # . negate = true
-288     be/copy-to-ESI  1/imm32/true
-289 $parse-hex-int:initial-0:
-290     # skip past leading '0x'
-291     # . if (*curr != '0') jump to loop
-292     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-293     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x30/imm32/0      # compare EAX
-294     75/jump-if-not-equal  $parse-hex-int:loop/disp8
-295     # . ++curr
-296     41/increment-ECX
-297 $parse-hex-int:initial-0x:
-298     # . if (curr >= in->end) return result
-299     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-300     7d/jump-if-greater-or-equal  $parse-hex-int:end/disp8
-301     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
-302     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-303     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-304     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x78/imm32/x      # compare EAX
-305     75/jump-if-not-equal  $parse-hex-int:loop/disp8
-306     # . ++curr
-307     41/increment-ECX
-308 $parse-hex-int:loop:
-309     # if (curr >= in->end) break
-310     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-311     7d/jump-if-greater-or-equal  $parse-hex-int:negate/disp8
-312     # EAX = from-hex-char(*curr)
-313     # . . copy arg to EAX
-314     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-315     # . . call
-316     e8/call  from-hex-char/disp32
-317     # result = result * 16 + EAX
-318     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm8            # shift EBX left by 4 bits
-319     01/add                          3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # add EAX to EBX
-320     # ++curr
-321     41/increment-ECX
-322     # loop
-323     eb/jump  $parse-hex-int:loop/disp8
-324 $parse-hex-int:negate:
-325     81          7/subop/compare     3/mod/direct    6/rm32/ESI    .           .             .           .           .               0/imm32           # compare ESI
-326     74/jump-if-equal  $parse-hex-int:end/disp8
-327     f7          3/subop/negate      3/mod/direct    3/rm32/EBX    .           .             .           .           .               .                 # negate EBX
-328 $parse-hex-int:end:
-329     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy EBX to EAX
-330     # . restore registers
-331     5e/pop-to-ESI
-332     5b/pop-to-EBX
-333     5a/pop-to-EDX
-334     59/pop-to-ECX
-335     # . epilog
-336     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-337     5d/pop-to-EBP
-338     c3/return
-339 
-340 test-parse-hex-int-single-digit:
-341     # . prolog
-342     55/push-EBP
-343     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-344     # var slice/ECX = "a"
-345     68/push  _test-slice-hex-int-single-letter-end/imm32
-346     68/push  _test-slice-hex-int-single-letter/imm32
-347     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-348     # EAX = parse-hex-int(slice)
-349     # . . push args
-350     51/push-ECX
-351     # . . call
-352     e8/call  parse-hex-int/disp32
-353     # . . discard args
-354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-355     # check-ints-equal(EAX, 0xa, msg)
-356     # . . push args
-357     68/push  "F - test-parse-hex-int-single-digit"/imm32
-358     68/push  0xa/imm32
-359     50/push-EAX
-360     # . . call
-361     e8/call  check-ints-equal/disp32
-362     # . . discard args
-363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-364     # . epilog
-365     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-366     5d/pop-to-EBP
-367     c3/return
-368 
-369 test-parse-hex-int-multi-digit:
-370     # . prolog
-371     55/push-EBP
-372     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-373     # var slice/ECX = "34a"
-374     68/push  _test-slice-hex-int-letters-end/imm32
-375     68/push  _test-slice-hex-int-letters/imm32
-376     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-377     # EAX = parse-hex-int(slice)
-378     # . . push args
-379     51/push-ECX
-380     # . . call
-381     e8/call  parse-hex-int/disp32
-382     # . . discard args
-383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-384     # check-ints-equal(EAX, 0x34a, msg)
-385     # . . push args
-386     68/push  "F - test-parse-hex-int-multi-digit"/imm32
-387     68/push  0x34a/imm32
-388     50/push-EAX
-389     # . . call
-390     e8/call  check-ints-equal/disp32
-391     # . . discard args
-392     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-393     # . epilog
-394     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-395     5d/pop-to-EBP
-396     c3/return
-397 
-398 test-parse-hex-int-0x-prefix:
-399     # . prolog
-400     55/push-EBP
-401     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-402     # var slice/ECX = "0x34"
-403     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
-404     68/push  _test-slice-hex-int-with-0x-prefix/imm32
-405     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-406     # EAX = parse-hex-int(slice)
-407     # . . push args
-408     51/push-ECX
-409     # . . call
-410     e8/call  parse-hex-int/disp32
-411     # . . discard args
-412     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-413     # check-ints-equal(EAX, 0x34a, msg)
-414     # . . push args
-415     68/push  "F - test-parse-hex-int-0x-prefix"/imm32
-416     68/push  0x34/imm32
-417     50/push-EAX
-418     # . . call
-419     e8/call  check-ints-equal/disp32
-420     # . . discard args
-421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-422     # . epilog
-423     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-424     5d/pop-to-EBP
-425     c3/return
-426 
-427 test-parse-hex-int-zero:
-428     # . prolog
-429     55/push-EBP
-430     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-431     # var slice/ECX = "0"
-432     68/push  _test-slice-hex-int-zero-end/imm32
-433     68/push  _test-slice-hex-int-zero/imm32
-434     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-435     # EAX = parse-hex-int(slice)
-436     # . . push args
-437     51/push-ECX
-438     # . . call
-439     e8/call  parse-hex-int/disp32
-440     # . . discard args
-441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-442     # check-ints-equal(EAX, 0x34a, msg)
-443     # . . push args
-444     68/push  "F - test-parse-hex-int-zero"/imm32
-445     68/push  0/imm32
-446     50/push-EAX
-447     # . . call
-448     e8/call  check-ints-equal/disp32
-449     # . . discard args
-450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-451     # . epilog
-452     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-453     5d/pop-to-EBP
-454     c3/return
-455 
-456 test-parse-hex-int-0-prefix:
-457     # . prolog
-458     55/push-EBP
-459     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-460     # var slice/ECX = "03"
-461     68/push  _test-slice-hex-int-with-0-prefix-end/imm32
-462     68/push  _test-slice-hex-int-with-0-prefix/imm32
-463     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-464     # EAX = parse-hex-int(slice)
-465     # . . push args
-466     51/push-ECX
-467     # . . call
-468     e8/call  parse-hex-int/disp32
-469     # . . discard args
-470     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-471     # check-ints-equal(EAX, 0x3, msg)
-472     # . . push args
-473     68/push  "F - test-parse-hex-int-0-prefix"/imm32
-474     68/push  0x3/imm32
-475     50/push-EAX
-476     # . . call
-477     e8/call  check-ints-equal/disp32
-478     # . . discard args
-479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-480     # . epilog
-481     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-482     5d/pop-to-EBP
-483     c3/return
-484 
-485 test-parse-hex-int-negative:
-486     # . prolog
-487     55/push-EBP
-488     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-489     # var slice/ECX = "-03"
-490     68/push  _test-slice-hex-int-negative-with-0-prefix-end/imm32
-491     68/push  _test-slice-hex-int-negative-with-0-prefix/imm32
-492     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-493     # EAX = parse-hex-int(slice)
-494     # . . push args
-495     51/push-ECX
-496     # . . call
-497     e8/call  parse-hex-int/disp32
-498     # . . discard args
-499     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-500     # check-ints-equal(EAX, 0xfffffffd, msg)
-501     # . . push args
-502     68/push  "F - test-parse-hex-int-negative"/imm32
-503     68/push  0xfffffffd/imm32
-504     50/push-EAX
-505     # . . call
-506     e8/call  check-ints-equal/disp32
-507     # . . discard args
-508     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-509     # . epilog
-510     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-511     5d/pop-to-EBP
-512     c3/return
-513 
-514 is-hex-digit?:  # c : byte -> bool/EAX
-515     # . prolog
-516     55/push-EBP
-517     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-518     # . save registers
-519     51/push-ECX
-520     # ECX = c
-521     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
-522     # return false if c < '0'
-523     b8/copy-to-EAX  0/imm32/false
-524     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x30/imm32        # compare ECX
-525     7c/jump-if-lesser  $is-hex-digit?:end/disp8
-526     # return false if c > 'f'
-527     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x66/imm32        # compare ECX
-528     7f/jump-if-greater  $is-hex-digit?:end/disp8
-529     # return true if c <= '9'
-530     b8/copy-to-EAX  1/imm32/true
-531     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x39/imm32        # compare ECX
-532     7e/jump-if-lesser-or-equal  $is-hex-digit?:end/disp8
-533     # return true if c >= 'a'
-534     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x61/imm32        # compare ECX
-535     7d/jump-if-greater-or-equal  $is-hex-digit?:end/disp8
-536     # otherwise return false
-537     b8/copy-to-EAX  0/imm32/false
-538 $is-hex-digit?:end:
-539     # . restore registers
-540     59/pop-to-ECX
-541     # . epilog
-542     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-543     5d/pop-to-EBP
-544     c3/return
-545 
-546 test-hex-below-0:
-547     # EAX = is-hex-digit?(0x2f)
-548     # . . push args
-549     68/push  0x2f/imm32
-550     # . . call
-551     e8/call  is-hex-digit?/disp32
-552     # . . discard args
-553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-554     # check-ints-equal(EAX, 0, msg)
-555     # . . push args
-556     68/push  "F - test-hex-below-0"/imm32
-557     68/push  0/imm32/false
-558     50/push-EAX
-559     # . . call
-560     e8/call  check-ints-equal/disp32
-561     # . . discard args
-562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-563     c3/return
-564 
-565 test-hex-0-to-9:
-566     # EAX = is-hex-digit?(0x30)
-567     # . . push args
-568     68/push  0x30/imm32
-569     # . . call
-570     e8/call  is-hex-digit?/disp32
-571     # . . discard args
-572     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-573     # check-ints-equal(EAX, 1, msg)
-574     # . . push args
-575     68/push  "F - test-hex-at-0"/imm32
-576     68/push  1/imm32/true
-577     50/push-EAX
-578     # . . call
-579     e8/call  check-ints-equal/disp32
-580     # . . discard args
-581     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-582     # EAX = is-hex-digit?(0x39)
-583     # . . push args
-584     68/push  0x39/imm32
-585     # . . call
-586     e8/call  is-hex-digit?/disp32
-587     # . . discard args
-588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-589     # check-ints-equal(EAX, 1, msg)
-590     # . . push args
-591     68/push  "F - test-hex-at-9"/imm32
-592     68/push  1/imm32/true
-593     50/push-EAX
-594     # . . call
-595     e8/call  check-ints-equal/disp32
-596     # . . discard args
-597     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-598     c3/return
-599 
-600 test-hex-above-9-to-a:
-601     # EAX = is-hex-digit?(0x3a)
-602     # . . push args
-603     68/push  0x3a/imm32
-604     # . . call
-605     e8/call  is-hex-digit?/disp32
-606     # . . discard args
-607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-608     # check-ints-equal(EAX, 0, msg)
-609     # . . push args
-610     68/push  "F - test-hex-above-9-to-a"/imm32
-611     68/push  0/imm32/false
-612     50/push-EAX
-613     # . . call
-614     e8/call  check-ints-equal/disp32
-615     # . . discard args
-616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-617     c3/return
-618 
-619 test-hex-a-to-f:
-620     # EAX = is-hex-digit?(0x61)
-621     # . . push args
-622     68/push  0x61/imm32
-623     # . . call
-624     e8/call  is-hex-digit?/disp32
-625     # . . discard args
-626     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-627     # check-ints-equal(EAX, 1, msg)
-628     # . . push args
-629     68/push  "F - test-hex-at-a"/imm32
-630     68/push  1/imm32/true
-631     50/push-EAX
-632     # . . call
-633     e8/call  check-ints-equal/disp32
-634     # . . discard args
-635     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-636     # EAX = is-hex-digit?(0x66)
-637     # . . push args
-638     68/push  0x66/imm32
-639     # . . call
-640     e8/call  is-hex-digit?/disp32
-641     # . . discard args
-642     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-643     # check-ints-equal(EAX, 1, msg)
-644     # . . push args
-645     68/push  "F - test-hex-at-f"/imm32
-646     68/push  1/imm32/true
-647     50/push-EAX
-648     # . . call
-649     e8/call  check-ints-equal/disp32
-650     # . . discard args
-651     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-652     c3/return
-653 
-654 test-hex-above-f:
-655     # EAX = is-hex-digit?(0x67)
-656     # . . push args
-657     68/push  0x67/imm32
-658     # . . call
-659     e8/call  is-hex-digit?/disp32
-660     # . . discard args
-661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-662     # check-ints-equal(EAX, 0, msg)
-663     # . . push args
-664     68/push  "F - test-hex-above-f"/imm32
-665     68/push  0/imm32/false
-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     c3/return
-672 
-673 from-hex-char:  # in/EAX : byte -> out/EAX : num
-674     # no error checking; accepts argument in EAX
-675     # if EAX <= '9' return EAX - '0'
-676     3d/compare-EAX  0x39/imm32/9
-677     7f/jump-if-greater  $from-hex-char:else/disp8
-678     2d/subtract-from-EAX  0x30/imm32/0
-679     c3/return
-680 $from-hex-char:else:
-681     # otherwise return EAX - 'a' + 10
-682     2d/subtract-from-EAX  0x57/imm32/a-10
-683     c3/return
-684 
-685 to-hex-char:  # in/EAX : nibble -> out/EAX : byte
-686     # no error checking; accepts argument in EAX
-687     # if EAX <= 9 return EAX + '0'
-688     3d/compare-EAX  0x9/imm32/9
-689     7f/jump-if-greater  $to-hex-char:else/disp8
-690     05/add-to-EAX  0x30/imm32/0
-691     c3/return
-692 $to-hex-char:else:
-693     # otherwise return EAX + 'a' - 10
-694     05/add-to-EAX  0x57/imm32/a-10
-695     c3/return
-696 
-697 == data
-698 
-699 _test-slice-empty:
-700   # nothing
-701 _test-slice-empty-end:
-702 
-703 _test-slice-hex-int:
-704   33/3 34/4
-705 _test-slice-hex-int-end:
-706 
-707 _test-slice-hex-int-letters:
-708   33/3 34/4 61/a
-709 _test-slice-hex-int-letters-end:
-710 
-711 _test-slice-hex-int-single-letter:
-712   61/a
-713 _test-slice-hex-int-single-letter-end:
-714 
-715 _test-slice-char-and-digits:
-716   71/q 33/3 34/4
-717 _test-slice-char-and-digits-end:
-718 
-719 _test-slice-digits-and-char:
-720   33/3 34/4 71/q
-721 _test-slice-digits-and-char-end:
-722 
-723 _test-slice-hex-int-with-0x-prefix:
-724   30/0 78/x 33/3 34/4
-725 _test-slice-hex-int-with-0x-prefix-end:
-726 
-727 _test-slice-hex-int-zero:
-728   30/0
-729 _test-slice-hex-int-zero-end:
-730 
-731 _test-slice-hex-int-with-0-prefix:
-732   30/0 33/3
-733 _test-slice-hex-int-with-0-prefix-end:
-734 
-735 _test-slice-hex-int-negative-with-0-prefix:
-736   2d/- 30/0 33/3
-737 _test-slice-hex-int-negative-with-0-prefix-end:
-738 
-739 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/064print-byte.subx.html b/html/subx/064print-byte.subx.html deleted file mode 100644 index 88d03088..00000000 --- a/html/subx/064print-byte.subx.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - -Mu - subx/064print-byte.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/064print-byte.subx -
-  1 # Print the (hex) textual representation of the lowest byte of a number.
-  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 # main:
-  9     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 10     # syscall(exit, Num-test-failures)
- 11     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 12     b8/copy-to-EAX  1/imm32/exit
- 13     cd/syscall  0x80/imm8
- 14 
- 15 print-byte:  # f : (address buffered-file), n : int -> <void>
- 16     # . prolog
- 17     55/push-EBP
- 18     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 19     # . save registers
- 20     50/push-EAX
- 21     # AL = convert upper nibble to hex
- 22     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
- 23     c1/shift    5/subop/logic-right 3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm8            # shift EAX right by 4 bits, while padding zeroes
- 24     25/and-EAX  0xf/imm32
- 25     # . AL = to-hex-char(AL)
- 26     e8/call  to-hex-char/disp32
- 27     # write-byte(f, AL)
- 28     # . . push args
- 29     50/push-EAX
- 30     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 31     # . . call
- 32     e8/call  write-byte/disp32
- 33     # . . discard args
- 34     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 35     # AL = convert lower nibble to hex
- 36     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
- 37     25/and-EAX  0xf/imm32
- 38     # . AL = to-hex-char(AL)
- 39     e8/call  to-hex-char/disp32
- 40     # write-byte(f, AL)
- 41     # . . push args
- 42     50/push-EAX
- 43     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 44     # . . call
- 45     e8/call  write-byte/disp32
- 46     # . . discard args
- 47     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 48 $print-byte:end:
- 49     # . restore registers
- 50     58/pop-to-EAX
- 51     # . epilog
- 52     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 53     5d/pop-to-EBP
- 54     c3/return
- 55 
- 56 test-print-byte:
- 57     # - check that print-byte prints the hex textual representation
- 58     # setup
- 59     # . clear-stream(_test-stream)
- 60     # . . push args
- 61     68/push  _test-stream/imm32
- 62     # . . call
- 63     e8/call  clear-stream/disp32
- 64     # . . discard args
- 65     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 66     # . clear-stream(_test-buffered-file+4)
- 67     # . . push args
- 68     b8/copy-to-EAX  _test-buffered-file/imm32
- 69     05/add-to-EAX  4/imm32
- 70     50/push-EAX
- 71     # . . call
- 72     e8/call  clear-stream/disp32
- 73     # . . discard args
- 74     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 75     # print-byte(_test-buffered-file, 0xa)  # exercises digit, non-digit as well as leading zero
- 76     # . . push args
- 77     68/push  0xa/imm32
- 78     68/push  _test-buffered-file/imm32
- 79     # . . call
- 80     e8/call  print-byte/disp32
- 81     # . . discard args
- 82     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 83     # flush(_test-buffered-file)
- 84     # . . push args
- 85     68/push  _test-buffered-file/imm32
- 86     # . . call
- 87     e8/call  flush/disp32
- 88     # . . discard args
- 89     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 90     # check-ints-equal(*_test-stream->data, '0a', msg)
- 91     # . . push args
- 92     68/push  "F - test-print-byte"/imm32
- 93     68/push  0x6130/imm32/0a
- 94     # . . push *_test-stream->data
- 95     b8/copy-to-EAX  _test-stream/imm32
- 96     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
- 97     # . . call
- 98     e8/call  check-ints-equal/disp32
- 99     # . . discard args
-100     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-101     # . end
-102     c3/return
-103 
-104 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/064write-byte.subx.html b/html/subx/064write-byte.subx.html new file mode 100644 index 00000000..2908cb0a --- /dev/null +++ b/html/subx/064write-byte.subx.html @@ -0,0 +1,283 @@ + + + + +Mu - subx/064write-byte.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/064write-byte.subx +
+  1 # write-byte: write a single byte to a buffered-file. The write may be buffered.
+  2 # flush: write out any buffered writes to disk.
+  3 #
+  4 # TODO: Come up with a way to signal failure to write to disk. This is hard
+  5 # since the failure may impact previous calls that were buffered.
+  6 
+  7 == data
+  8 
+  9 # The buffered file for standard output.
+ 10 Stdout:
+ 11     # file descriptor or (address stream)
+ 12     01 00 00 00  # 1 = standard output
+ 13     # current write index
+ 14     00 00 00 00
+ 15     # current read index
+ 16     00 00 00 00
+ 17     # length (8)
+ 18     08 00 00 00
+ 19     # data
+ 20     00 00 00 00 00 00 00 00  # 8 bytes
+ 21 
+ 22 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
+ 23 # I don't want to type in 1024 bytes here.
+ 24 
+ 25 == code
+ 26 #   instruction                     effective address                                                   register    displacement    immediate
+ 27 # . op          subop               mod             rm32          base        index         scale       r32
+ 28 # . 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
+ 29 
+ 30 # main:
+ 31     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 32     # syscall(exit, Num-test-failures)
+ 33     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 34     b8/copy-to-EAX  1/imm32/exit
+ 35     cd/syscall  0x80/imm8
+ 36 
+ 37 # Write lower byte of 'n' to 'f'.
+ 38 write-byte:  # f : (address buffered-file), n : int -> <void>
+ 39     # . prolog
+ 40     55/push-EBP
+ 41     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 42     # . save registers
+ 43     51/push-ECX
+ 44     57/push-EDI
+ 45     # EDI = f
+ 46     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+ 47     # ECX = f->write
+ 48     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+ 49     # if (f->write >= f->length) flush and clear f's stream
+ 50     3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   0xc/disp8       .                 # compare ECX with *(EDI+12)
+ 51     7c/jump-if-lesser  $write-byte:to-stream/disp8
+ 52     # . flush(f)
+ 53     # . . push args
+ 54     57/push-EDI
+ 55     # . . call
+ 56     e8/call  flush/disp32
+ 57     # . . discard args
+ 58     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 59     # . clear-stream(stream = f+4)
+ 60     # . . push args
+ 61     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
+ 62     50/push-EAX
+ 63     # . . call
+ 64     e8/call  clear-stream/disp32
+ 65     # . . discard args
+ 66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 67     # . f->write must now be 0; update its cache at ECX
+ 68     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+ 69 $write-byte:to-stream:
+ 70     # write to stream
+ 71     # f->data[f->write] = LSB(n)
+ 72     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 73     8a/copy-byte                    1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/AL    0xc/disp8       .                 # copy byte at *(EBP+12) to AL
+ 74     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           0/r32/AL    0x10/disp8      .                 # copy AL to *(EDI+ECX+16)
+ 75     # ++f->write
+ 76     ff          0/subop/increment   1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         .                 # increment *(EDI+4)
+ 77 $write-byte:end:
+ 78     # . restore registers
+ 79     5f/pop-to-EDI
+ 80     59/pop-to-ECX
+ 81     # . epilog
+ 82     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 83     5d/pop-to-EBP
+ 84     c3/return
+ 85 
+ 86 flush:  # f : (address buffered-file) -> <void>
+ 87     # . prolog
+ 88     55/push-EBP
+ 89     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 90     # . save registers
+ 91     50/push-EAX
+ 92     51/push-ECX
+ 93     # EAX = f
+ 94     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+ 95     # write-stream(f->fd, data = f+4)
+ 96       # . . push args
+ 97     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy EAX+4 to ECX
+ 98     51/push-ECX
+ 99     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
+100       # . . call
+101     e8/call  write-stream/disp32
+102       # . . discard args
+103     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+104 $flush:end:
+105     # . restore registers
+106     59/pop-to-ECX
+107     58/pop-to-EAX
+108     # . epilog
+109     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+110     5d/pop-to-EBP
+111     c3/return
+112 
+113 # - tests
+114 
+115 test-write-byte-single:
+116     # - check that write-byte writes to first byte of 'file'
+117     # setup
+118     # . clear-stream(_test-stream)
+119     # . . push args
+120     68/push  _test-stream/imm32
+121     # . . call
+122     e8/call  clear-stream/disp32
+123     # . . discard args
+124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+125     # . clear-stream(_test-buffered-file+4)
+126     # . . push args
+127     b8/copy-to-EAX  _test-buffered-file/imm32
+128     05/add-to-EAX  4/imm32
+129     50/push-EAX
+130     # . . call
+131     e8/call  clear-stream/disp32
+132     # . . discard args
+133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+134     # write-byte(_test-buffered-file, 'A')
+135     # . . push args
+136     68/push  0x41/imm32
+137     68/push  _test-buffered-file/imm32
+138     # . . call
+139     e8/call  write-byte/disp32
+140     # . . discard args
+141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+142     # flush(_test-buffered-file)
+143     # . . push args
+144     68/push  _test-buffered-file/imm32
+145     # . . call
+146     e8/call  flush/disp32
+147     # . . discard args
+148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+149     # check-stream-equal(_test-stream, "A", msg)
+150     # . . push args
+151     68/push  "F - test-write-byte-single"/imm32
+152     68/push  "A"/imm32
+153     68/push  _test-stream/imm32
+154     # . . call
+155     e8/call  check-stream-equal/disp32
+156     # . . discard args
+157     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+158     # . end
+159     c3/return
+160 
+161 test-write-byte-multiple-flushes:
+162     # - check that write-byte correctly flushes buffered data
+163     # setup
+164     # . clear-stream(_test-stream)
+165     # . . push args
+166     68/push  _test-stream/imm32
+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     # . clear-stream(_test-buffered-file+4)
+172     # . . push args
+173     b8/copy-to-EAX  _test-buffered-file/imm32
+174     05/add-to-EAX  4/imm32
+175     50/push-EAX
+176     # . . call
+177     e8/call  clear-stream/disp32
+178     # . . discard args
+179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+180     # fill up the buffer for _test-buffered-file
+181     # . write(_test-buffered-file+4, 'abcdef')
+182     # . . push args
+183     68/push  "abcdef"/imm32
+184     b8/copy-to-EAX  _test-buffered-file/imm32
+185     05/add-to-EAX  4/imm32
+186     50/push-EAX
+187     # . . call
+188     e8/call  write/disp32
+189     # . . discard args
+190     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+191     # write-byte(_test-buffered-file, 'g')
+192     # . . push args
+193     68/push  0x67/imm32
+194     68/push  _test-buffered-file/imm32
+195     # . . call
+196     e8/call  write-byte/disp32
+197     # . . discard args
+198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+199     # flush(_test-buffered-file)
+200     # . . push args
+201     68/push  _test-buffered-file/imm32
+202     # . . call
+203     e8/call  flush/disp32
+204     # . . discard args
+205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+206     # check-stream-equal(_test-stream, "abcdef", msg)
+207     # . . push args
+208     68/push  "F - test-write-byte-multiple-flushes: 1"/imm32
+209     68/push  "abcdefg"/imm32
+210     68/push  _test-stream/imm32
+211     # . . call
+212     e8/call  check-stream-equal/disp32
+213     # . . discard args
+214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+215     # . end
+216     c3/return
+217 
+218 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/065hex.subx.html b/html/subx/065hex.subx.html new file mode 100644 index 00000000..1b74efbe --- /dev/null +++ b/html/subx/065hex.subx.html @@ -0,0 +1,874 @@ + + + + +Mu - subx/065hex.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/065hex.subx +
+  1 # some utilities for converting numbers to/from hex
+  2 # lowercase letters only for now
+  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 # main:
+ 10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 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 is-hex-int?:  # in : (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     52/push-EDX
+ 23     53/push-EBX
+ 24     # ECX = s
+ 25     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+ 26     # EDX = s->end
+ 27     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
+ 28     # curr/ECX = s->start
+ 29     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # copy *ECX to ECX
+ 30     # if s is empty return false
+ 31     b8/copy-to-EAX  0/imm32/false
+ 32     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+ 33     7d/jump-if-greater-or-equal  $is-hex-int?:end/disp8
+ 34     # skip past leading '-'
+ 35     # . if (*curr == '-') ++curr
+ 36     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+ 37     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/BL    .               .                 # copy byte at *ECX to BL
+ 38     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x2d/imm32/-      # compare EBX
+ 39     75/jump-if-not-equal  $is-hex-int?:initial-0/disp8
+ 40     # . ++curr
+ 41     41/increment-ECX
+ 42     # skip past leading '0x'
+ 43 $is-hex-int?:initial-0:
+ 44     # . if (*curr != '0') jump to loop
+ 45     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+ 46     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/BL    .               .                 # copy byte at *ECX to BL
+ 47     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x30/imm32/0      # compare EBX
+ 48     75/jump-if-not-equal  $is-hex-int?:loop/disp8
+ 49     # . ++curr
+ 50     41/increment-ECX
+ 51 $is-hex-int?:initial-0x:
+ 52     # . if (curr >= in->end) return true
+ 53     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+ 54     7d/jump-if-greater-or-equal  $is-hex-int?:true/disp8
+ 55     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
+ 56     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+ 57     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/BL    .               .                 # copy byte at *ECX to BL
+ 58     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x78/imm32/x      # compare EBX
+ 59     75/jump-if-not-equal  $is-hex-int?:loop/disp8
+ 60     # . ++curr
+ 61     41/increment-ECX
+ 62 $is-hex-int?:loop:
+ 63     # if (curr >= in->end) return true
+ 64     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+ 65     7d/jump-if-greater-or-equal  $is-hex-int?:true/disp8
+ 66     # EAX = is-hex-digit?(*curr)
+ 67     # . . push args
+ 68     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+ 69     50/push-EAX
+ 70     # . . call
+ 71     e8/call  is-hex-digit?/disp32
+ 72     # . . discard args
+ 73     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 74     # if EAX == false return false
+ 75     3d/compare-with-EAX  0/imm32
+ 76     74/jump-if-equal  $is-hex-int?:end/disp8
+ 77     # ++curr
+ 78     41/increment-ECX
+ 79     # loop
+ 80     eb/jump  $is-hex-int?:loop/disp8
+ 81 $is-hex-int?:true:
+ 82     # return true
+ 83     b8/copy-to-EAX  1/imm32/true
+ 84 $is-hex-int?:end:
+ 85     # . restore registers
+ 86     5b/pop-to-EBX
+ 87     5a/pop-to-EDX
+ 88     59/pop-to-ECX
+ 89     # . epilog
+ 90     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 91     5d/pop-to-EBP
+ 92     c3/return
+ 93 
+ 94 test-is-hex-int:
+ 95     # . prolog
+ 96     55/push-EBP
+ 97     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 98     # var slice/ECX = "34"
+ 99     68/push  _test-slice-hex-int-end/imm32
+100     68/push  _test-slice-hex-int/imm32
+101     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+102     # EAX = is-hex-int?(slice)
+103     # . . push args
+104     51/push-ECX
+105     # . . call
+106     e8/call  is-hex-int?/disp32
+107     # . . discard args
+108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+109     # check-ints-equal(EAX, 1, msg)
+110     # . . push args
+111     68/push  "F - test-is-hex-int"/imm32
+112     68/push  1/imm32/true
+113     50/push-EAX
+114     # . . call
+115     e8/call  check-ints-equal/disp32
+116     # . . discard args
+117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+118     # . epilog
+119     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+120     5d/pop-to-EBP
+121     c3/return
+122 
+123 test-is-hex-int-handles-letters:
+124     # . prolog
+125     55/push-EBP
+126     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+127     # var slice/ECX = "34a"
+128     68/push  _test-slice-hex-int-letters-end/imm32
+129     68/push  _test-slice-hex-int-letters/imm32
+130     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+131     # EAX = is-hex-int?(slice)
+132     # . . push args
+133     51/push-ECX
+134     # . . call
+135     e8/call  is-hex-int?/disp32
+136     # . . discard args
+137     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+138     # check-ints-equal(EAX, 1, msg)
+139     # . . push args
+140     68/push  "F - test-is-hex-int-handles-letters"/imm32
+141     68/push  1/imm32/true
+142     50/push-EAX
+143     # . . call
+144     e8/call  check-ints-equal/disp32
+145     # . . discard args
+146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+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 test-is-hex-int-with-trailing-char:
+153     # . prolog
+154     55/push-EBP
+155     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+156     # var slice/ECX = "34q"
+157     68/push  _test-slice-digits-and-char-end/imm32
+158     68/push  _test-slice-digits-and-char/imm32
+159     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+160     # EAX = is-hex-int?(slice)
+161     # . . push args
+162     51/push-ECX
+163     # . . call
+164     e8/call  is-hex-int?/disp32
+165     # . . discard args
+166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+167     # check-ints-equal(EAX, 0, msg)
+168     # . . push args
+169     68/push  "F - test-is-hex-int-with-trailing-char"/imm32
+170     68/push  0/imm32/false
+171     50/push-EAX
+172     # . . call
+173     e8/call  check-ints-equal/disp32
+174     # . . discard args
+175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+176     # . epilog
+177     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+178     5d/pop-to-EBP
+179     c3/return
+180 
+181 test-is-hex-int-with-leading-char:
+182     # . prolog
+183     55/push-EBP
+184     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+185     # var slice/ECX = "q34"
+186     68/push  _test-slice-char-and-digits-end/imm32
+187     68/push  _test-slice-char-and-digits/imm32
+188     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+189     # EAX = is-hex-int?(slice)
+190     # . . push args
+191     51/push-ECX
+192     # . . call
+193     e8/call  is-hex-int?/disp32
+194     # . . discard args
+195     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+196     # check-ints-equal(EAX, 0, msg)
+197     # . . push args
+198     68/push  "F - test-is-hex-int-with-leading-char"/imm32
+199     68/push  0/imm32/false
+200     50/push-EAX
+201     # . . call
+202     e8/call  check-ints-equal/disp32
+203     # . . discard args
+204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+205     # . epilog
+206     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+207     5d/pop-to-EBP
+208     c3/return
+209 
+210 test-is-hex-int-empty:
+211     # . prolog
+212     55/push-EBP
+213     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+214     # var slice/ECX = ""
+215     68/push  _test-slice-empty-end/imm32
+216     68/push  _test-slice-empty/imm32
+217     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+218     # EAX = is-hex-int?(slice)
+219     # . . push args
+220     51/push-ECX
+221     # . . call
+222     e8/call  is-hex-int?/disp32
+223     # . . discard args
+224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+225     # check-ints-equal(EAX, 0, msg)
+226     # . . push args
+227     68/push  "F - test-is-hex-int-empty"/imm32
+228     68/push  0/imm32/false
+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-is-hex-int-handles-0x-prefix:
+240     # . prolog
+241     55/push-EBP
+242     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+243     # var slice/ECX = "0x3a"
+244     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
+245     68/push  _test-slice-hex-int-with-0x-prefix/imm32
+246     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+247     # EAX = is-hex-int?(slice)
+248     # . . push args
+249     51/push-ECX
+250     # . . call
+251     e8/call  is-hex-int?/disp32
+252     # . . discard args
+253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+254     # check-ints-equal(EAX, 1, msg)
+255     # . . push args
+256     68/push  "F - test-is-hex-int-handles-0x-prefix"/imm32
+257     68/push  1/imm32/true
+258     50/push-EAX
+259     # . . call
+260     e8/call  check-ints-equal/disp32
+261     # . . discard args
+262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+263     # . epilog
+264     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+265     5d/pop-to-EBP
+266     c3/return
+267 
+268 test-is-hex-int-handles-negative:
+269     # . prolog
+270     55/push-EBP
+271     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+272     # var slice/ECX = "-34a"
+273     68/push  _test-slice-hex-int-letters-end/imm32
+274     68/push  _test-slice-hex-int-letters-negative/imm32
+275     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+276     # EAX = is-hex-int?(slice)
+277     # . . push args
+278     51/push-ECX
+279     # . . call
+280     e8/call  is-hex-int?/disp32
+281     # . . discard args
+282     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+283     # check-ints-equal(EAX, 1, msg)
+284     # . . push args
+285     68/push  "F - test-is-hex-int-handles-negative"/imm32
+286     68/push  1/imm32/true
+287     50/push-EAX
+288     # . . call
+289     e8/call  check-ints-equal/disp32
+290     # . . discard args
+291     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+292     # . epilog
+293     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+294     5d/pop-to-EBP
+295     c3/return
+296 
+297 test-is-hex-int-handles-negative-0x-prefix:
+298     # . prolog
+299     55/push-EBP
+300     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+301     # var slice/ECX = "-0x3a"
+302     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
+303     68/push  _test-slice-hex-int-with-0x-prefix-negative/imm32
+304     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+305     # EAX = is-hex-int?(slice)
+306     # . . push args
+307     51/push-ECX
+308     # . . call
+309     e8/call  is-hex-int?/disp32
+310     # . . discard args
+311     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+312     # check-ints-equal(EAX, 1, msg)
+313     # . . push args
+314     68/push  "F - test-is-hex-int-handles-negative-0x-prefix"/imm32
+315     68/push  1/imm32/true
+316     50/push-EAX
+317     # . . call
+318     e8/call  check-ints-equal/disp32
+319     # . . discard args
+320     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+321     # . epilog
+322     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+323     5d/pop-to-EBP
+324     c3/return
+325 
+326 parse-hex-int:  # in : (address slice) -> result/EAX
+327     # . prolog
+328     55/push-EBP
+329     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+330     # . save registers
+331     51/push-ECX
+332     52/push-EDX
+333     53/push-EBX
+334     56/push-ESI
+335     # result/EBX = 0
+336     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+337     # ECX = s
+338     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+339     # EDX = s->end
+340     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
+341     # curr/ECX = s->start
+342     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # copy *ECX to ECX
+343     # negate?/ESI = false
+344     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
+345 $parse-hex-int:negative:
+346     # . if (*curr == '-') negate = true
+347     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+348     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+349     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x2d/imm32/-      # compare EAX
+350     75/jump-if-not-equal  $parse-hex-int:initial-0/disp8
+351     # . ++curr
+352     41/increment-ECX
+353     # . negate = true
+354     be/copy-to-ESI  1/imm32/true
+355 $parse-hex-int:initial-0:
+356     # skip past leading '0x'
+357     # . if (*curr != '0') jump to loop
+358     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+359     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x30/imm32/0      # compare EAX
+360     75/jump-if-not-equal  $parse-hex-int:loop/disp8
+361     # . ++curr
+362     41/increment-ECX
+363 $parse-hex-int:initial-0x:
+364     # . if (curr >= in->end) return result
+365     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+366     7d/jump-if-greater-or-equal  $parse-hex-int:end/disp8
+367     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
+368     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+369     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+370     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x78/imm32/x      # compare EAX
+371     75/jump-if-not-equal  $parse-hex-int:loop/disp8
+372     # . ++curr
+373     41/increment-ECX
+374 $parse-hex-int:loop:
+375     # if (curr >= in->end) break
+376     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+377     7d/jump-if-greater-or-equal  $parse-hex-int:negate/disp8
+378     # EAX = from-hex-char(*curr)
+379     # . . copy arg to EAX
+380     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+381     # . . call
+382     e8/call  from-hex-char/disp32
+383     # result = result * 16 + EAX
+384     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm8            # shift EBX left by 4 bits
+385     01/add                          3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # add EAX to EBX
+386     # ++curr
+387     41/increment-ECX
+388     # loop
+389     eb/jump  $parse-hex-int:loop/disp8
+390 $parse-hex-int:negate:
+391     81          7/subop/compare     3/mod/direct    6/rm32/ESI    .           .             .           .           .               0/imm32           # compare ESI
+392     74/jump-if-equal  $parse-hex-int:end/disp8
+393     f7          3/subop/negate      3/mod/direct    3/rm32/EBX    .           .             .           .           .               .                 # negate EBX
+394 $parse-hex-int:end:
+395     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy EBX to EAX
+396     # . restore registers
+397     5e/pop-to-ESI
+398     5b/pop-to-EBX
+399     5a/pop-to-EDX
+400     59/pop-to-ECX
+401     # . epilog
+402     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+403     5d/pop-to-EBP
+404     c3/return
+405 
+406 test-parse-hex-int-single-digit:
+407     # . prolog
+408     55/push-EBP
+409     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+410     # var slice/ECX = "a"
+411     68/push  _test-slice-hex-int-single-letter-end/imm32
+412     68/push  _test-slice-hex-int-single-letter/imm32
+413     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+414     # EAX = parse-hex-int(slice)
+415     # . . push args
+416     51/push-ECX
+417     # . . call
+418     e8/call  parse-hex-int/disp32
+419     # . . discard args
+420     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+421     # check-ints-equal(EAX, 0xa, msg)
+422     # . . push args
+423     68/push  "F - test-parse-hex-int-single-digit"/imm32
+424     68/push  0xa/imm32
+425     50/push-EAX
+426     # . . call
+427     e8/call  check-ints-equal/disp32
+428     # . . discard args
+429     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+430     # . epilog
+431     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+432     5d/pop-to-EBP
+433     c3/return
+434 
+435 test-parse-hex-int-multi-digit:
+436     # . prolog
+437     55/push-EBP
+438     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+439     # var slice/ECX = "34a"
+440     68/push  _test-slice-hex-int-letters-end/imm32
+441     68/push  _test-slice-hex-int-letters/imm32
+442     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+443     # EAX = parse-hex-int(slice)
+444     # . . push args
+445     51/push-ECX
+446     # . . call
+447     e8/call  parse-hex-int/disp32
+448     # . . discard args
+449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+450     # check-ints-equal(EAX, 0x34a, msg)
+451     # . . push args
+452     68/push  "F - test-parse-hex-int-multi-digit"/imm32
+453     68/push  0x34a/imm32
+454     50/push-EAX
+455     # . . call
+456     e8/call  check-ints-equal/disp32
+457     # . . discard args
+458     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+459     # . epilog
+460     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+461     5d/pop-to-EBP
+462     c3/return
+463 
+464 test-parse-hex-int-0x-prefix:
+465     # . prolog
+466     55/push-EBP
+467     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+468     # var slice/ECX = "0x34"
+469     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
+470     68/push  _test-slice-hex-int-with-0x-prefix/imm32
+471     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+472     # EAX = parse-hex-int(slice)
+473     # . . push args
+474     51/push-ECX
+475     # . . call
+476     e8/call  parse-hex-int/disp32
+477     # . . discard args
+478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+479     # check-ints-equal(EAX, 0x34a, msg)
+480     # . . push args
+481     68/push  "F - test-parse-hex-int-0x-prefix"/imm32
+482     68/push  0x34/imm32
+483     50/push-EAX
+484     # . . call
+485     e8/call  check-ints-equal/disp32
+486     # . . discard args
+487     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+488     # . epilog
+489     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+490     5d/pop-to-EBP
+491     c3/return
+492 
+493 test-parse-hex-int-zero:
+494     # . prolog
+495     55/push-EBP
+496     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+497     # var slice/ECX = "0"
+498     68/push  _test-slice-hex-int-zero-end/imm32
+499     68/push  _test-slice-hex-int-zero/imm32
+500     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+501     # EAX = parse-hex-int(slice)
+502     # . . push args
+503     51/push-ECX
+504     # . . call
+505     e8/call  parse-hex-int/disp32
+506     # . . discard args
+507     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+508     # check-ints-equal(EAX, 0x34a, msg)
+509     # . . push args
+510     68/push  "F - test-parse-hex-int-zero"/imm32
+511     68/push  0/imm32
+512     50/push-EAX
+513     # . . call
+514     e8/call  check-ints-equal/disp32
+515     # . . discard args
+516     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+517     # . epilog
+518     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+519     5d/pop-to-EBP
+520     c3/return
+521 
+522 test-parse-hex-int-0-prefix:
+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 = "03"
+527     68/push  _test-slice-hex-int-with-0-prefix-end/imm32
+528     68/push  _test-slice-hex-int-with-0-prefix/imm32
+529     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+530     # EAX = parse-hex-int(slice)
+531     # . . push args
+532     51/push-ECX
+533     # . . call
+534     e8/call  parse-hex-int/disp32
+535     # . . discard args
+536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+537     # check-ints-equal(EAX, 0x3, msg)
+538     # . . push args
+539     68/push  "F - test-parse-hex-int-0-prefix"/imm32
+540     68/push  0x3/imm32
+541     50/push-EAX
+542     # . . call
+543     e8/call  check-ints-equal/disp32
+544     # . . discard args
+545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+546     # . epilog
+547     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+548     5d/pop-to-EBP
+549     c3/return
+550 
+551 test-parse-hex-int-negative:
+552     # . prolog
+553     55/push-EBP
+554     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+555     # var slice/ECX = "-03"
+556     68/push  _test-slice-hex-int-negative-with-0-prefix-end/imm32
+557     68/push  _test-slice-hex-int-negative-with-0-prefix/imm32
+558     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+559     # EAX = parse-hex-int(slice)
+560     # . . push args
+561     51/push-ECX
+562     # . . call
+563     e8/call  parse-hex-int/disp32
+564     # . . discard args
+565     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+566     # check-ints-equal(EAX, 0xfffffffd, msg)
+567     # . . push args
+568     68/push  "F - test-parse-hex-int-negative"/imm32
+569     68/push  0xfffffffd/imm32
+570     50/push-EAX
+571     # . . call
+572     e8/call  check-ints-equal/disp32
+573     # . . discard args
+574     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+575     # . epilog
+576     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+577     5d/pop-to-EBP
+578     c3/return
+579 
+580 is-hex-digit?:  # c : byte -> EAX : boolean
+581     # . prolog
+582     55/push-EBP
+583     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+584     # . save registers
+585     51/push-ECX
+586     # ECX = c
+587     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+588     # return false if c < '0'
+589     b8/copy-to-EAX  0/imm32/false
+590     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x30/imm32        # compare ECX
+591     7c/jump-if-lesser  $is-hex-digit?:end/disp8
+592     # return false if c > 'f'
+593     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x66/imm32        # compare ECX
+594     7f/jump-if-greater  $is-hex-digit?:end/disp8
+595     # return true if c <= '9'
+596     b8/copy-to-EAX  1/imm32/true
+597     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x39/imm32        # compare ECX
+598     7e/jump-if-lesser-or-equal  $is-hex-digit?:end/disp8
+599     # return true if c >= 'a'
+600     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x61/imm32        # compare ECX
+601     7d/jump-if-greater-or-equal  $is-hex-digit?:end/disp8
+602     # otherwise return false
+603     b8/copy-to-EAX  0/imm32/false
+604 $is-hex-digit?:end:
+605     # . restore registers
+606     59/pop-to-ECX
+607     # . epilog
+608     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+609     5d/pop-to-EBP
+610     c3/return
+611 
+612 test-hex-below-0:
+613     # EAX = is-hex-digit?(0x2f)
+614     # . . push args
+615     68/push  0x2f/imm32
+616     # . . call
+617     e8/call  is-hex-digit?/disp32
+618     # . . discard args
+619     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+620     # check-ints-equal(EAX, 0, msg)
+621     # . . push args
+622     68/push  "F - test-hex-below-0"/imm32
+623     68/push  0/imm32/false
+624     50/push-EAX
+625     # . . call
+626     e8/call  check-ints-equal/disp32
+627     # . . discard args
+628     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+629     c3/return
+630 
+631 test-hex-0-to-9:
+632     # EAX = is-hex-digit?(0x30)
+633     # . . push args
+634     68/push  0x30/imm32
+635     # . . call
+636     e8/call  is-hex-digit?/disp32
+637     # . . discard args
+638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+639     # check-ints-equal(EAX, 1, msg)
+640     # . . push args
+641     68/push  "F - test-hex-at-0"/imm32
+642     68/push  1/imm32/true
+643     50/push-EAX
+644     # . . call
+645     e8/call  check-ints-equal/disp32
+646     # . . discard args
+647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+648     # EAX = is-hex-digit?(0x39)
+649     # . . push args
+650     68/push  0x39/imm32
+651     # . . call
+652     e8/call  is-hex-digit?/disp32
+653     # . . discard args
+654     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+655     # check-ints-equal(EAX, 1, msg)
+656     # . . push args
+657     68/push  "F - test-hex-at-9"/imm32
+658     68/push  1/imm32/true
+659     50/push-EAX
+660     # . . call
+661     e8/call  check-ints-equal/disp32
+662     # . . discard args
+663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+664     c3/return
+665 
+666 test-hex-above-9-to-a:
+667     # EAX = is-hex-digit?(0x3a)
+668     # . . push args
+669     68/push  0x3a/imm32
+670     # . . call
+671     e8/call  is-hex-digit?/disp32
+672     # . . discard args
+673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+674     # check-ints-equal(EAX, 0, msg)
+675     # . . push args
+676     68/push  "F - test-hex-above-9-to-a"/imm32
+677     68/push  0/imm32/false
+678     50/push-EAX
+679     # . . call
+680     e8/call  check-ints-equal/disp32
+681     # . . discard args
+682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+683     c3/return
+684 
+685 test-hex-a-to-f:
+686     # EAX = is-hex-digit?(0x61)
+687     # . . push args
+688     68/push  0x61/imm32
+689     # . . call
+690     e8/call  is-hex-digit?/disp32
+691     # . . discard args
+692     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+693     # check-ints-equal(EAX, 1, msg)
+694     # . . push args
+695     68/push  "F - test-hex-at-a"/imm32
+696     68/push  1/imm32/true
+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     # EAX = is-hex-digit?(0x66)
+703     # . . push args
+704     68/push  0x66/imm32
+705     # . . call
+706     e8/call  is-hex-digit?/disp32
+707     # . . discard args
+708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+709     # check-ints-equal(EAX, 1, msg)
+710     # . . push args
+711     68/push  "F - test-hex-at-f"/imm32
+712     68/push  1/imm32/true
+713     50/push-EAX
+714     # . . call
+715     e8/call  check-ints-equal/disp32
+716     # . . discard args
+717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+718     c3/return
+719 
+720 test-hex-above-f:
+721     # EAX = is-hex-digit?(0x67)
+722     # . . push args
+723     68/push  0x67/imm32
+724     # . . call
+725     e8/call  is-hex-digit?/disp32
+726     # . . discard args
+727     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+728     # check-ints-equal(EAX, 0, msg)
+729     # . . push args
+730     68/push  "F - test-hex-above-f"/imm32
+731     68/push  0/imm32/false
+732     50/push-EAX
+733     # . . call
+734     e8/call  check-ints-equal/disp32
+735     # . . discard args
+736     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+737     c3/return
+738 
+739 from-hex-char:  # in/EAX : byte -> out/EAX : num
+740     # no error checking; accepts argument in EAX
+741     # if EAX <= '9' return EAX - '0'
+742     3d/compare-EAX  0x39/imm32/9
+743     7f/jump-if-greater  $from-hex-char:else/disp8
+744     2d/subtract-from-EAX  0x30/imm32/0
+745     c3/return
+746 $from-hex-char:else:
+747     # otherwise return EAX - 'a' + 10
+748     2d/subtract-from-EAX  0x57/imm32/a-10
+749     c3/return
+750 
+751 to-hex-char:  # in/EAX : nibble -> out/EAX : byte
+752     # no error checking; accepts argument in EAX
+753     # if EAX <= 9 return EAX + '0'
+754     3d/compare-EAX  0x9/imm32/9
+755     7f/jump-if-greater  $to-hex-char:else/disp8
+756     05/add-to-EAX  0x30/imm32/0
+757     c3/return
+758 $to-hex-char:else:
+759     # otherwise return EAX + 'a' - 10
+760     05/add-to-EAX  0x57/imm32/a-10
+761     c3/return
+762 
+763 == data
+764 
+765 _test-slice-empty:
+766   # nothing
+767 _test-slice-empty-end:
+768 
+769 _test-slice-hex-int:
+770   33/3 34/4
+771 _test-slice-hex-int-end:
+772 
+773 _test-slice-hex-int-letters-negative:
+774   2d/-
+775 _test-slice-hex-int-letters:
+776   33/3 34/4 61/a
+777 _test-slice-hex-int-letters-end:
+778 
+779 _test-slice-hex-int-single-letter:
+780   61/a
+781 _test-slice-hex-int-single-letter-end:
+782 
+783 _test-slice-char-and-digits:
+784   71/q 33/3 34/4
+785 _test-slice-char-and-digits-end:
+786 
+787 _test-slice-digits-and-char:
+788   33/3 34/4 71/q
+789 _test-slice-digits-and-char-end:
+790 
+791 _test-slice-hex-int-with-0x-prefix-negative:
+792   2d/-
+793 _test-slice-hex-int-with-0x-prefix:
+794   30/0 78/x 33/3 34/4
+795 _test-slice-hex-int-with-0x-prefix-end:
+796 
+797 _test-slice-hex-int-zero:
+798   30/0
+799 _test-slice-hex-int-zero-end:
+800 
+801 _test-slice-hex-int-with-0-prefix:
+802   30/0 33/3
+803 _test-slice-hex-int-with-0-prefix-end:
+804 
+805 _test-slice-hex-int-negative-with-0-prefix:
+806   2d/- 30/0 33/3
+807 _test-slice-hex-int-negative-with-0-prefix-end:
+808 
+809 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/065write-buffered.subx.html b/html/subx/065write-buffered.subx.html deleted file mode 100644 index b437f121..00000000 --- a/html/subx/065write-buffered.subx.html +++ /dev/null @@ -1,286 +0,0 @@ - - - - -Mu - subx/065write-buffered.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/065write-buffered.subx -
-  1 # write-buffered: like 'write', but for a buffered-file
-  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 # main:
-  9 #?     e8/call test-write-buffered/disp32
- 10 #?     e8/call test-write-buffered-with-intermediate-flush/disp32
- 11     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 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 write-buffered:  # f : (address buffered-file), msg : (address array byte) -> <void>
- 18     # pseudocode:
- 19     #   in = msg->data
- 20     #   inend = &msg->data[msg->length]
- 21     #   while in < inend
- 22     #     if f->write >= f->length
- 23     #       flush(f)
- 24     #       clear-stream(f)
- 25     #     f->data[f->write] = *in
- 26     #     ++f->write
- 27     #     ++in
- 28     #
- 29     # registers:
- 30     #   ESI : in
- 31     #   ECX : inend
- 32     #   EDI : f
- 33     #   EDX : f->length
- 34     #   EBX : f->write (cached copy, need to keep in sync)
- 35     #   EAX : temp
- 36     #
- 37     # . prolog
- 38     55/push-EBP
- 39     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 40     # . save registers
- 41     50/push-EAX
- 42     51/push-ECX
- 43     52/push-EDX
- 44     53/push-EBX
- 45     56/push-ESI
- 46     57/push-EDI
- 47     # EAX = msg
- 48     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
- 49     # in/ESI = msg->data
- 50     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           6/r32/ESI   4/disp8         .                 # copy EAX+4 to ESI
- 51     # inend/ECX = &msg->data[msg->length]
- 52     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
- 53     8d/copy-address                 0/mod/indirect  4/rm32/sib    6/base/ESI  1/index/ECX   .           1/r32/ECX   .               .                 # copy ESI+ECX to ECX
- 54     # EDI = f
- 55     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
- 56     # EDX = f->length
- 57     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EDI+12) to EDX
- 58     # EBX = f->write
- 59     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(EDI+4) to EBX
- 60 $write-buffered:loop:
- 61     # if (in >= inend) break
- 62     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ESI with ECX
- 63     7d/jump-if-greater-or-equal  $write-buffered:loop-end/disp8
- 64     # if (f->write >= f->length) flush and clear f's stream
- 65     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX with EDX
- 66     7c/jump-if-lesser  $write-buffered:to-stream/disp8
- 67     # . persist f->write
- 68     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
- 69     # . flush(f)
- 70     # . . push args
- 71     57/push-EDI
- 72     # . . call
- 73     e8/call  flush/disp32
- 74     # . . discard args
- 75     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 76     # . clear-stream(stream = f+4)
- 77     # . . push args
- 78     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
- 79     50/push-EAX
- 80     # . . call
- 81     e8/call  clear-stream/disp32
- 82     # . . discard args
- 83     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 84     # . f->write must now be 0; update its cache at EBX
- 85     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
- 86 $write-buffered:to-stream:
- 87     # write to stream
- 88     # f->data[f->write] = *in
- 89     # . AL = *in
- 90     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 91     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
- 92     # . f->data[f->write] = AL
- 93     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)
- 94     # ++f->write
- 95     43/increment-EBX
- 96     # ++in
- 97     46/increment-ESI
- 98     eb/jump  $write-buffered:loop/disp8
- 99 $write-buffered:loop-end:
-100     # persist necessary variables from registers
-101     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
-102 $write-buffered:end:
-103     # . restore registers
-104     5f/pop-to-EDI
-105     5e/pop-to-ESI
-106     5b/pop-to-EBX
-107     5a/pop-to-EDX
-108     59/pop-to-ECX
-109     58/pop-to-EAX
-110     # . epilog
-111     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-112     5d/pop-to-EBP
-113     c3/return
-114 
-115 test-write-buffered:
-116     # - check that write-buffered writes to the file
-117     # setup
-118     # . clear-stream(_test-stream)
-119     # . . push args
-120     68/push  _test-stream/imm32
-121     # . . call
-122     e8/call  clear-stream/disp32
-123     # . . discard args
-124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-125     # . clear-stream(_test-buffered-file+4)
-126     # . . push args
-127     b8/copy-to-EAX  _test-buffered-file/imm32
-128     05/add-to-EAX  4/imm32
-129     50/push-EAX
-130     # . . call
-131     e8/call  clear-stream/disp32
-132     # . . discard args
-133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-134     # write-buffered(_test-buffered-file, "Abc")
-135     # . . push args
-136     68/push  "Abc"/imm32
-137     68/push  _test-buffered-file/imm32
-138     # . . call
-139     e8/call  write-buffered/disp32
-140     # . . discard args
-141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-142     # flush(_test-buffered-file)
-143     # . . push args
-144     68/push  _test-buffered-file/imm32
-145     # . . call
-146     e8/call  flush/disp32
-147     # . . discard args
-148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-149     # check-ints-equal(*_test-stream->data, "Abc", msg)
-150     # . . push args
-151     68/push  "F - test-write-buffered-single"/imm32
-152     68/push  0x636241/imm32
-153     # . . push *_test-stream->data
-154     b8/copy-to-EAX  _test-stream/imm32
-155     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-156     # . . call
-157     e8/call  check-ints-equal/disp32
-158     # . . discard args
-159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-160     # . end
-161     c3/return
-162 
-163 test-write-buffered-with-intermediate-flush:
-164     # - check that write-buffered flushes in the middle if its buffer fills up
-165     # setup
-166     # . clear-stream(_test-stream)
-167     # . . push args
-168     68/push  _test-stream/imm32
-169     # . . call
-170     e8/call  clear-stream/disp32
-171     # . . discard args
-172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-173     # . clear-stream(_test-buffered-file+4)
-174     # . . push args
-175     b8/copy-to-EAX  _test-buffered-file/imm32
-176     05/add-to-EAX  4/imm32
-177     50/push-EAX
-178     # . . call
-179     e8/call  clear-stream/disp32
-180     # . . discard args
-181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-182     # _test-stream can hold 8 bytes, but _test-buffered-file can hold only 6.
-183     # Try to write 7 bytes.
-184     # . write-buffered(_test-buffered-file, "Abcdefg")
-185     # . . push args
-186     68/push  "Abcdefg"/imm32
-187     68/push  _test-buffered-file/imm32
-188     # . . call
-189     e8/call  write-buffered/disp32
-190     # . . discard args
-191     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-192     # don't flush
-193     # 6 bytes should still have gotten to _test-stream
-194     # . check-ints-equal(*_test-stream->write, 6, msg)
-195     # . . push args
-196     68/push  "F - test-write-buffered-with-intermediate-flush: flushed data"/imm32
-197     68/push  6/imm32
-198     # . . push *_test-stream->write
-199     b8/copy-to-EAX  _test-stream/imm32
-200     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
-201     # . . call
-202     e8/call  check-ints-equal/disp32
-203     # . . discard args
-204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-205     # and 1 byte should still be in _test-buffered-file
-206     # . check-ints-equal(*_test-buffered-file>write, 1, msg)
-207     # . . push args
-208     68/push  "F - test-write-buffered-with-intermediate-flush: unflushed bytes"/imm32
-209     68/push  1/imm32
-210     # . . push *_test-buffered-file->write
-211     b8/copy-to-EAX  _test-buffered-file/imm32
-212     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-213     # . . call
-214     e8/call  check-ints-equal/disp32
-215     # . . discard args
-216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-217     # . end
-218     c3/return
-219 
-220 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/066error-byte.subx.html b/html/subx/066error-byte.subx.html deleted file mode 100644 index 5f5b8284..00000000 --- a/html/subx/066error-byte.subx.html +++ /dev/null @@ -1,176 +0,0 @@ - - - - -Mu - subx/066error-byte.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/066error-byte.subx -
-  1 # Print an error message followed by the text representation of a byte. Then exit.
-  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 # main:
-  9     # manual test
- 10 #?     # . var ed/EAX : exit-descriptor
- 11 #?     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
- 12 #?     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
- 13 #?     # . configure ed to really exit()
- 14 #?     # . . ed->target = 0
- 15 #?     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
- 16 #?     # . error-byte(ed, Stdout, msg, 34)
- 17 #?     68/push  0x34/imm32
- 18 #?     68/push  "abc"/imm32
- 19 #?     68/push  Stderr/imm32
- 20 #?     50/push-EAX
- 21 #?     e8/call  error-byte/disp32
- 22     # automatic test
- 23     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 24     # . syscall(exit, Num-test-failures)
- 25     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 26     b8/copy-to-EAX  1/imm32/exit
- 27     cd/syscall  0x80/imm8
- 28 
- 29 # write(out, "Error: "+msg+": "+byte) then stop(ed, 1)
- 30 error-byte:  # ed : (address exit-descriptor), out : (address buffered-file), msg : (address array byte), n : byte -> <void>
- 31     # . prolog
- 32     55/push-EBP
- 33     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 34     # write-buffered(out, "Error: ")
- 35     # . . push args
- 36     68/push  "Error: "/imm32
- 37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 38     # . . call
- 39     e8/call  write-buffered/disp32
- 40     # . . discard args
- 41     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 42     # write-buffered(out, msg)
- 43     # . . push args
- 44     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
- 45     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 46     # . . call
- 47     e8/call  write-buffered/disp32
- 48     # . . discard args
- 49     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 50     # write-buffered(out, ": ")
- 51     # . . push args
- 52     68/push  ": "/imm32
- 53     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 54     # . . call
- 55     e8/call  write-buffered/disp32
- 56     # . . discard args
- 57     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 58     # print-byte(out, byte)
- 59     # . . push args
- 60     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
- 61     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 62     # . . call
- 63     e8/call  print-byte/disp32
- 64     # . . discard args
- 65     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 66     # write-buffered(out, Newline)
- 67     # . . push args
- 68     68/push  Newline/imm32
- 69     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 70     # . . call
- 71     e8/call  write-buffered/disp32
- 72     # . . discard args
- 73     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 74     # . flush(out)
- 75     # . . push args
- 76     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 77     # . . call
- 78     e8/call  flush/disp32
- 79     # . . discard args
- 80     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 81     # stop(ed, 1)
- 82     # . . push args
- 83     68/push  1/imm32
- 84     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 85     # . . call
- 86     e8/call  stop/disp32
- 87     # should never get past this point
- 88 $error-byte:dead-end:
- 89     # . epilog
- 90     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 91     5d/pop-to-EBP
- 92     c3/return
- 93 
- 94 == data
- 95 
- 96 # The buffered file for standard error.
- 97 Stderr:
- 98     # file descriptor or (address stream)
- 99     02 00 00 00  # 1 = standard error
-100     # current write index
-101     00 00 00 00
-102     # current read index
-103     00 00 00 00
-104     # length (8)
-105     08 00 00 00
-106     # data
-107     00 00 00 00 00 00 00 00  # 8 bytes
-108 
-109 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
-110 # I don't want to type in 1024 bytes here.
-111 
-112 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/066print-byte.subx.html b/html/subx/066print-byte.subx.html new file mode 100644 index 00000000..99fc717f --- /dev/null +++ b/html/subx/066print-byte.subx.html @@ -0,0 +1,167 @@ + + + + +Mu - subx/066print-byte.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/066print-byte.subx +
+  1 # Print the (hex) textual representation of the lowest byte of a number.
+  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 # main:
+  9     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 10     # syscall(exit, Num-test-failures)
+ 11     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 12     b8/copy-to-EAX  1/imm32/exit
+ 13     cd/syscall  0x80/imm8
+ 14 
+ 15 print-byte:  # f : (address buffered-file), n : int -> <void>
+ 16     # . prolog
+ 17     55/push-EBP
+ 18     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 19     # . save registers
+ 20     50/push-EAX
+ 21     # AL = convert upper nibble to hex
+ 22     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
+ 23     c1/shift    5/subop/logic-right 3/mod/direct    0/rm32/EAX    .           .             .           .           .               4/imm8            # shift EAX right by 4 bits, while padding zeroes
+ 24     25/and-EAX  0xf/imm32
+ 25     # . AL = to-hex-char(AL)
+ 26     e8/call  to-hex-char/disp32
+ 27     # write-byte(f, AL)
+ 28     # . . push args
+ 29     50/push-EAX
+ 30     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 31     # . . call
+ 32     e8/call  write-byte/disp32
+ 33     # . . discard args
+ 34     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 35     # AL = convert lower nibble to hex
+ 36     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
+ 37     25/and-EAX  0xf/imm32
+ 38     # . AL = to-hex-char(AL)
+ 39     e8/call  to-hex-char/disp32
+ 40     # write-byte(f, AL)
+ 41     # . . push args
+ 42     50/push-EAX
+ 43     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 44     # . . call
+ 45     e8/call  write-byte/disp32
+ 46     # . . discard args
+ 47     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 48 $print-byte:end:
+ 49     # . restore registers
+ 50     58/pop-to-EAX
+ 51     # . epilog
+ 52     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 53     5d/pop-to-EBP
+ 54     c3/return
+ 55 
+ 56 test-print-byte:
+ 57     # - check that print-byte prints the hex textual representation
+ 58     # setup
+ 59     # . clear-stream(_test-stream)
+ 60     # . . push args
+ 61     68/push  _test-stream/imm32
+ 62     # . . call
+ 63     e8/call  clear-stream/disp32
+ 64     # . . discard args
+ 65     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 66     # . clear-stream(_test-buffered-file+4)
+ 67     # . . push args
+ 68     b8/copy-to-EAX  _test-buffered-file/imm32
+ 69     05/add-to-EAX  4/imm32
+ 70     50/push-EAX
+ 71     # . . call
+ 72     e8/call  clear-stream/disp32
+ 73     # . . discard args
+ 74     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 75     # print-byte(_test-buffered-file, 0xa)  # exercises digit, non-digit as well as leading zero
+ 76     # . . push args
+ 77     68/push  0xa/imm32
+ 78     68/push  _test-buffered-file/imm32
+ 79     # . . call
+ 80     e8/call  print-byte/disp32
+ 81     # . . discard args
+ 82     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 83     # flush(_test-buffered-file)
+ 84     # . . push args
+ 85     68/push  _test-buffered-file/imm32
+ 86     # . . call
+ 87     e8/call  flush/disp32
+ 88     # . . discard args
+ 89     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 90     # check-stream-equal(_test-stream, "0a", msg)
+ 91     # . . push args
+ 92     68/push  "F - test-print-byte"/imm32
+ 93     68/push  "0a"/imm32
+ 94     68/push  _test-stream/imm32
+ 95     # . . call
+ 96     e8/call  check-stream-equal/disp32
+ 97     # . . discard args
+ 98     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 99     # . end
+100     c3/return
+101 
+102 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/067allocate.subx.html b/html/subx/067allocate.subx.html deleted file mode 100644 index 15af4e6c..00000000 --- a/html/subx/067allocate.subx.html +++ /dev/null @@ -1,271 +0,0 @@ - - - - -Mu - subx/067allocate.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/067allocate.subx -
-  1 # Helper to dynamically allocate memory on the heap.
-  2 #
-  3 # We'd like to be able to write tests for functions that allocate memory,
-  4 # making assertions on the precise addresses used. To achieve this we'll pass
-  5 # in an *allocation descriptor* to allocate from.
-  6 #
-  7 # Allocation descriptors are also useful outside of tests. Assembly and machine
-  8 # code are of necessity unsafe languages, and one of the most insidious kinds
-  9 # of bugs unsafe languages expose us to are dangling pointers to memory that
- 10 # has been freed and potentially even reused for something totally different.
- 11 # To reduce the odds of such "use after free" errors, SubX programs tend to not
- 12 # reclaim and reuse dynamically allocated memory. (Running out of memory is far
- 13 # easier to debug.) Long-running programs that want to reuse memory are mostly
- 14 # on their own to be careful. However, they do get one bit of help: they can
- 15 # carve out chunks of memory and then allocate from them manually using this
- 16 # very same 'allocate' helper. They just need a new allocation descriptor for
- 17 # their book-keeping.
- 18 
- 19 == data
- 20 
- 21 # The 'global' allocation descriptor. Pass this into 'allocate' to claim a
- 22 # hitherto unused bit of memory.
- 23 Heap:
- 24     Start-of-heap/imm32  # curr
- 25     00 00 00 0b  # limit = 0x0b000000; keep sync'd with DATA_SEGMENT + SEGMENT_ALIGNMENT
- 26 
- 27 == code
- 28 #   instruction                     effective address                                                   register    displacement    immediate
- 29 # . op          subop               mod             rm32          base        index         scale       r32
- 30 # . 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
- 31 
- 32 # main:
- 33     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 34     # syscall(exit, Num-test-failures)
- 35     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 36     b8/copy-to-EAX  1/imm32/exit
- 37     cd/syscall  0x80/imm8
- 38 
- 39 # Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr.
- 40 # If there isn't enough memory before ad->limit, return 0 and leave 'ad' unmodified.
- 41 allocate:  # ad : (address allocation-descriptor), n : int -> address-or-null/EAX
- 42     # . prolog
- 43     55/push-EBP
- 44     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 45     # . save registers
- 46     51/push-ECX
- 47     52/push-EDX
- 48     # ECX = ad
- 49     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
- 50     # save ad->curr
- 51     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
- 52     # check if there's enough space
- 53     # . EDX = ad->curr + n
- 54     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
- 55     03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # add *(EBP+12) to EDX
- 56     3b/compare                      1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # compare EDX with *(ECX+4)
- 57     7c/jump-if-lesser  $allocate:commit/disp8
- 58     # return null if not
- 59     b8/copy-to-EAX  0/imm32
- 60     eb/jump  $allocate:end/disp8
- 61 $allocate:commit:
- 62     # update ad->curr
- 63     89/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy EDX to *ECX
- 64 $allocate:end:
- 65     # . restore registers
- 66     5a/pop-to-EDX
- 67     59/pop-to-ECX
- 68     # . epilog
- 69     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 70     5d/pop-to-EBP
- 71     c3/return
- 72 
- 73 test-allocate-success:
- 74     # . prolog
- 75     55/push-EBP
- 76     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 77     # var ad/ECX : (address allocation-descriptor) = {11, 15}
- 78     68/push  0xf/imm32/limit
- 79     68/push  0xb/imm32/curr
- 80     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 81     # EAX = allocate(ad, 3)
- 82     # . . push args
- 83     68/push  3/imm32
- 84     51/push-ECX
- 85     # . . call
- 86     e8/call  allocate/disp32
- 87     # . . discard args
- 88     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 89     # check-ints-equal(EAX, 11, msg)
- 90     # . . push args
- 91     68/push  "F - test-allocate-success: returns current pointer of allocation descriptor"/imm32
- 92     68/push  0xb/imm32
- 93     50/push-EAX
- 94     # . . call
- 95     e8/call  check-ints-equal/disp32
- 96     # . . discard args
- 97     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 98     # check-ints-equal(ad->curr, 14, msg)
- 99     # . . push args
-100     68/push  "F - test-allocate-success: updates allocation descriptor"/imm32
-101     68/push  0xe/imm32
-102     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
-103     # . . call
-104     e8/call  check-ints-equal/disp32
-105     # . . discard args
-106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-107     # . epilog
-108     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-109     5d/pop-to-EBP
-110     c3/return
-111 
-112 test-allocate-failure:
-113     # . prolog
-114     55/push-EBP
-115     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-116     # var ad/ECX : (address allocation-descriptor) = {11, 15}
-117     68/push  0xf/imm32/limit
-118     68/push  0xb/imm32/curr
-119     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-120     # EAX = allocate(ad, 6)
-121     # . . push args
-122     68/push  6/imm32
-123     51/push-ECX
-124     # . . call
-125     e8/call  allocate/disp32
-126     # . . discard args
-127     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-128     # check-ints-equal(EAX, 0, msg)
-129     # . . push args
-130     68/push  "F - test-allocate-failure: returns null"/imm32
-131     68/push  0/imm32
-132     50/push-EAX
-133     # . . call
-134     e8/call  check-ints-equal/disp32
-135     # . . discard args
-136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-137     # no change to ad->curr
-138     # . check-ints-equal(ad->curr, 11)
-139     # . . push args
-140     68/push  "F - test-allocate-failure: updates allocation descriptor"/imm32
-141     68/push  0xb/imm32
-142     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
-143     # . . call
-144     e8/call  check-ints-equal/disp32
-145     # . . discard args
-146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-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 # helper: create a nested allocation descriptor (useful for tests)
-153 allocate-region:  # ad : (address allocation-descriptor), n : int -> new-ad : (address allocation-descriptor)
-154     # . prolog
-155     55/push-EBP
-156     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-157     # . save registers
-158     51/push-ECX
-159     # EAX = allocate(ad, n)
-160     # . . push args
-161     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-162     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-163     # . . call
-164     e8/call  allocate/disp32
-165     # . . discard args
-166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-167     # if EAX == 0 abort
-168     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
-169     74/jump-if-equal  $allocate-region:abort/disp8
-170     # earmark 8 bytes at the start for a new allocation descriptor
-171     # . *EAX = EAX + 8
-172     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
-173     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
-174     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
-175     # . *(EAX+4) = EAX + n
-176     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
-177     03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # add *(EBP+12) to ECX
-178     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(EAX+4)
-179     # . restore registers
-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 # We could create a more general '$abort' jump target, but then we'd need to do
-187 # a conditional jump followed by loading the error message and an unconditional
-188 # jump. Or we'd need to unconditionally load the error message before a
-189 # conditional jump, even if it's unused the vast majority of the time. This way
-190 # we bloat a potentially cold segment in RAM so we can abort with a single
-191 # instruction.
-192 $allocate-region:abort:
-193     # . _write(2/stderr, error)
-194     # . . push args
-195     68/push  "allocate-region: failed to allocate"/imm32
-196     68/push  2/imm32/stderr
-197     # . . call
-198     e8/call  _write/disp32
-199     # . . discard args
-200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-201     # . syscall(exit, 1)
-202     bb/copy-to-EBX  1/imm32
-203     b8/copy-to-EAX  1/imm32/exit
-204     cd/syscall  0x80/imm8
-205     # never gets here
-206 
-207 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/067write-buffered.subx.html b/html/subx/067write-buffered.subx.html new file mode 100644 index 00000000..a1cf9a29 --- /dev/null +++ b/html/subx/067write-buffered.subx.html @@ -0,0 +1,284 @@ + + + + +Mu - subx/067write-buffered.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/067write-buffered.subx +
+  1 # write-buffered: like 'write', but for a buffered-file
+  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 # main:
+  9 #?     e8/call test-write-buffered/disp32
+ 10 #?     e8/call test-write-buffered-with-intermediate-flush/disp32
+ 11     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 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 write-buffered:  # f : (address buffered-file), msg : (address array byte) -> <void>
+ 18     # pseudocode:
+ 19     #   in = msg->data
+ 20     #   inend = &msg->data[msg->length]
+ 21     #   while in < inend
+ 22     #     if f->write >= f->length
+ 23     #       flush(f)
+ 24     #       clear-stream(f)
+ 25     #     f->data[f->write] = *in
+ 26     #     ++f->write
+ 27     #     ++in
+ 28     #
+ 29     # registers:
+ 30     #   ESI : in
+ 31     #   ECX : inend
+ 32     #   EDI : f
+ 33     #   EDX : f->length
+ 34     #   EBX : f->write (cached copy, need to keep in sync)
+ 35     #   EAX : temp
+ 36     #
+ 37     # . prolog
+ 38     55/push-EBP
+ 39     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 40     # . save registers
+ 41     50/push-EAX
+ 42     51/push-ECX
+ 43     52/push-EDX
+ 44     53/push-EBX
+ 45     56/push-ESI
+ 46     57/push-EDI
+ 47     # EAX = msg
+ 48     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
+ 49     # in/ESI = msg->data
+ 50     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           6/r32/ESI   4/disp8         .                 # copy EAX+4 to ESI
+ 51     # inend/ECX = &msg->data[msg->length]
+ 52     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+ 53     8d/copy-address                 0/mod/indirect  4/rm32/sib    6/base/ESI  1/index/ECX   .           1/r32/ECX   .               .                 # copy ESI+ECX to ECX
+ 54     # EDI = f
+ 55     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+ 56     # EDX = f->length
+ 57     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EDI+12) to EDX
+ 58     # EBX = f->write
+ 59     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(EDI+4) to EBX
+ 60 $write-buffered:loop:
+ 61     # if (in >= inend) break
+ 62     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ESI with ECX
+ 63     7d/jump-if-greater-or-equal  $write-buffered:loop-end/disp8
+ 64     # if (f->write >= f->length) flush and clear f's stream
+ 65     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX with EDX
+ 66     7c/jump-if-lesser  $write-buffered:to-stream/disp8
+ 67     # . persist f->write
+ 68     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
+ 69     # . flush(f)
+ 70     # . . push args
+ 71     57/push-EDI
+ 72     # . . call
+ 73     e8/call  flush/disp32
+ 74     # . . discard args
+ 75     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 76     # . clear-stream(stream = f+4)
+ 77     # . . push args
+ 78     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
+ 79     50/push-EAX
+ 80     # . . call
+ 81     e8/call  clear-stream/disp32
+ 82     # . . discard args
+ 83     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 84     # . f->write must now be 0; update its cache at EBX
+ 85     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+ 86 $write-buffered:to-stream:
+ 87     # write to stream
+ 88     # f->data[f->write] = *in
+ 89     # . AL = *in
+ 90     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 91     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
+ 92     # . f->data[f->write] = AL
+ 93     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)
+ 94     # ++f->write
+ 95     43/increment-EBX
+ 96     # ++in
+ 97     46/increment-ESI
+ 98     eb/jump  $write-buffered:loop/disp8
+ 99 $write-buffered:loop-end:
+100     # persist necessary variables from registers
+101     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
+102 $write-buffered:end:
+103     # . restore registers
+104     5f/pop-to-EDI
+105     5e/pop-to-ESI
+106     5b/pop-to-EBX
+107     5a/pop-to-EDX
+108     59/pop-to-ECX
+109     58/pop-to-EAX
+110     # . epilog
+111     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+112     5d/pop-to-EBP
+113     c3/return
+114 
+115 test-write-buffered:
+116     # - check that write-buffered writes to the file
+117     # setup
+118     # . clear-stream(_test-stream)
+119     # . . push args
+120     68/push  _test-stream/imm32
+121     # . . call
+122     e8/call  clear-stream/disp32
+123     # . . discard args
+124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+125     # . clear-stream(_test-buffered-file+4)
+126     # . . push args
+127     b8/copy-to-EAX  _test-buffered-file/imm32
+128     05/add-to-EAX  4/imm32
+129     50/push-EAX
+130     # . . call
+131     e8/call  clear-stream/disp32
+132     # . . discard args
+133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+134     # write-buffered(_test-buffered-file, "Abc")
+135     # . . push args
+136     68/push  "Abc"/imm32
+137     68/push  _test-buffered-file/imm32
+138     # . . call
+139     e8/call  write-buffered/disp32
+140     # . . discard args
+141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+142     # flush(_test-buffered-file)
+143     # . . push args
+144     68/push  _test-buffered-file/imm32
+145     # . . call
+146     e8/call  flush/disp32
+147     # . . discard args
+148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+149     # check-stream-equal(_test-stream, "Abc", msg)
+150     # . . push args
+151     68/push  "F - test-write-buffered-single"/imm32
+152     68/push  "Abc"/imm32
+153     68/push  _test-stream/imm32
+154     # . . call
+155     e8/call  check-stream-equal/disp32
+156     # . . discard args
+157     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+158     # . end
+159     c3/return
+160 
+161 test-write-buffered-with-intermediate-flush:
+162     # - check that write-buffered flushes in the middle if its buffer fills up
+163     # setup
+164     # . clear-stream(_test-stream)
+165     # . . push args
+166     68/push  _test-stream/imm32
+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     # . clear-stream(_test-buffered-file+4)
+172     # . . push args
+173     b8/copy-to-EAX  _test-buffered-file/imm32
+174     05/add-to-EAX  4/imm32
+175     50/push-EAX
+176     # . . call
+177     e8/call  clear-stream/disp32
+178     # . . discard args
+179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+180     # _test-stream can hold 8 bytes, but _test-buffered-file can hold only 6.
+181     # Try to write 7 bytes.
+182     # . write-buffered(_test-buffered-file, "Abcdefg")
+183     # . . push args
+184     68/push  "Abcdefg"/imm32
+185     68/push  _test-buffered-file/imm32
+186     # . . call
+187     e8/call  write-buffered/disp32
+188     # . . discard args
+189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+190     # don't flush
+191     # 6 bytes should still have gotten to _test-stream
+192     # . check-ints-equal(*_test-stream->write, 6, msg)
+193     # . . push args
+194     68/push  "F - test-write-buffered-with-intermediate-flush: flushed data"/imm32
+195     68/push  6/imm32
+196     # . . push *_test-stream->write
+197     b8/copy-to-EAX  _test-stream/imm32
+198     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # 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     # and 1 byte should still be in _test-buffered-file
+204     # . check-ints-equal(*_test-buffered-file->write, 1, msg)
+205     # . . push args
+206     68/push  "F - test-write-buffered-with-intermediate-flush: unflushed bytes"/imm32
+207     68/push  1/imm32
+208     # . . push *_test-buffered-file->write
+209     b8/copy-to-EAX  _test-buffered-file/imm32
+210     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+211     # . . call
+212     e8/call  check-ints-equal/disp32
+213     # . . discard args
+214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+215     # . end
+216     c3/return
+217 
+218 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/068error-byte.subx.html b/html/subx/068error-byte.subx.html new file mode 100644 index 00000000..a8a5c7a7 --- /dev/null +++ b/html/subx/068error-byte.subx.html @@ -0,0 +1,176 @@ + + + + +Mu - subx/068error-byte.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/068error-byte.subx +
+  1 # Print an error message followed by the text representation of a byte. Then exit.
+  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 # main:
+  9     # manual test
+ 10 #?     # . var ed/EAX : exit-descriptor
+ 11 #?     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 12 #?     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
+ 13 #?     # . configure ed to really exit()
+ 14 #?     # . . ed->target = 0
+ 15 #?     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+ 16 #?     # . error-byte(ed, Stdout, msg, 34)
+ 17 #?     68/push  0x34/imm32
+ 18 #?     68/push  "abc"/imm32
+ 19 #?     68/push  Stderr/imm32
+ 20 #?     50/push-EAX
+ 21 #?     e8/call  error-byte/disp32
+ 22     # automatic test
+ 23     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 24     # . syscall(exit, Num-test-failures)
+ 25     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 26     b8/copy-to-EAX  1/imm32/exit
+ 27     cd/syscall  0x80/imm8
+ 28 
+ 29 # write(out, "Error: "+msg+": "+byte) then stop(ed, 1)
+ 30 error-byte:  # ed : (address exit-descriptor), out : (address buffered-file), msg : (address array byte), n : byte -> <void>
+ 31     # . prolog
+ 32     55/push-EBP
+ 33     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 34     # write-buffered(out, "Error: ")
+ 35     # . . push args
+ 36     68/push  "Error: "/imm32
+ 37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 38     # . . call
+ 39     e8/call  write-buffered/disp32
+ 40     # . . discard args
+ 41     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 42     # write-buffered(out, msg)
+ 43     # . . push args
+ 44     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+ 45     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 46     # . . call
+ 47     e8/call  write-buffered/disp32
+ 48     # . . discard args
+ 49     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 50     # write-buffered(out, ": ")
+ 51     # . . push args
+ 52     68/push  ": "/imm32
+ 53     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 54     # . . call
+ 55     e8/call  write-buffered/disp32
+ 56     # . . discard args
+ 57     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 58     # print-byte(out, byte)
+ 59     # . . push args
+ 60     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
+ 61     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 62     # . . call
+ 63     e8/call  print-byte/disp32
+ 64     # . . discard args
+ 65     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 66     # write-buffered(out, Newline)
+ 67     # . . push args
+ 68     68/push  Newline/imm32
+ 69     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 70     # . . call
+ 71     e8/call  write-buffered/disp32
+ 72     # . . discard args
+ 73     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 74     # . flush(out)
+ 75     # . . push args
+ 76     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 77     # . . call
+ 78     e8/call  flush/disp32
+ 79     # . . discard args
+ 80     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 81     # stop(ed, 1)
+ 82     # . . push args
+ 83     68/push  1/imm32
+ 84     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 85     # . . call
+ 86     e8/call  stop/disp32
+ 87     # should never get past this point
+ 88 $error-byte:dead-end:
+ 89     # . epilog
+ 90     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 91     5d/pop-to-EBP
+ 92     c3/return
+ 93 
+ 94 == data
+ 95 
+ 96 # The buffered file for standard error.
+ 97 Stderr:
+ 98     # file descriptor or (address stream)
+ 99     02 00 00 00  # 1 = standard error
+100     # current write index
+101     00 00 00 00
+102     # current read index
+103     00 00 00 00
+104     # length (8)
+105     08 00 00 00
+106     # data
+107     00 00 00 00 00 00 00 00  # 8 bytes
+108 
+109 # TODO: 8 bytes is too small. We'll need to grow the buffer for efficiency. But
+110 # I don't want to type in 1024 bytes here.
+111 
+112 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/068new-stream.subx.html b/html/subx/068new-stream.subx.html deleted file mode 100644 index 17f79a22..00000000 --- a/html/subx/068new-stream.subx.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - -Mu - subx/068new-stream.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/068new-stream.subx -
-  1 # Helper to allocate a stream on the heap.
-  2 #
-  3 # We'll now start gingerly supporting streams containing arbitrary types.
-  4 
-  5 == code
-  6 #   instruction                     effective address                                                   register    displacement    immediate
-  7 # . op          subop               mod             rm32          base        index         scale       r32
-  8 # . 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
-  9 
- 10 # main:
- 11     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 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 new-stream:  # ad : (address allocation-descriptor), length : int, elemsize : int -> address/EAX
- 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     52/push-EDX
- 23     # n = elemsize * length + 12 (for read, write and length)
- 24     # . EAX = elemsize
- 25     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
- 26     # . EAX *= length
- 27     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
- 28     f7          4/subop/multiply    1/mod/*+disp8   5/rm32/EBP    .           .                                     0xc/disp8       .                 # multiply *(EBP+12) into EAX
- 29     # . if overflow abort
- 30     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
- 31     75/jump-if-not-equal  $new-stream:abort/disp8
- 32     # . EDX = elemsize*length
- 33     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
- 34     # . EAX += 12
- 35     05/add-to-EAX  0xc/imm32
- 36     # allocate(ad, n)
- 37     # . . push args
- 38     50/push-EAX
- 39     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 40     # . . call
- 41     e8/call  allocate/disp32
- 42     # . . discard args
- 43     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 44     # EAX->length = elemsize*length
- 45     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   8/disp8         .                 # copy EDX to *(EAX+8)
- 46     # clear-stream(EAX)
- 47     # . . push args
- 48     50/push-EAX
- 49     # . . call
- 50     e8/call  clear-stream/disp32
- 51     # . . discard args
- 52     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 53 $new-stream:end:
- 54     # . restore registers
- 55     5a/pop-to-EDX
- 56     # . epilog
- 57     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 58     5d/pop-to-EBP
- 59     c3/return
- 60 
- 61 $new-stream:abort:
- 62     # . _write(2/stderr, error)
- 63     # . . push args
- 64     68/push  "new-stream: size too large"/imm32
- 65     68/push  2/imm32/stderr
- 66     # . . call
- 67     e8/call  _write/disp32
- 68     # . . discard args
- 69     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 70     # . syscall(exit, 1)
- 71     bb/copy-to-EBX  1/imm32
- 72     b8/copy-to-EAX  1/imm32/exit
- 73     cd/syscall  0x80/imm8
- 74     # never gets here
- 75 
- 76 test-new-stream:
- 77     # . prolog
- 78     55/push-EBP
- 79     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 80     # var ad/ECX : (address allocation-descriptor) = allocate-region(Heap, 512)
- 81     # . EAX = allocate-region(Heap, 512)
- 82     # . . push args
- 83     68/push  0x200/imm32
- 84     68/push  Heap/imm32
- 85     # . . call
- 86     e8/call  allocate-region/disp32
- 87     # . . discard args
- 88     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 89     # . ECX = EAX
- 90     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
- 91     # var start/EDX = ad->curr
- 92     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
- 93     # EAX = new-stream(ad, 3, 2)
- 94     # . . push args
- 95     68/push  2/imm32
- 96     68/push  3/imm32
- 97     51/push-ECX
- 98     # . . call
- 99     e8/call  new-stream/disp32
-100     # . . discard args
-101     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-102     # check-ints-equal(EAX, EDX, msg)
-103     # . . push args
-104     68/push  "F - test-new-stream: returns current pointer of allocation descriptor"/imm32
-105     52/push-EDX
-106     50/push-EAX
-107     # . . call
-108     e8/call  check-ints-equal/disp32
-109     # . . discard args
-110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-111     # check-ints-equal(EAX->length, 6, msg)
-112     # . . push args
-113     68/push  "F - test-new-stream: sets length correctly"/imm32
-114     68/push  6/imm32
-115     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           .               8/disp8           # push *(EAX+8)
-116     # . . call
-117     e8/call  check-ints-equal/disp32
-118     # . . discard args
-119     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-120     # the rest is delegated to clear-stream() so we won't bother checking it
-121     # . epilog
-122     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-123     5d/pop-to-EBP
-124     c3/return
-125 
-126 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/069allocate.subx.html b/html/subx/069allocate.subx.html new file mode 100644 index 00000000..e5f5efcb --- /dev/null +++ b/html/subx/069allocate.subx.html @@ -0,0 +1,271 @@ + + + + +Mu - subx/069allocate.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/069allocate.subx +
+  1 # Helper to dynamically allocate memory on the heap.
+  2 #
+  3 # We'd like to be able to write tests for functions that allocate memory,
+  4 # making assertions on the precise addresses used. To achieve this we'll pass
+  5 # in an *allocation descriptor* to allocate from.
+  6 #
+  7 # Allocation descriptors are also useful outside of tests. Assembly and machine
+  8 # code are of necessity unsafe languages, and one of the most insidious kinds
+  9 # of bugs unsafe languages expose us to are dangling pointers to memory that
+ 10 # has been freed and potentially even reused for something totally different.
+ 11 # To reduce the odds of such "use after free" errors, SubX programs tend to not
+ 12 # reclaim and reuse dynamically allocated memory. (Running out of memory is far
+ 13 # easier to debug.) Long-running programs that want to reuse memory are mostly
+ 14 # on their own to be careful. However, they do get one bit of help: they can
+ 15 # carve out chunks of memory and then allocate from them manually using this
+ 16 # very same 'allocate' helper. They just need a new allocation descriptor for
+ 17 # their book-keeping.
+ 18 
+ 19 == data
+ 20 
+ 21 # The 'global' allocation descriptor. Pass this into 'allocate' to claim a
+ 22 # hitherto unused bit of memory.
+ 23 Heap:
+ 24     Start-of-heap/imm32  # curr
+ 25     00 00 00 0b  # limit = 0x0b000000; keep sync'd with DATA_SEGMENT + SEGMENT_ALIGNMENT
+ 26 
+ 27 == code
+ 28 #   instruction                     effective address                                                   register    displacement    immediate
+ 29 # . op          subop               mod             rm32          base        index         scale       r32
+ 30 # . 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
+ 31 
+ 32 # main:
+ 33     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 34     # syscall(exit, Num-test-failures)
+ 35     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 36     b8/copy-to-EAX  1/imm32/exit
+ 37     cd/syscall  0x80/imm8
+ 38 
+ 39 # Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr.
+ 40 # If there isn't enough memory before ad->limit, return 0 and leave 'ad' unmodified.
+ 41 allocate:  # ad : (address allocation-descriptor), n : int -> address-or-null/EAX
+ 42     # . prolog
+ 43     55/push-EBP
+ 44     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 45     # . save registers
+ 46     51/push-ECX
+ 47     52/push-EDX
+ 48     # ECX = ad
+ 49     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+ 50     # save ad->curr
+ 51     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+ 52     # check if there's enough space
+ 53     # . EDX = ad->curr + n
+ 54     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
+ 55     03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # add *(EBP+12) to EDX
+ 56     3b/compare                      1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # compare EDX with *(ECX+4)
+ 57     7c/jump-if-lesser  $allocate:commit/disp8
+ 58     # return null if not
+ 59     b8/copy-to-EAX  0/imm32
+ 60     eb/jump  $allocate:end/disp8
+ 61 $allocate:commit:
+ 62     # update ad->curr
+ 63     89/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy EDX to *ECX
+ 64 $allocate:end:
+ 65     # . restore registers
+ 66     5a/pop-to-EDX
+ 67     59/pop-to-ECX
+ 68     # . epilog
+ 69     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 70     5d/pop-to-EBP
+ 71     c3/return
+ 72 
+ 73 test-allocate-success:
+ 74     # . prolog
+ 75     55/push-EBP
+ 76     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 77     # var ad/ECX : (address allocation-descriptor) = {11, 15}
+ 78     68/push  0xf/imm32/limit
+ 79     68/push  0xb/imm32/curr
+ 80     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 81     # EAX = allocate(ad, 3)
+ 82     # . . push args
+ 83     68/push  3/imm32
+ 84     51/push-ECX
+ 85     # . . call
+ 86     e8/call  allocate/disp32
+ 87     # . . discard args
+ 88     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 89     # check-ints-equal(EAX, 11, msg)
+ 90     # . . push args
+ 91     68/push  "F - test-allocate-success: returns current pointer of allocation descriptor"/imm32
+ 92     68/push  0xb/imm32
+ 93     50/push-EAX
+ 94     # . . call
+ 95     e8/call  check-ints-equal/disp32
+ 96     # . . discard args
+ 97     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 98     # check-ints-equal(ad->curr, 14, msg)
+ 99     # . . push args
+100     68/push  "F - test-allocate-success: updates allocation descriptor"/imm32
+101     68/push  0xe/imm32
+102     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+103     # . . call
+104     e8/call  check-ints-equal/disp32
+105     # . . discard args
+106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+107     # . epilog
+108     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+109     5d/pop-to-EBP
+110     c3/return
+111 
+112 test-allocate-failure:
+113     # . prolog
+114     55/push-EBP
+115     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+116     # var ad/ECX : (address allocation-descriptor) = {11, 15}
+117     68/push  0xf/imm32/limit
+118     68/push  0xb/imm32/curr
+119     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+120     # EAX = allocate(ad, 6)
+121     # . . push args
+122     68/push  6/imm32
+123     51/push-ECX
+124     # . . call
+125     e8/call  allocate/disp32
+126     # . . discard args
+127     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+128     # check-ints-equal(EAX, 0, msg)
+129     # . . push args
+130     68/push  "F - test-allocate-failure: returns null"/imm32
+131     68/push  0/imm32
+132     50/push-EAX
+133     # . . call
+134     e8/call  check-ints-equal/disp32
+135     # . . discard args
+136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+137     # no change to ad->curr
+138     # . check-ints-equal(ad->curr, 11)
+139     # . . push args
+140     68/push  "F - test-allocate-failure: updates allocation descriptor"/imm32
+141     68/push  0xb/imm32
+142     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+143     # . . call
+144     e8/call  check-ints-equal/disp32
+145     # . . discard args
+146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+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 # helper: create a nested allocation descriptor (useful for tests)
+153 allocate-region:  # ad : (address allocation-descriptor), n : int -> new-ad : (address allocation-descriptor)
+154     # . prolog
+155     55/push-EBP
+156     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+157     # . save registers
+158     51/push-ECX
+159     # EAX = allocate(ad, n)
+160     # . . push args
+161     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+162     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+163     # . . call
+164     e8/call  allocate/disp32
+165     # . . discard args
+166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+167     # if EAX == 0 abort
+168     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
+169     74/jump-if-equal  $allocate-region:abort/disp8
+170     # earmark 8 bytes at the start for a new allocation descriptor
+171     # . *EAX = EAX + 8
+172     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+173     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
+174     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
+175     # . *(EAX+4) = EAX + n
+176     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+177     03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # add *(EBP+12) to ECX
+178     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(EAX+4)
+179     # . restore registers
+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 # We could create a more general '$abort' jump target, but then we'd need to do
+187 # a conditional jump followed by loading the error message and an unconditional
+188 # jump. Or we'd need to unconditionally load the error message before a
+189 # conditional jump, even if it's unused the vast majority of the time. This way
+190 # we bloat a potentially cold segment in RAM so we can abort with a single
+191 # instruction.
+192 $allocate-region:abort:
+193     # . _write(2/stderr, error)
+194     # . . push args
+195     68/push  "allocate-region: failed to allocate"/imm32
+196     68/push  2/imm32/stderr
+197     # . . call
+198     e8/call  _write/disp32
+199     # . . discard args
+200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+201     # . syscall(exit, 1)
+202     bb/copy-to-EBX  1/imm32
+203     b8/copy-to-EAX  1/imm32/exit
+204     cd/syscall  0x80/imm8
+205     # never gets here
+206 
+207 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/069read-line.subx.html b/html/subx/069read-line.subx.html deleted file mode 100644 index 66e1bc2f..00000000 --- a/html/subx/069read-line.subx.html +++ /dev/null @@ -1,377 +0,0 @@ - - - - -Mu - subx/069read-line.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/069read-line.subx -
-  1 == code
-  2 #   instruction                     effective address                                                   register    displacement    immediate
-  3 # . op          subop               mod             rm32          base        index         scale       r32
-  4 # . 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
-  5 
-  6 # main:
-  7     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
-  8     # syscall(exit, Num-test-failures)
-  9     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 10     b8/copy-to-EAX  1/imm32/exit
- 11     cd/syscall  0x80/imm8
- 12 
- 13 # read bytes from 'f' until (and including) a newline and store them into 's'
- 14 # return true if no data found, false otherwise
- 15 # just abort if 's' is too small
- 16 read-line:  # f : (address buffered-file), s : (address stream byte) -> eof?/EAX
- 17     # pseudocode:
- 18     #   loop:
- 19     #     if (s->write >= s->length) abort
- 20     #     if (f->read >= f->write) populate stream from file
- 21     #     if (f->write == 0) break
- 22     #     AL = f->data[f->read]
- 23     #     s->data[s->write] = AL
- 24     #     ++f->read
- 25     #     ++s->write
- 26     #     if AL == '\n' break
- 27     # . prolog
- 28     55/push-EBP
- 29     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 30     # . save registers
- 31     51/push-ECX
- 32     52/push-EDX
- 33     56/push-ESI
- 34     57/push-EDI
- 35     # ESI = f
- 36     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
- 37     # ECX = f->read
- 38     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(ESI+8) to ECX
- 39     # EDI = s
- 40     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
- 41     # EDX = s->write
- 42     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy *EDI to EDX
- 43 $read-line:loop:
- 44     # if (s->write >= s->length) abort
- 45     3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # compare EDX with *(EDI+8)
- 46     7d/jump-if-greater-or-equal  $read-line:abort/disp8
- 47     # if (f->read >= f->write) populate stream from file
- 48     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # compare ECX with *(ESI+4)
- 49     7c/jump-if-lesser  $read-line:from-stream/disp8
- 50     # . clear-stream(stream = f+4)
- 51     # . . push args
- 52     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy ESI+4 to EAX
- 53     50/push-EAX
- 54     # . . call
- 55     e8/call  clear-stream/disp32
- 56     # . . discard args
- 57     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 58     # . f->read must now be 0; update its cache at ECX
- 59     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
- 60     # . EAX = read(f->fd, stream = f+4)
- 61     # . . push args
- 62     50/push-EAX
- 63     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
- 64     # . . call
- 65     e8/call  read/disp32
- 66     # . . discard args
- 67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 68     # if f->write == 0 return true
- 69     # . if EAX == 0 return true
- 70     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
- 71     75/jump-if-not-equal  $read-line:from-stream/disp8
- 72     b8/copy-to-EAX  0xffffffff/imm32
- 73     eb/jump  $read-line:end/disp8
- 74 $read-line:from-stream:
- 75     # AL = f->data[f->read]
- 76     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 77     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0x10/disp8      .                 # copy byte at *(ESI+ECX+16) to AL
- 78     # s->data[s->write] = AL
- 79     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           0/r32/AL    0xc/disp8       .                 # copy AL to *(EDI+EDX+12)
- 80     # ++f->read
- 81     41/increment-ECX
- 82     # ++s->write
- 83     42/increment-EDX
- 84     # if AL == '\n' return false
- 85     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32         # compare EAX
- 86     75/jump-if-not-equal  $read-line:loop/disp8
- 87     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 88 $read-line:end:
- 89     # save f->read
- 90     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # copy ECX to *(ESI+8)
- 91     # save s->write
- 92     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy EDX to *EDI
- 93     # . restore registers
- 94     5f/pop-to-EDI
- 95     5e/pop-to-ESI
- 96     5a/pop-to-EDX
- 97     59/pop-to-ECX
- 98     # . epilog
- 99     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-100     5d/pop-to-EBP
-101     c3/return
-102 
-103 $read-line:abort:
-104     # . _write(2/stderr, error)
-105     # . . push args
-106     68/push  "read-line: line too long"/imm32
-107     68/push  2/imm32/stderr
-108     # . . call
-109     e8/call  _write/disp32
-110     # . . discard args
-111     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-112     # . syscall(exit, 1)
-113     bb/copy-to-EBX  1/imm32
-114     b8/copy-to-EAX  1/imm32/exit
-115     cd/syscall  0x80/imm8
-116     # never gets here
-117 
-118 test-read-line:
-119     # - check that read-line stops at a newline
-120     # setup
-121     # . clear-stream(_test-stream)
-122     # . . push args
-123     68/push  _test-stream/imm32
-124     # . . call
-125     e8/call  clear-stream/disp32
-126     # . . discard args
-127     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-128     # . clear-stream(_test-buffered-file+4)
-129     # . . push args
-130     b8/copy-to-EAX  _test-buffered-file/imm32
-131     05/add-to-EAX  4/imm32
-132     50/push-EAX
-133     # . . call
-134     e8/call  clear-stream/disp32
-135     # . . discard args
-136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-137     # . clear-stream(_test-stream-buffer)
-138     # . . push args
-139     68/push  _test-stream-buffer/imm32
-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     # write(_test-stream, "ab\ncd")
-145     # . write(_test-stream, "ab")
-146     # . . push args
-147     68/push  "ab"/imm32
-148     68/push  _test-stream/imm32
-149     # . . call
-150     e8/call  write/disp32
-151     # . . discard args
-152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-153     # . write(_test-stream, "\n")
-154     # . . push args
-155     68/push Newline/imm32
-156     68/push  _test-stream/imm32
-157     # . . call
-158     e8/call  write/disp32
-159     # . . discard args
-160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-161     # . write(_test-stream, "cd")
-162     # . . push args
-163     68/push  "cd"/imm32
-164     68/push  _test-stream/imm32
-165     # . . call
-166     e8/call  write/disp32
-167     # . . discard args
-168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-169     # read a line from _test-stream (buffered by _test-buffered-file) into _test-stream-buffer
-170     # . EAX = read-line(_test-buffered-file, _test-stream-buffer)
-171     # . . push args
-172     68/push  _test-stream-buffer/imm32
-173     68/push  _test-buffered-file/imm32
-174     # . . call
-175     e8/call  read-line/disp32
-176     # . . discard args
-177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-178     # check-ints-equal(EAX, 0/not-at-eof, msg)
-179     # . . push args
-180     68/push  "F - test-read-line: return value"/imm32
-181     68/push  0/imm32/not-at-eof
-182     50/push-EAX
-183     # . . call
-184     e8/call  check-ints-equal/disp32
-185     # . . discard args
-186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-187     # check-ints-equal(*_test-stream-buffer->data, 61/a 62/b 0a/newline 00, msg)
-188     # . . push args
-189     68/push  "F - test-read-line"/imm32
-190     68/push  0x000a6261/imm32
-191     # . . push *_test-stream->data
-192     b8/copy-to-EAX  _test-stream-buffer/imm32
-193     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-194     # . . call
-195     e8/call  check-ints-equal/disp32
-196     # . . discard args
-197     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-198     # end
-199     c3/return
-200 
-201 test-read-line-returns-true-on-eof:
-202     # setup
-203     # . clear-stream(_test-stream)
-204     # . . push args
-205     68/push  _test-stream/imm32
-206     # . . call
-207     e8/call  clear-stream/disp32
-208     # . . discard args
-209     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-210     # . clear-stream(_test-buffered-file+4)
-211     # . . push args
-212     b8/copy-to-EAX  _test-buffered-file/imm32
-213     05/add-to-EAX  4/imm32
-214     50/push-EAX
-215     # . . call
-216     e8/call  clear-stream/disp32
-217     # . . discard args
-218     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-219     # . clear-stream(_test-stream-buffer)
-220     # . . push args
-221     68/push  _test-stream-buffer/imm32
-222     # . . call
-223     e8/call  clear-stream/disp32
-224     # . . discard args
-225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-226     # write nothing
-227     # EAX = read-line(_test-buffered-file, _test-stream-buffer)
-228     # . . push args
-229     68/push  _test-stream-buffer/imm32
-230     68/push  _test-buffered-file/imm32
-231     # . . call
-232     e8/call  read-line/disp32
-233     # . . discard args
-234     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-235     # check-ints-equal(EAX, eof, msg)
-236     # . . push args
-237     68/push  "F - test-read-line-returns-true-on-eof"/imm32
-238     68/push  0xffffffff/imm32/not-at-eof
-239     50/push-EAX
-240     # . . call
-241     e8/call  check-ints-equal/disp32
-242     # . . discard args
-243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-244     # end
-245     c3/return
-246 
-247 test-read-line-reads-final-line-until-eof:
-248     # setup
-249     # . clear-stream(_test-stream)
-250     # . . push args
-251     68/push  _test-stream/imm32
-252     # . . call
-253     e8/call  clear-stream/disp32
-254     # . . discard args
-255     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-256     # . clear-stream(_test-buffered-file+4)
-257     # . . push args
-258     b8/copy-to-EAX  _test-buffered-file/imm32
-259     05/add-to-EAX  4/imm32
-260     50/push-EAX
-261     # . . call
-262     e8/call  clear-stream/disp32
-263     # . . discard args
-264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-265     # . clear-stream(_test-stream-buffer)
-266     # . . push args
-267     68/push  _test-stream-buffer/imm32
-268     # . . call
-269     e8/call  clear-stream/disp32
-270     # . . discard args
-271     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-272     # write(_test-stream, "cd")
-273     # . . push args
-274     68/push  "cd"/imm32
-275     68/push  _test-stream/imm32
-276     # . . call
-277     e8/call  write/disp32
-278     # . . discard args
-279     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-280     # read a line from _test-stream (buffered by _test-buffered-file) into _test-stream-buffer
-281     # . EAX = read-line(_test-buffered-file, _test-stream-buffer)
-282     # . . push args
-283     68/push  _test-stream-buffer/imm32
-284     68/push  _test-buffered-file/imm32
-285     # . . call
-286     e8/call  read-line/disp32
-287     # . . discard args
-288     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-289     # check-ints-equal(EAX, eof, msg)
-290     # . . push args
-291     68/push  "F - test-read-line-reads-final-line-until-eof: return value"/imm32
-292     68/push  0xffffffff/imm32/not-at-eof
-293     50/push-EAX
-294     # . . call
-295     e8/call  check-ints-equal/disp32
-296     # . . discard args
-297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-298     # check-ints-equal(*_test-stream-buffer->data, 63/c 64/d 00 00, msg)
-299     # . . push args
-300     68/push  "F - test-read-line-reads-final-line-until-eof"/imm32
-301     68/push  0x00006463/imm32
-302     # . . push *_test-stream->data
-303     b8/copy-to-EAX  _test-stream-buffer/imm32
-304     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-305     # . . call
-306     e8/call  check-ints-equal/disp32
-307     # . . discard args
-308     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-309     # end
-310     c3/return
-311 
-312 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/070new-stream.subx.html b/html/subx/070new-stream.subx.html new file mode 100644 index 00000000..a43e7da2 --- /dev/null +++ b/html/subx/070new-stream.subx.html @@ -0,0 +1,188 @@ + + + + +Mu - subx/070new-stream.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/070new-stream.subx +
+  1 # Helper to allocate a stream on the heap.
+  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 # main:
+  9     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 10     # syscall(exit, Num-test-failures)
+ 11     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 12     b8/copy-to-EAX  1/imm32/exit
+ 13     cd/syscall  0x80/imm8
+ 14 
+ 15 new-stream:  # ad : (address allocation-descriptor), length : int, elemsize : int -> address/EAX
+ 16     # . prolog
+ 17     55/push-EBP
+ 18     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 19     # . save registers
+ 20     52/push-EDX
+ 21     # n = elemsize * length + 12 (for read, write and length)
+ 22     # . EAX = elemsize
+ 23     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
+ 24     # . EAX *= length
+ 25     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
+ 26     f7          4/subop/multiply    1/mod/*+disp8   5/rm32/EBP    .           .                                     0xc/disp8       .                 # multiply *(EBP+12) into EAX
+ 27     # . if overflow abort
+ 28     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
+ 29     75/jump-if-not-equal  $new-stream:abort/disp8
+ 30     # . EDX = elemsize*length
+ 31     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
+ 32     # . EAX += 12
+ 33     05/add-to-EAX  0xc/imm32
+ 34     # allocate(ad, n)
+ 35     # . . push args
+ 36     50/push-EAX
+ 37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 38     # . . call
+ 39     e8/call  allocate/disp32
+ 40     # . . discard args
+ 41     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 42     # EAX->length = elemsize*length
+ 43     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   8/disp8         .                 # copy EDX to *(EAX+8)
+ 44     # clear-stream(EAX)
+ 45     # . . push args
+ 46     50/push-EAX
+ 47     # . . call
+ 48     e8/call  clear-stream/disp32
+ 49     # . . discard args
+ 50     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 51 $new-stream:end:
+ 52     # . restore registers
+ 53     5a/pop-to-EDX
+ 54     # . epilog
+ 55     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 56     5d/pop-to-EBP
+ 57     c3/return
+ 58 
+ 59 $new-stream:abort:
+ 60     # . _write(2/stderr, error)
+ 61     # . . push args
+ 62     68/push  "new-stream: size too large"/imm32
+ 63     68/push  2/imm32/stderr
+ 64     # . . call
+ 65     e8/call  _write/disp32
+ 66     # . . discard args
+ 67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 68     # . syscall(exit, 1)
+ 69     bb/copy-to-EBX  1/imm32
+ 70     b8/copy-to-EAX  1/imm32/exit
+ 71     cd/syscall  0x80/imm8
+ 72     # never gets here
+ 73 
+ 74 test-new-stream:
+ 75     # . prolog
+ 76     55/push-EBP
+ 77     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 78     # var ad/ECX : (address allocation-descriptor) = allocate-region(Heap, 512)
+ 79     # . EAX = allocate-region(Heap, 512)
+ 80     # . . push args
+ 81     68/push  0x200/imm32
+ 82     68/push  Heap/imm32
+ 83     # . . call
+ 84     e8/call  allocate-region/disp32
+ 85     # . . discard args
+ 86     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 87     # . ECX = EAX
+ 88     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+ 89     # var start/EDX = ad->curr
+ 90     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
+ 91     # EAX = new-stream(ad, 3, 2)
+ 92     # . . push args
+ 93     68/push  2/imm32
+ 94     68/push  3/imm32
+ 95     51/push-ECX
+ 96     # . . call
+ 97     e8/call  new-stream/disp32
+ 98     # . . discard args
+ 99     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+100     # check-ints-equal(EAX, EDX, msg)
+101     # . . push args
+102     68/push  "F - test-new-stream: returns current pointer of allocation descriptor"/imm32
+103     52/push-EDX
+104     50/push-EAX
+105     # . . call
+106     e8/call  check-ints-equal/disp32
+107     # . . discard args
+108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+109     # check-ints-equal(EAX->length, 6, msg)
+110     # . . push args
+111     68/push  "F - test-new-stream: sets length correctly"/imm32
+112     68/push  6/imm32
+113     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           .               8/disp8           # push *(EAX+8)
+114     # . . call
+115     e8/call  check-ints-equal/disp32
+116     # . . discard args
+117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+118     # the rest is delegated to clear-stream() so we won't bother checking it
+119     # . epilog
+120     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+121     5d/pop-to-EBP
+122     c3/return
+123 
+124 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/070slice.subx.html b/html/subx/070slice.subx.html deleted file mode 100644 index c92a3306..00000000 --- a/html/subx/070slice.subx.html +++ /dev/null @@ -1,586 +0,0 @@ - - - - -Mu - subx/070slice.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/070slice.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 # main:
- 10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
- 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) -> bool/EAX
- 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 with 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) -> bool/EAX
- 99     # . prolog
-100     55/push-EBP
-101     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-102     # . save registers
-103     51/push-ECX
-104     52/push-EDX
-105     53/push-EBX
-106     56/push-ESI
-107     # ESI = s
-108     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-109     # curr/EDX = s->start
-110     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
-111     # max/ESI = s->end
-112     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
-113     # EBX = p
-114     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
-115     # EAX = s->end - s->start
-116     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy ESI to EAX
-117     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # subtract EDX from EAX
-118     # if (EAX != p->length) return false;
-119     39/compare                      0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # compare *EBX and EAX
-120     75/jump-if-not-equal  $slice-equal?:false/disp8
-121     # skip p->length
-122     81          0/subop/add         3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm32           # add to EBX
-123     # EAX = ECX = false
-124     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-125     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
-126 $slice-equal?:loop:
-127     # if (curr >= max) return true
-128     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX and ESI
-129     7d/jump-if-greater-or-equal  $slice-equal?:true/disp8
-130     # AL = *p
-131     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
-132     # CL = *curr
-133     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
-134     # if (EAX != ECX) return false
-135     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX and ECX
-136     75/jump-if-not-equal  $slice-equal?:false/disp8
-137     # ++p
-138     43/increment-EBX
-139     # ++curr
-140     42/increment-EDX
-141     eb/jump $slice-equal?:loop/disp8
-142 $slice-equal?:false:
-143     b8/copy-to-EAX  0/imm32
-144     eb/jump  $slice-equal?:end/disp8
-145 $slice-equal?:true:
-146     b8/copy-to-EAX  1/imm32
-147 $slice-equal?:end:
-148     # . restore registers
-149     5e/pop-to-ESI
-150     5b/pop-to-EBX
-151     5a/pop-to-EDX
-152     59/pop-to-ECX
-153     # . epilog
-154     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-155     5d/pop-to-EBP
-156     c3/return
-157 
-158 test-slice-equal:
-159     # - slice-equal?(slice("Abc"), "Abc") == 1
-160     # . prolog
-161     55/push-EBP
-162     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-163     # var slice/ECX
-164     68/push  _test-slice-data-3/imm32/end
-165     68/push  _test-slice-data-0/imm32/start
-166     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-167     # EAX = slice-equal?(ECX, "Abc")
-168     # . . push args
-169     68/push  "Abc"/imm32
-170     51/push-ECX
-171     # . . call
-172     e8/call  slice-equal?/disp32
-173     # . . discard args
-174     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-175     # check-ints-equal(EAX, 1, msg)
-176     # . . push args
-177     68/push  "F - test-slice-equal"/imm32
-178     68/push  1/imm32
-179     50/push-EAX
-180     # . . call
-181     e8/call  check-ints-equal/disp32
-182     # . . discard args
-183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-184     # . epilog
-185     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-186     5d/pop-to-EBP
-187     c3/return
-188 
-189 test-slice-equal-false:
-190     # - slice-equal?(slice("bcd"), "Abc") == 0
-191     # . prolog
-192     55/push-EBP
-193     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-194     # var slice/ECX
-195     68/push  _test-slice-data-4/imm32/end
-196     68/push  _test-slice-data-1/imm32/start
-197     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-198     # EAX = slice-equal?(ECX, "Abc")
-199     # . . push args
-200     68/push  "Abc"/imm32
-201     51/push-ECX
-202     # . . call
-203     e8/call  slice-equal?/disp32
-204     # . . discard args
-205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-206     # check-ints-equal(EAX, 0, msg)
-207     # . . push args
-208     68/push  "F - test-slice-equal-false"/imm32
-209     68/push  0/imm32
-210     50/push-EAX
-211     # . . call
-212     e8/call  check-ints-equal/disp32
-213     # . . discard args
-214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-215     # . epilog
-216     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-217     5d/pop-to-EBP
-218     c3/return
-219 
-220 test-slice-equal-too-long:
-221     # - slice-equal?(slice("Abcd"), "Abc") == 0
-222     # . prolog
-223     55/push-EBP
-224     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-225     # var slice/ECX
-226     68/push  _test-slice-data-4/imm32/end
-227     68/push  _test-slice-data-0/imm32/start
-228     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-229     # EAX = slice-equal?(ECX, "Abc")
-230     # . . push args
-231     68/push  "Abc"/imm32
-232     51/push-ECX
-233     # . . call
-234     e8/call  slice-equal?/disp32
-235     # . . discard args
-236     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-237     # check-ints-equal(EAX, 0, msg)
-238     # . . push args
-239     68/push  "F - test-slice-equal-too-long"/imm32
-240     68/push  0/imm32
-241     50/push-EAX
-242     # . . call
-243     e8/call  check-ints-equal/disp32
-244     # . . discard args
-245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-246     # . epilog
-247     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-248     5d/pop-to-EBP
-249     c3/return
-250 
-251 test-slice-equal-too-short:
-252     # - slice-equal?(slice("A"), "Abc") == 0
-253     # . prolog
-254     55/push-EBP
-255     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-256     # var slice/ECX
-257     68/push  _test-slice-data-1/imm32/end
-258     68/push  _test-slice-data-0/imm32/start
-259     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-260     # EAX = slice-equal?(ECX, "Abc")
-261     # . . push args
-262     68/push  "Abc"/imm32
-263     51/push-ECX
-264     # . . call
-265     e8/call  slice-equal?/disp32
-266     # . . discard args
-267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-268     # check-ints-equal(EAX, 0, msg)
-269     # . . push args
-270     68/push  "F - test-slice-equal-too-short"/imm32
-271     68/push  0/imm32
-272     50/push-EAX
-273     # . . call
-274     e8/call  check-ints-equal/disp32
-275     # . . discard args
-276     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-277     # . epilog
-278     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-279     5d/pop-to-EBP
-280     c3/return
-281 
-282 test-slice-equal-empty:
-283     # - slice-equal?(slice(""), "Abc") == 0
-284     # . prolog
-285     55/push-EBP
-286     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-287     # var slice/ECX
-288     68/push  _test-slice-data-0/imm32/end
-289     68/push  _test-slice-data-0/imm32/start
-290     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-291     # EAX = slice-equal?(ECX, "Abc")
-292     # . . push args
-293     68/push  "Abc"/imm32
-294     51/push-ECX
-295     # . . call
-296     e8/call  slice-equal?/disp32
-297     # . . discard args
-298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-299     # check-ints-equal(EAX, 0, msg)
-300     # . . push args
-301     68/push  "F - test-slice-equal-empty"/imm32
-302     68/push  0/imm32
-303     50/push-EAX
-304     # . . call
-305     e8/call  check-ints-equal/disp32
-306     # . . discard args
-307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-308     # . epilog
-309     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-310     5d/pop-to-EBP
-311     c3/return
-312 
-313 test-slice-equal-with-empty:
-314     # - slice-equal?(slice("Ab"), "") == 0
-315     # . prolog
-316     55/push-EBP
-317     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-318     # var slice/ECX
-319     68/push  _test-slice-data-2/imm32/end
-320     68/push  _test-slice-data-0/imm32/start
-321     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-322     # EAX = slice-equal?(ECX, "")
-323     # . . push args
-324     68/push  ""/imm32
-325     51/push-ECX
-326     # . . call
-327     e8/call  slice-equal?/disp32
-328     # . . discard args
-329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-330     # check-ints-equal(EAX, 0, msg)
-331     # . . push args
-332     68/push  "F - test-slice-equal-with-empty"/imm32
-333     68/push  0/imm32
-334     50/push-EAX
-335     # . . call
-336     e8/call  check-ints-equal/disp32
-337     # . . discard args
-338     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-339     # . epilog
-340     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-341     5d/pop-to-EBP
-342     c3/return
-343 
-344 test-slice-equal-empty-with-empty:
-345     # - slice-equal?(slice(""), "") == 1
-346     # . prolog
-347     55/push-EBP
-348     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-349     # var slice/ECX
-350     68/push  _test-slice-data-0/imm32/end
-351     68/push  _test-slice-data-0/imm32/start
-352     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-353     # EAX = slice-equal?(ECX, "")
-354     # . . push args
-355     68/push  ""/imm32
-356     51/push-ECX
-357     # . . call
-358     e8/call  slice-equal?/disp32
-359     # . . discard args
-360     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-361     # check-ints-equal(EAX, 1, msg)
-362     # . . push args
-363     68/push  "F - test-slice-equal-empty-with-empty"/imm32
-364     68/push  1/imm32
-365     50/push-EAX
-366     # . . call
-367     e8/call  check-ints-equal/disp32
-368     # . . discard args
-369     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-370     # . epilog
-371     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-372     5d/pop-to-EBP
-373     c3/return
-374 
-375 write-slice:  # out : (address buffered-file), s : (address slice)
-376     # . prolog
-377     55/push-EBP
-378     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-379     # . save registers
-380     50/push-EAX
-381     51/push-ECX
-382     52/push-EDX
-383     53/push-EBX
-384     56/push-ESI
-385     57/push-EDI
-386     # ESI = s
-387     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
-388     # curr/ECX = s->start
-389     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
-390     # max/ESI = s->end
-391     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
-392     # EDI = f
-393     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
-394     # EDX = f->length
-395     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EDI+12) to EDX
-396     # EBX = f->write
-397     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(EDI+4) to EBX
-398 $write-slice:loop:
-399     # if (curr >= max) break
-400     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # compare ECX with ESI
-401     7d/jump-if-greater-or-equal  $write-slice:loop-end/disp8
-402     # if (f->write >= f->length) flush and clear f's stream
-403     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX with EDX
-404     7c/jump-if-lesser  $write-slice:to-stream/disp8
-405     # . persist f->write
-406     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
-407     # . flush(f)
-408     # . . push args
-409     57/push-EDI
-410     # . . call
-411     e8/call  flush/disp32
-412     # . . discard args
-413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-414     # . clear-stream(stream = f+4)
-415     # . . push args
-416     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
-417     50/push-EAX
-418     # . . call
-419     e8/call  clear-stream/disp32
-420     # . . discard args
-421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-422     # . f->write must now be 0; update its cache at EBX
-423     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-424 $write-slice:to-stream:
-425     # f->data[f->write] = *in
-426     # . AL = *in
-427     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-428     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-429     # . f->data[f->write] = AL
-430     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)
-431     # ++f->write
-432     43/increment-EBX
-433     # ++in
-434     41/increment-ECX
-435     eb/jump  $write-slice:loop/disp8
-436 $write-slice:loop-end:
-437     # persist necessary variables from registers
-438     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
-439 $write-slice:end:
-440     # . restore registers
-441     5f/pop-to-EDI
-442     5e/pop-to-ESI
-443     5b/pop-to-EBX
-444     5a/pop-to-EDX
-445     59/pop-to-ECX
-446     58/pop-to-EAX
-447     # . epilog
-448     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-449     5d/pop-to-EBP
-450     c3/return
-451 
-452 test-write-slice:
-453     # . prolog
-454     55/push-EBP
-455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-456     # setup
-457     # . clear-stream(_test-stream)
-458     # . . push args
-459     68/push  _test-stream/imm32
-460     # . . call
-461     e8/call  clear-stream/disp32
-462     # . . discard args
-463     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-464     # . clear-stream(_test-buffered-file+4)
-465     # . . push args
-466     b8/copy-to-EAX  _test-buffered-file/imm32
-467     05/add-to-EAX  4/imm32
-468     50/push-EAX
-469     # . . call
-470     e8/call  clear-stream/disp32
-471     # . . discard args
-472     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-473     # var slice/ECX = "Abc"
-474     68/push  _test-slice-data-3/imm32/end
-475     68/push  _test-slice-data-0/imm32/start
-476     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-477     # write-slice(_test-buffered-file, slice)
-478     # . . push args
-479     51/push-ECX
-480     68/push  _test-buffered-file/imm32
-481     # . . call
-482     e8/call  write-slice/disp32
-483     # . . discard args
-484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-485     # flush(_test-buffered-file)
-486     # . . push args
-487     68/push  _test-buffered-file/imm32
-488     # . . call
-489     e8/call  flush/disp32
-490     # . . discard args
-491     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-492     # check-ints-equal(*_test-stream->data, "Abc", msg)
-493     # . . push args
-494     68/push  "F - test-write-slice"/imm32
-495     68/push  0x636241/imm32
-496     # . . push *_test-stream->data
-497     b8/copy-to-EAX  _test-stream/imm32
-498     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-499     # . . call
-500     e8/call  check-ints-equal/disp32
-501     # . . discard args
-502     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-503     # . epilog
-504     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-505     5d/pop-to-EBP
-506     c3/return
-507 
-508 == data
-509 
-510 _test-slice-data-0:
-511     41/A
-512 _test-slice-data-1:
-513     62/b
-514 _test-slice-data-2:
-515     63/c
-516 _test-slice-data-3:
-517     64/d
-518 _test-slice-data-4:
-519 
-520 # . _. vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/071next-token.subx.html b/html/subx/071next-token.subx.html deleted file mode 100644 index ddc0e0e5..00000000 --- a/html/subx/071next-token.subx.html +++ /dev/null @@ -1,914 +0,0 @@ - - - - -Mu - subx/071next-token.subx - - - - - - - - - - -https://github.com/akkartik/mu/blob/master/subx/071next-token.subx -
-  1 == code
-  2 #   instruction                     effective address                                                   register    displacement    immediate
-  3 # . op          subop               mod             rm32          base        index         scale       r32
-  4 # . 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
-  5 
-  6 # main:
-  7 #? e8/call test-next-token-from-slice/disp32
-  8     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
-  9     # syscall(exit, Num-test-failures)
- 10     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 11     b8/copy-to-EAX  1/imm32/exit
- 12     cd/syscall  0x80/imm8
- 13 
- 14 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
- 15 # on eof return an empty interval
- 16 next-token:  # in : (address stream), delimiter : byte, out : (address slice)
- 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     50/push-EAX
- 22     51/push-ECX
- 23     56/push-ESI
- 24     57/push-EDI
- 25     # ESI = in
- 26     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
- 27     # EDI = out
- 28     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x10/disp8      .                 # copy *(EBP+16) to EDI
- 29     # skip-chars-matching(in, delimiter)
- 30     # . . push args
- 31     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 32     56/push-ESI
- 33     # . . call
- 34     e8/call  skip-chars-matching/disp32
- 35     # . . discard args
- 36     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 37     # out->start = &in->data[in->read]
- 38     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
- 39     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
- 40     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
- 41     # skip-chars-not-matching(in, delimiter)
- 42     # . . push args
- 43     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 44     56/push-ESI
- 45     # . . call
- 46     e8/call  skip-chars-not-matching/disp32
- 47     # . . discard args
- 48     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 49     # out->end = &in->data[in->read]
- 50     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
- 51     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
- 52     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
- 53     # . restore registers
- 54     5f/pop-to-EDI
- 55     5e/pop-to-ESI
- 56     59/pop-to-ECX
- 57     58/pop-to-EAX
- 58     # . epilog
- 59     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 60     5d/pop-to-EBP
- 61     c3/return
- 62 
- 63 test-next-token:
- 64     # . prolog
- 65     55/push-EBP
- 66     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 67     # setup
- 68     # . clear-stream(_test-stream)
- 69     # . . push args
- 70     68/push  _test-stream/imm32
- 71     # . . call
- 72     e8/call  clear-stream/disp32
- 73     # . . discard args
- 74     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 75     # var slice/ECX = {0, 0}
- 76     68/push  0/imm32/end
- 77     68/push  0/imm32/start
- 78     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 79     # write(_test-stream, "  ab")
- 80     # . . push args
- 81     68/push  "  ab"/imm32
- 82     68/push  _test-stream/imm32
- 83     # . . call
- 84     e8/call  write/disp32
- 85     # . . discard args
- 86     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 87     # next-token(_test-stream, 0x20/space, slice)
- 88     # . . push args
- 89     51/push-ECX
- 90     68/push  0x20/imm32
- 91     68/push  _test-stream/imm32
- 92     # . . call
- 93     e8/call  next-token/disp32
- 94     # . . discard args
- 95     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 96     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
- 97     # . check-ints-equal(slice->start - _test-stream, 14, msg)
- 98     # . . push args
- 99     68/push  "F - test-next-token: start"/imm32
-100     68/push  0xe/imm32
-101     # . . push slice->start - _test-stream
-102     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-103     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-104     50/push-EAX
-105     # . . call
-106     e8/call  check-ints-equal/disp32
-107     # . . discard args
-108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-109     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
-110     # . check-ints-equal(slice->end - _test-stream, 16, msg)
-111     # . . push args
-112     68/push  "F - test-next-token: end"/imm32
-113     68/push  0x10/imm32
-114     # . . push slice->end - _test-stream
-115     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-116     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-117     50/push-EAX
-118     # . . call
-119     e8/call  check-ints-equal/disp32
-120     # . . discard args
-121     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-122     # . epilog
-123     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-124     5d/pop-to-EBP
-125     c3/return
-126 
-127 test-next-token-eof:
-128     # . prolog
-129     55/push-EBP
-130     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-131     # setup
-132     # . clear-stream(_test-stream)
-133     # . . push args
-134     68/push  _test-stream/imm32
-135     # . . call
-136     e8/call  clear-stream/disp32
-137     # . . discard args
-138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-139     # var slice/ECX = {0, 0}
-140     68/push  0/imm32/end
-141     68/push  0/imm32/start
-142     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-143     # write nothing to _test-stream
-144     # next-token(_test-stream, 0x20/space, slice)
-145     # . . push args
-146     51/push-ECX
-147     68/push  0x20/imm32
-148     68/push  _test-stream/imm32
-149     # . . call
-150     e8/call  next-token/disp32
-151     # . . discard args
-152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-153     # check-ints-equal(slice->end, slice->start, msg)
-154     # . . push args
-155     68/push  "F - test-next-token-eof"/imm32
-156     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
-157     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
-158     # . . call
-159     e8/call  check-ints-equal/disp32
-160     # . . discard args
-161     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-162     # . epilog
-163     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-164     5d/pop-to-EBP
-165     c3/return
-166 
-167 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
-168 # on eof return an empty interval
-169 next-token-from-slice:  # start : (address byte), end : (address byte), delimiter : byte, out : (address slice) -> <void>
-170     # . prolog
-171     55/push-EBP
-172     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-173     # . save registers
-174     50/push-EAX
-175     51/push-ECX
-176     52/push-EDX
-177     57/push-EDI
-178     # ECX = end
-179     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
-180     # EDX = delimiter
-181     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
-182     # EDI = out
-183     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x14/disp8      .                 # copy *(EBP+20) to EDI
-184     # EAX = skip-chars-matching-in-slice(start, end, delimiter)
-185     # . . push args
-186     52/push-EDX
-187     51/push-ECX
-188     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-189     # . . call
-190     e8/call  skip-chars-matching-in-slice/disp32
-191     # . . discard args
-192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-193     # out->start = EAX
-194     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
-195     # EAX = skip-chars-not-matching-in-slice(EAX, end, delimiter)
-196     # . . push args
-197     52/push-EDX
-198     51/push-ECX
-199     50/push-EAX
-200     # . . call
-201     e8/call  skip-chars-not-matching-in-slice/disp32
-202     # . . discard args
-203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-204     # out->end = EAX
-205     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-206     # . restore registers
-207     5f/pop-to-EDI
-208     5a/pop-to-EDX
-209     59/pop-to-ECX
-210     58/pop-to-EAX
-211     # . epilog
-212     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-213     5d/pop-to-EBP
-214     c3/return
-215 
-216 test-next-token-from-slice:
-217     # . prolog
-218     55/push-EBP
-219     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-220     # (EAX..ECX) = "  ab"
-221     b8/copy-to-EAX  "  ab"/imm32
-222     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-223     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
-224     05/add-to-EAX  4/imm32
-225     # var out/EDI : (address slice) = {0, 0}
-226     68/push  0/imm32/end
-227     68/push  0/imm32/start
-228     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
-229     # next-token-from-slice(EAX, ECX, 0x20/space, out)
-230     # . . push args
-231     57/push-EDI
-232     68/push  0x20/imm32
-233     51/push-ECX
-234     50/push-EAX
-235     # . . call
-236     e8/call  next-token-from-slice/disp32
-237     # . . discard args
-238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-239     # out->start should be at the 'a'
-240     # . check-ints-equal(out->start - in->start, 2, msg)
-241     # . . push args
-242     68/push  "F - test-next-token-from-slice: start"/imm32
-243     68/push  2/imm32
-244     # . . push out->start - in->start
-245     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
-246     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
-247     51/push-ECX
-248     # . . call
-249     e8/call  check-ints-equal/disp32
-250     # . . discard args
-251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-252     # out->end should be after the 'b'
-253     # check-ints-equal(out->end - in->start, 4, msg)
-254     # . . push args
-255     68/push  "F - test-next-token-from-slice: end"/imm32
-256     68/push  4/imm32
-257     # . . push out->end - in->start
-258     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
-259     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
-260     51/push-ECX
-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-next-token-from-slice-eof:
-271     # . prolog
-272     55/push-EBP
-273     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-274     # var out/EDI : (address slice) = {0, 0}
-275     68/push  0/imm32/end
-276     68/push  0/imm32/start
-277     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
-278     # next-token-from-slice(0, 0, 0x20/space, out)
-279     # . . push args
-280     57/push-EDI
-281     68/push  0x20/imm32
-282     68/push  0/imm32
-283     68/push  0/imm32
-284     # . . call
-285     e8/call  next-token-from-slice/disp32
-286     # . . discard args
-287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-288     # out should be empty
-289     # . check-ints-equal(out->end - out->start, 0, msg)
-290     # . . push args
-291     68/push  "F - test-next-token-from-slice-eof"/imm32
-292     68/push  0/imm32
-293     # . . push out->start - in->start
-294     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
-295     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
-296     51/push-ECX
-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     # . epilog
-302     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-303     5d/pop-to-EBP
-304     c3/return
-305 
-306 test-next-token-from-slice-nothing:
-307     # . prolog
-308     55/push-EBP
-309     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-310     # (EAX..ECX) = "    "
-311     b8/copy-to-EAX  "    "/imm32
-312     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-313     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
-314     05/add-to-EAX  4/imm32
-315     # var out/EDI : (address slice) = {0, 0}
-316     68/push  0/imm32/end
-317     68/push  0/imm32/start
-318     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
-319     # next-token-from-slice(in, 0x20/space, out)
-320     # . . push args
-321     57/push-EDI
-322     68/push  0x20/imm32
-323     51/push-ECX
-324     50/push-EAX
-325     # . . call
-326     e8/call  next-token-from-slice/disp32
-327     # . . discard args
-328     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-329     # out should be empty
-330     # . check-ints-equal(out->end - out->start, 0, msg)
-331     # . . push args
-332     68/push  "F - test-next-token-from-slice-eof"/imm32
-333     68/push  0/imm32
-334     # . . push out->start - in->start
-335     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
-336     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
-337     51/push-ECX
-338     # . . call
-339     e8/call  check-ints-equal/disp32
-340     # . . discard args
-341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-342     # . epilog
-343     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-344     5d/pop-to-EBP
-345     c3/return
-346 
-347 skip-chars-matching:  # in : (address stream), delimiter : byte
-348     # . prolog
-349     55/push-EBP
-350     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-351     # . save registers
-352     50/push-EAX
-353     51/push-ECX
-354     52/push-EDX
-355     56/push-ESI
-356     # ESI = in
-357     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-358     # ECX = in->read
-359     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-360     # EBX = in->write
-361     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
-362     # EDX = delimiter
-363     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
-364 $skip-chars-matching:loop:
-365     # if (in->read >= in->write) break
-366     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
-367     7d/jump-if-greater-or-equal  $skip-chars-matching:end/disp8
-368     # EAX = in->data[in->read]
-369     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-370     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
-371     # if (EAX != delimiter) break
-372     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
-373     75/jump-if-not-equal  $skip-chars-matching:end/disp8
-374     # ++in->read
-375     41/inc-ECX
-376     eb/jump  $skip-chars-matching:loop/disp8
-377 $skip-chars-matching:end:
-378     # persist in->read
-379     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
-380     # . restore registers
-381     5e/pop-to-ESI
-382     5a/pop-to-EDX
-383     59/pop-to-ECX
-384     58/pop-to-EAX
-385     # . epilog
-386     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-387     5d/pop-to-EBP
-388     c3/return
-389 
-390 test-skip-chars-matching:
-391     # setup
-392     # . clear-stream(_test-stream)
-393     # . . push args
-394     68/push  _test-stream/imm32
-395     # . . call
-396     e8/call  clear-stream/disp32
-397     # . . discard args
-398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-399     # write(_test-stream, "  ab")
-400     # . . push args
-401     68/push  "  ab"/imm32
-402     68/push  _test-stream/imm32
-403     # . . call
-404     e8/call  write/disp32
-405     # . . discard args
-406     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-407     # skip-chars-matching(_test-stream, 0x20/space)
-408     # . . push args
-409     68/push  0x20/imm32
-410     68/push  _test-stream/imm32
-411     # . . call
-412     e8/call  skip-chars-matching/disp32
-413     # . . discard args
-414     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-415     # check-ints-equal(_test-stream->read, 2, msg)
-416     # . . push args
-417     68/push  "F - test-skip-chars-matching"/imm32
-418     68/push  2/imm32
-419     # . . push *_test-stream->read
-420     b8/copy-to-EAX  _test-stream/imm32
-421     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-422     # . . call
-423     e8/call  check-ints-equal/disp32
-424     # . . discard args
-425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-426     # end
-427     c3/return
-428 
-429 test-skip-chars-matching-none:
-430     # setup
-431     # . clear-stream(_test-stream)
-432     # . . push args
-433     68/push  _test-stream/imm32
-434     # . . call
-435     e8/call  clear-stream/disp32
-436     # . . discard args
-437     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-438     # write(_test-stream, "ab")
-439     # . . push args
-440     68/push  "ab"/imm32
-441     68/push  _test-stream/imm32
-442     # . . call
-443     e8/call  write/disp32
-444     # . . discard args
-445     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-446     # skip-chars-matching(_test-stream, 0x20/space)
-447     # . . push args
-448     68/push  0x20/imm32
-449     68/push  _test-stream/imm32
-450     # . . call
-451     e8/call  skip-chars-matching/disp32
-452     # . . discard args
-453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-454     # check-ints-equal(_test-stream->read, 0, msg)
-455     # . . push args
-456     68/push  "F - test-skip-chars-matching-none"/imm32
-457     68/push  0/imm32
-458     # . . push *_test-stream->read
-459     b8/copy-to-EAX  _test-stream/imm32
-460     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-461     # . . call
-462     e8/call  check-ints-equal/disp32
-463     # . . discard args
-464     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-465     # end
-466     c3/return
-467 
-468 # minor fork of 'skip-chars-matching'
-469 skip-chars-not-matching:  # in : (address stream), delimiter : byte
-470     # . prolog
-471     55/push-EBP
-472     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-473     # . save registers
-474     50/push-EAX
-475     51/push-ECX
-476     52/push-EDX
-477     56/push-ESI
-478     # ESI = in
-479     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-480     # ECX = in->read
-481     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-482     # EBX = in->write
-483     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
-484     # EDX = delimiter
-485     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
-486 $skip-chars-not-matching:loop:
-487     # if (in->read >= in->write) break
-488     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
-489     7d/jump-if-greater-or-equal  $skip-chars-not-matching:end/disp8
-490     # EAX = in->data[in->read]
-491     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-492     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
-493     # if (EAX == delimiter) break
-494     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
-495     74/jump-if-equal  $skip-chars-not-matching:end/disp8
-496     # ++in->read
-497     41/inc-ECX
-498     eb/jump  $skip-chars-not-matching:loop/disp8
-499 $skip-chars-not-matching:end:
-500     # persist in->read
-501     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
-502     # . restore registers
-503     5e/pop-to-ESI
-504     5a/pop-to-EDX
-505     59/pop-to-ECX
-506     58/pop-to-EAX
-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-skip-chars-not-matching:
-513     # setup
-514     # . clear-stream(_test-stream)
-515     # . . push args
-516     68/push  _test-stream/imm32
-517     # . . call
-518     e8/call  clear-stream/disp32
-519     # . . discard args
-520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-521     # write(_test-stream, "ab ")
-522     # . . push args
-523     68/push  "ab "/imm32
-524     68/push  _test-stream/imm32
-525     # . . call
-526     e8/call  write/disp32
-527     # . . discard args
-528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-529     # skip-chars-not-matching(_test-stream, 0x20/space)
-530     # . . push args
-531     68/push  0x20/imm32
-532     68/push  _test-stream/imm32
-533     # . . call
-534     e8/call  skip-chars-not-matching/disp32
-535     # . . discard args
-536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-537     # check-ints-equal(_test-stream->read, 2, msg)
-538     # . . push args
-539     68/push  "F - test-skip-chars-not-matching"/imm32
-540     68/push  2/imm32
-541     # . . push *_test-stream->read
-542     b8/copy-to-EAX  _test-stream/imm32
-543     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-544     # . . call
-545     e8/call  check-ints-equal/disp32
-546     # . . discard args
-547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-548     # end
-549     c3/return
-550 
-551 test-skip-chars-not-matching-none:
-552     # setup
-553     # . clear-stream(_test-stream)
-554     # . . push args
-555     68/push  _test-stream/imm32
-556     # . . call
-557     e8/call  clear-stream/disp32
-558     # . . discard args
-559     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-560     # write(_test-stream, " ab")
-561     # . . push args
-562     68/push  " ab"/imm32
-563     68/push  _test-stream/imm32
-564     # . . call
-565     e8/call  write/disp32
-566     # . . discard args
-567     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-568     # skip-chars-not-matching(_test-stream, 0x20/space)
-569     # . . push args
-570     68/push  0x20/imm32
-571     68/push  _test-stream/imm32
-572     # . . call
-573     e8/call  skip-chars-not-matching/disp32
-574     # . . discard args
-575     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-576     # check-ints-equal(_test-stream->read, 0, msg)
-577     # . . push args
-578     68/push  "F - test-skip-chars-not-matching-none"/imm32
-579     68/push  0/imm32
-580     # . . push *_test-stream->read
-581     b8/copy-to-EAX  _test-stream/imm32
-582     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-583     # . . call
-584     e8/call  check-ints-equal/disp32
-585     # . . discard args
-586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-587     # end
-588     c3/return
-589 
-590 test-skip-chars-not-matching-all:
-591     # setup
-592     # . clear-stream(_test-stream)
-593     # . . push args
-594     68/push  _test-stream/imm32
-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     # write(_test-stream, "ab")
-600     # . . push args
-601     68/push  "ab"/imm32
-602     68/push  _test-stream/imm32
-603     # . . call
-604     e8/call  write/disp32
-605     # . . discard args
-606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-607     # skip-chars-not-matching(_test-stream, 0x20/space)
-608     # . . push args
-609     68/push  0x20/imm32
-610     68/push  _test-stream/imm32
-611     # . . call
-612     e8/call  skip-chars-not-matching/disp32
-613     # . . discard args
-614     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-615     # check-ints-equal(_test-stream->read, 2, msg)
-616     # . . push args
-617     68/push  "F - test-skip-chars-not-matching-all"/imm32
-618     68/push  2/imm32
-619     # . . push *_test-stream->read
-620     b8/copy-to-EAX  _test-stream/imm32
-621     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-622     # . . call
-623     e8/call  check-ints-equal/disp32
-624     # . . discard args
-625     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-626     # end
-627     c3/return
-628 
-629 skip-chars-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
-630     # . prolog
-631     55/push-EBP
-632     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-633     # . save registers
-634     51/push-ECX
-635     52/push-EDX
-636     53/push-EBX
-637     # EAX = curr
-638     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
-639     # ECX = end
-640     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
-641     # EDX = delimiter
-642     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
-643     # EBX = 0
-644     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-645 $skip-chars-matching-in-slice:loop:
-646     # if (curr >= end) break
-647     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
-648     7d/jump-if-greater-or-equal  $skip-chars-matching-in-slice:end/disp8
-649     # if (*curr != delimiter) break
-650     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
-651     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
-652     75/jump-if-not-equal  $skip-chars-matching-in-slice:end/disp8
-653     # ++in->read
-654     40/inc-EAX
-655     eb/jump  $skip-chars-matching-in-slice:loop/disp8
-656 $skip-chars-matching-in-slice:end:
-657     # . restore registers
-658     5b/pop-to-EBX
-659     5a/pop-to-EDX
-660     59/pop-to-ECX
-661     # . epilog
-662     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-663     5d/pop-to-EBP
-664     c3/return
-665 
-666 test-skip-chars-matching-in-slice:
-667     # (EAX..ECX) = "  ab"
-668     b8/copy-to-EAX  "  ab"/imm32
-669     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-670     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
-671     05/add-to-EAX  4/imm32
-672     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
-673     # . . push args
-674     68/push  0x20/imm32
-675     51/push-ECX
-676     50/push-EAX
-677     # . . call
-678     e8/call  skip-chars-matching-in-slice/disp32
-679     # . . discard args
-680     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-681     # check-ints-equal(ECX-EAX, 2, msg)
-682     # . . push args
-683     68/push  "F - test-skip-chars-matching-in-slice"/imm32
-684     68/push  2/imm32
-685     # . . push ECX-EAX
-686     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-687     51/push-ECX
-688     # . . call
-689     e8/call  check-ints-equal/disp32
-690     # . . discard args
-691     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-692     # end
-693     c3/return
-694 
-695 test-skip-chars-matching-in-slice-none:
-696     # (EAX..ECX) = "ab"
-697     b8/copy-to-EAX  "ab"/imm32
-698     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-699     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
-700     05/add-to-EAX  4/imm32
-701     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
-702     # . . push args
-703     68/push  0x20/imm32
-704     51/push-ECX
-705     50/push-EAX
-706     # . . call
-707     e8/call  skip-chars-matching-in-slice/disp32
-708     # . . discard args
-709     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-710     # check-ints-equal(ECX-EAX, 2, msg)
-711     # . . push args
-712     68/push  "F - test-skip-chars-matching-in-slice-none"/imm32
-713     68/push  2/imm32
-714     # . . push ECX-EAX
-715     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-716     51/push-ECX
-717     # . . call
-718     e8/call  check-ints-equal/disp32
-719     # . . discard args
-720     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-721     # end
-722     c3/return
-723 
-724 # minor fork of 'skip-chars-matching-in-slice'
-725 skip-chars-not-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
-726     # . prolog
-727     55/push-EBP
-728     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-729     # . save registers
-730     51/push-ECX
-731     52/push-EDX
-732     53/push-EBX
-733     # EAX = curr
-734     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
-735     # ECX = end
-736     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
-737     # EDX = delimiter
-738     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
-739     # EBX = 0
-740     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-741 $skip-chars-not-matching-in-slice:loop:
-742     # if (curr >= end) break
-743     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
-744     7d/jump-if-greater-or-equal  $skip-chars-not-matching-in-slice:end/disp8
-745     # if (*curr == delimiter) break
-746     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
-747     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
-748     74/jump-if-equal  $skip-chars-not-matching-in-slice:end/disp8
-749     # ++in->read
-750     40/inc-EAX
-751     eb/jump  $skip-chars-not-matching-in-slice:loop/disp8
-752 $skip-chars-not-matching-in-slice:end:
-753     # . restore registers
-754     5b/pop-to-EBX
-755     5a/pop-to-EDX
-756     59/pop-to-ECX
-757     # . epilog
-758     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-759     5d/pop-to-EBP
-760     c3/return
-761 
-762 test-skip-chars-not-matching-in-slice:
-763     # (EAX..ECX) = "ab "
-764     b8/copy-to-EAX  "ab "/imm32
-765     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-766     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
-767     05/add-to-EAX  4/imm32
-768     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
-769     # . . push args
-770     68/push  0x20/imm32
-771     51/push-ECX
-772     50/push-EAX
-773     # . . call
-774     e8/call  skip-chars-not-matching-in-slice/disp32
-775     # . . discard args
-776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-777     # check-ints-equal(ECX-EAX, 1, msg)
-778     # . . push args
-779     68/push  "F - test-skip-chars-not-matching-in-slice"/imm32
-780     68/push  1/imm32
-781     # . . push ECX-EAX
-782     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-783     51/push-ECX
-784     # . . call
-785     e8/call  check-ints-equal/disp32
-786     # . . discard args
-787     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-788     # end
-789     c3/return
-790 
-791 test-skip-chars-not-matching-in-slice-none:
-792     # (EAX..ECX) = " ab"
-793     b8/copy-to-EAX  " ab"/imm32
-794     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-795     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
-796     05/add-to-EAX  4/imm32
-797     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
-798     # . . push args
-799     68/push  0x20/imm32
-800     51/push-ECX
-801     50/push-EAX
-802     # . . call
-803     e8/call  skip-chars-not-matching-in-slice/disp32
-804     # . . discard args
-805     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-806     # check-ints-equal(ECX-EAX, 3, msg)
-807     # . . push args
-808     68/push  "F - test-skip-chars-not-matching-in-slice-none"/imm32
-809     68/push  3/imm32
-810     # . . push ECX-EAX
-811     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-812     51/push-ECX
-813     # . . call
-814     e8/call  check-ints-equal/disp32
-815     # . . discard args
-816     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-817     # end
-818     c3/return
-819 
-820 test-skip-chars-not-matching-in-slice-all:
-821     # (EAX..ECX) = "ab"
-822     b8/copy-to-EAX  "ab"/imm32
-823     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-824     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
-825     05/add-to-EAX  4/imm32
-826     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
-827     # . . push args
-828     68/push  0x20/imm32
-829     51/push-ECX
-830     50/push-EAX
-831     # . . call
-832     e8/call  skip-chars-not-matching-in-slice/disp32
-833     # . . discard args
-834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-835     # check-ints-equal(ECX-EAX, 0, msg)
-836     # . . push args
-837     68/push  "F - test-skip-chars-not-matching-in-slice-all"/imm32
-838     68/push  0/imm32
-839     # . . push ECX-EAX
-840     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-841     51/push-ECX
-842     # . . call
-843     e8/call  check-ints-equal/disp32
-844     # . . discard args
-845     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-846     # end
-847     c3/return
-848 
-849 # . . vim:nowrap:textwidth=0
-
- - - diff --git a/html/subx/071read-line.subx.html b/html/subx/071read-line.subx.html new file mode 100644 index 00000000..cae91e35 --- /dev/null +++ b/html/subx/071read-line.subx.html @@ -0,0 +1,373 @@ + + + + +Mu - subx/071read-line.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/071read-line.subx +
+  1 == code
+  2 #   instruction                     effective address                                                   register    displacement    immediate
+  3 # . op          subop               mod             rm32          base        index         scale       r32
+  4 # . 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
+  5 
+  6 # main:
+  7     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+  8     # syscall(exit, Num-test-failures)
+  9     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 10     b8/copy-to-EAX  1/imm32/exit
+ 11     cd/syscall  0x80/imm8
+ 12 
+ 13 # read bytes from 'f' until (and including) a newline and store them into 's'
+ 14 # return true if no data found, false otherwise
+ 15 # just abort if 's' is too small
+ 16 read-line:  # f : (address buffered-file), s : (address stream byte) -> eof?/EAX
+ 17     # pseudocode:
+ 18     #   loop:
+ 19     #     if (s->write >= s->length) abort
+ 20     #     if (f->read >= f->write) populate stream from file
+ 21     #     if (f->write == 0) break
+ 22     #     AL = f->data[f->read]
+ 23     #     s->data[s->write] = AL
+ 24     #     ++f->read
+ 25     #     ++s->write
+ 26     #     if AL == '\n' break
+ 27     # . prolog
+ 28     55/push-EBP
+ 29     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 30     # . save registers
+ 31     51/push-ECX
+ 32     52/push-EDX
+ 33     56/push-ESI
+ 34     57/push-EDI
+ 35     # ESI = f
+ 36     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 37     # ECX = f->read
+ 38     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(ESI+8) to ECX
+ 39     # EDI = s
+ 40     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+ 41     # EDX = s->write
+ 42     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy *EDI to EDX
+ 43 $read-line:loop:
+ 44     # if (s->write >= s->length) abort
+ 45     3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # compare EDX with *(EDI+8)
+ 46     7d/jump-if-greater-or-equal  $read-line:abort/disp8
+ 47     # if (f->read >= f->write) populate stream from file
+ 48     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # compare ECX with *(ESI+4)
+ 49     7c/jump-if-lesser  $read-line:from-stream/disp8
+ 50     # . clear-stream(stream = f+4)
+ 51     # . . push args
+ 52     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy ESI+4 to EAX
+ 53     50/push-EAX
+ 54     # . . call
+ 55     e8/call  clear-stream/disp32
+ 56     # . . discard args
+ 57     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 58     # . f->read must now be 0; update its cache at ECX
+ 59     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+ 60     # . EAX = read(f->fd, stream = f+4)
+ 61     # . . push args
+ 62     50/push-EAX
+ 63     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+ 64     # . . call
+ 65     e8/call  read/disp32
+ 66     # . . discard args
+ 67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 68     # if f->write == 0 return true
+ 69     # . if EAX == 0 return true
+ 70     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
+ 71     75/jump-if-not-equal  $read-line:from-stream/disp8
+ 72     b8/copy-to-EAX  0xffffffff/imm32
+ 73     eb/jump  $read-line:end/disp8
+ 74 $read-line:from-stream:
+ 75     # AL = f->data[f->read]
+ 76     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 77     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0x10/disp8      .                 # copy byte at *(ESI+ECX+16) to AL
+ 78     # s->data[s->write] = AL
+ 79     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/EDI  2/index/EDX   .           0/r32/AL    0xc/disp8       .                 # copy AL to *(EDI+EDX+12)
+ 80     # ++f->read
+ 81     41/increment-ECX
+ 82     # ++s->write
+ 83     42/increment-EDX
+ 84     # if AL == '\n' return false
+ 85     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32         # compare EAX
+ 86     75/jump-if-not-equal  $read-line:loop/disp8
+ 87     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 88 $read-line:end:
+ 89     # save f->read
+ 90     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # copy ECX to *(ESI+8)
+ 91     # save s->write
+ 92     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy EDX to *EDI
+ 93     # . restore registers
+ 94     5f/pop-to-EDI
+ 95     5e/pop-to-ESI
+ 96     5a/pop-to-EDX
+ 97     59/pop-to-ECX
+ 98     # . epilog
+ 99     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+100     5d/pop-to-EBP
+101     c3/return
+102 
+103 $read-line:abort:
+104     # . _write(2/stderr, error)
+105     # . . push args
+106     68/push  "read-line: line too long"/imm32
+107     68/push  2/imm32/stderr
+108     # . . call
+109     e8/call  _write/disp32
+110     # . . discard args
+111     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+112     # . syscall(exit, 1)
+113     bb/copy-to-EBX  1/imm32
+114     b8/copy-to-EAX  1/imm32/exit
+115     cd/syscall  0x80/imm8
+116     # never gets here
+117 
+118 test-read-line:
+119     # - check that read-line stops at a newline
+120     # setup
+121     # . clear-stream(_test-stream)
+122     # . . push args
+123     68/push  _test-stream/imm32
+124     # . . call
+125     e8/call  clear-stream/disp32
+126     # . . discard args
+127     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+128     # . clear-stream(_test-buffered-file+4)
+129     # . . push args
+130     b8/copy-to-EAX  _test-buffered-file/imm32
+131     05/add-to-EAX  4/imm32
+132     50/push-EAX
+133     # . . call
+134     e8/call  clear-stream/disp32
+135     # . . discard args
+136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+137     # . clear-stream(_test-stream-buffer)
+138     # . . push args
+139     68/push  _test-stream-buffer/imm32
+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     # write(_test-stream, "ab\ncd")
+145     # . write(_test-stream, "ab")
+146     # . . push args
+147     68/push  "ab"/imm32
+148     68/push  _test-stream/imm32
+149     # . . call
+150     e8/call  write/disp32
+151     # . . discard args
+152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+153     # . write(_test-stream, "\n")
+154     # . . push args
+155     68/push Newline/imm32
+156     68/push  _test-stream/imm32
+157     # . . call
+158     e8/call  write/disp32
+159     # . . discard args
+160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+161     # . write(_test-stream, "cd")
+162     # . . push args
+163     68/push  "cd"/imm32
+164     68/push  _test-stream/imm32
+165     # . . call
+166     e8/call  write/disp32
+167     # . . discard args
+168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+169     # read a line from _test-stream (buffered by _test-buffered-file) into _test-stream-buffer
+170     # . EAX = read-line(_test-buffered-file, _test-stream-buffer)
+171     # . . push args
+172     68/push  _test-stream-buffer/imm32
+173     68/push  _test-buffered-file/imm32
+174     # . . call
+175     e8/call  read-line/disp32
+176     # . . discard args
+177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+178     # check-ints-equal(EAX, 0/not-at-eof, msg)
+179     # . . push args
+180     68/push  "F - test-read-line: return value"/imm32
+181     68/push  0/imm32/not-at-eof
+182     50/push-EAX
+183     # . . call
+184     e8/call  check-ints-equal/disp32
+185     # . . discard args
+186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+187     # check-next-stream-line-equal(_test-stream-buffer, "ab", msg)
+188     # . . push args
+189     68/push  "F - test-read-line"/imm32
+190     68/push  "ab"/imm32
+191     68/push  _test-stream-buffer/imm32
+192     # . . call
+193     e8/call  check-next-stream-line-equal/disp32
+194     # . . discard args
+195     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+196     # end
+197     c3/return
+198 
+199 test-read-line-returns-true-on-eof:
+200     # setup
+201     # . clear-stream(_test-stream)
+202     # . . push args
+203     68/push  _test-stream/imm32
+204     # . . call
+205     e8/call  clear-stream/disp32
+206     # . . discard args
+207     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+208     # . clear-stream(_test-buffered-file+4)
+209     # . . push args
+210     b8/copy-to-EAX  _test-buffered-file/imm32
+211     05/add-to-EAX  4/imm32
+212     50/push-EAX
+213     # . . call
+214     e8/call  clear-stream/disp32
+215     # . . discard args
+216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+217     # . clear-stream(_test-stream-buffer)
+218     # . . push args
+219     68/push  _test-stream-buffer/imm32
+220     # . . call
+221     e8/call  clear-stream/disp32
+222     # . . discard args
+223     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+224     # write nothing
+225     # EAX = read-line(_test-buffered-file, _test-stream-buffer)
+226     # . . push args
+227     68/push  _test-stream-buffer/imm32
+228     68/push  _test-buffered-file/imm32
+229     # . . call
+230     e8/call  read-line/disp32
+231     # . . discard args
+232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+233     # check-ints-equal(EAX, eof, msg)
+234     # . . push args
+235     68/push  "F - test-read-line-returns-true-on-eof"/imm32
+236     68/push  0xffffffff/imm32/not-at-eof
+237     50/push-EAX
+238     # . . call
+239     e8/call  check-ints-equal/disp32
+240     # . . discard args
+241     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+242     # end
+243     c3/return
+244 
+245 test-read-line-reads-final-line-until-eof:
+246     # setup
+247     # . clear-stream(_test-stream)
+248     # . . push args
+249     68/push  _test-stream/imm32
+250     # . . call
+251     e8/call  clear-stream/disp32
+252     # . . discard args
+253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+254     # . clear-stream(_test-buffered-file+4)
+255     # . . push args
+256     b8/copy-to-EAX  _test-buffered-file/imm32
+257     05/add-to-EAX  4/imm32
+258     50/push-EAX
+259     # . . call
+260     e8/call  clear-stream/disp32
+261     # . . discard args
+262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+263     # . clear-stream(_test-stream-buffer)
+264     # . . push args
+265     68/push  _test-stream-buffer/imm32
+266     # . . call
+267     e8/call  clear-stream/disp32
+268     # . . discard args
+269     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+270     # write(_test-stream, "cd")
+271     # . . push args
+272     68/push  "cd"/imm32
+273     68/push  _test-stream/imm32
+274     # . . call
+275     e8/call  write/disp32
+276     # . . discard args
+277     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+278     # read a line from _test-stream (buffered by _test-buffered-file) into _test-stream-buffer
+279     # . EAX = read-line(_test-buffered-file, _test-stream-buffer)
+280     # . . push args
+281     68/push  _test-stream-buffer/imm32
+282     68/push  _test-buffered-file/imm32
+283     # . . call
+284     e8/call  read-line/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, eof, msg)
+288     # . . push args
+289     68/push  "F - test-read-line-reads-final-line-until-eof: return value"/imm32
+290     68/push  0xffffffff/imm32/not-at-eof
+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     # check-stream-equal(_test-stream-buffer, "cd", msg)
+297     # . . push args
+298     68/push  "F - test-read-line-reads-final-line-until-eof"/imm32
+299     68/push  "cd"/imm32
+300     68/push  _test-stream-buffer/imm32
+301     # . . call
+302     e8/call  check-stream-equal/disp32
+303     # . . discard args
+304     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+305     # end
+306     c3/return
+307 
+308 # . . vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/072slice.subx.html b/html/subx/072slice.subx.html new file mode 100644 index 00000000..6d3be6ad --- /dev/null +++ b/html/subx/072slice.subx.html @@ -0,0 +1,584 @@ + + + + +Mu - subx/072slice.subx + + + + + + + + + + +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 # main:
+ 10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+ 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 with 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     # . prolog
+100     55/push-EBP
+101     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+102     # . save registers
+103     51/push-ECX
+104     52/push-EDX
+105     53/push-EBX
+106     56/push-ESI
+107     # ESI = s
+108     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+109     # curr/EDX = s->start
+110     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
+111     # max/ESI = s->end
+112     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
+113     # EBX = p
+114     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
+115     # EAX = s->end - s->start
+116     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy ESI to EAX
+117     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # subtract EDX from EAX
+118     # if (EAX != p->length) return false;
+119     39/compare                      0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # compare *EBX and EAX
+120     75/jump-if-not-equal  $slice-equal?:false/disp8
+121     # skip p->length
+122     81          0/subop/add         3/mod/direct    3/rm32/EBX    .           .             .           .           .               4/imm32           # add to EBX
+123     # EAX = ECX = 0
+124     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+125     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+126 $slice-equal?:loop:
+127     # if (curr >= max) return true
+128     39/compare                      3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # compare EDX and ESI
+129     7d/jump-if-greater-or-equal  $slice-equal?:true/disp8
+130     # AL = *p
+131     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
+132     # CL = *curr
+133     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           1/r32/CL    .               .                 # copy byte at *EDX to CL
+134     # if (EAX != ECX) return false
+135     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX and ECX
+136     75/jump-if-not-equal  $slice-equal?:false/disp8
+137     # ++p
+138     43/increment-EBX
+139     # ++curr
+140     42/increment-EDX
+141     eb/jump $slice-equal?:loop/disp8
+142 $slice-equal?:false:
+143     b8/copy-to-EAX  0/imm32
+144     eb/jump  $slice-equal?:end/disp8
+145 $slice-equal?:true:
+146     b8/copy-to-EAX  1/imm32
+147 $slice-equal?:end:
+148     # . restore registers
+149     5e/pop-to-ESI
+150     5b/pop-to-EBX
+151     5a/pop-to-EDX
+152     59/pop-to-ECX
+153     # . epilog
+154     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+155     5d/pop-to-EBP
+156     c3/return
+157 
+158 test-slice-equal:
+159     # - slice-equal?(slice("Abc"), "Abc") == 1
+160     # . prolog
+161     55/push-EBP
+162     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+163     # var slice/ECX
+164     68/push  _test-slice-data-3/imm32/end
+165     68/push  _test-slice-data-0/imm32/start
+166     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+167     # EAX = slice-equal?(ECX, "Abc")
+168     # . . push args
+169     68/push  "Abc"/imm32
+170     51/push-ECX
+171     # . . call
+172     e8/call  slice-equal?/disp32
+173     # . . discard args
+174     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+175     # check-ints-equal(EAX, 1, msg)
+176     # . . push args
+177     68/push  "F - test-slice-equal"/imm32
+178     68/push  1/imm32
+179     50/push-EAX
+180     # . . call
+181     e8/call  check-ints-equal/disp32
+182     # . . discard args
+183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+184     # . epilog
+185     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+186     5d/pop-to-EBP
+187     c3/return
+188 
+189 test-slice-equal-false:
+190     # - slice-equal?(slice("bcd"), "Abc") == 0
+191     # . prolog
+192     55/push-EBP
+193     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+194     # var slice/ECX
+195     68/push  _test-slice-data-4/imm32/end
+196     68/push  _test-slice-data-1/imm32/start
+197     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+198     # EAX = slice-equal?(ECX, "Abc")
+199     # . . push args
+200     68/push  "Abc"/imm32
+201     51/push-ECX
+202     # . . call
+203     e8/call  slice-equal?/disp32
+204     # . . discard args
+205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+206     # check-ints-equal(EAX, 0, msg)
+207     # . . push args
+208     68/push  "F - test-slice-equal-false"/imm32
+209     68/push  0/imm32
+210     50/push-EAX
+211     # . . call
+212     e8/call  check-ints-equal/disp32
+213     # . . discard args
+214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+215     # . epilog
+216     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+217     5d/pop-to-EBP
+218     c3/return
+219 
+220 test-slice-equal-too-long:
+221     # - slice-equal?(slice("Abcd"), "Abc") == 0
+222     # . prolog
+223     55/push-EBP
+224     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+225     # var slice/ECX
+226     68/push  _test-slice-data-4/imm32/end
+227     68/push  _test-slice-data-0/imm32/start
+228     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+229     # EAX = slice-equal?(ECX, "Abc")
+230     # . . push args
+231     68/push  "Abc"/imm32
+232     51/push-ECX
+233     # . . call
+234     e8/call  slice-equal?/disp32
+235     # . . discard args
+236     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+237     # check-ints-equal(EAX, 0, msg)
+238     # . . push args
+239     68/push  "F - test-slice-equal-too-long"/imm32
+240     68/push  0/imm32
+241     50/push-EAX
+242     # . . call
+243     e8/call  check-ints-equal/disp32
+244     # . . discard args
+245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+246     # . epilog
+247     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+248     5d/pop-to-EBP
+249     c3/return
+250 
+251 test-slice-equal-too-short:
+252     # - slice-equal?(slice("A"), "Abc") == 0
+253     # . prolog
+254     55/push-EBP
+255     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+256     # var slice/ECX
+257     68/push  _test-slice-data-1/imm32/end
+258     68/push  _test-slice-data-0/imm32/start
+259     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+260     # EAX = slice-equal?(ECX, "Abc")
+261     # . . push args
+262     68/push  "Abc"/imm32
+263     51/push-ECX
+264     # . . call
+265     e8/call  slice-equal?/disp32
+266     # . . discard args
+267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+268     # check-ints-equal(EAX, 0, msg)
+269     # . . push args
+270     68/push  "F - test-slice-equal-too-short"/imm32
+271     68/push  0/imm32
+272     50/push-EAX
+273     # . . call
+274     e8/call  check-ints-equal/disp32
+275     # . . discard args
+276     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+277     # . epilog
+278     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+279     5d/pop-to-EBP
+280     c3/return
+281 
+282 test-slice-equal-empty:
+283     # - slice-equal?(slice(""), "Abc") == 0
+284     # . prolog
+285     55/push-EBP
+286     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+287     # var slice/ECX
+288     68/push  _test-slice-data-0/imm32/end
+289     68/push  _test-slice-data-0/imm32/start
+290     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+291     # EAX = slice-equal?(ECX, "Abc")
+292     # . . push args
+293     68/push  "Abc"/imm32
+294     51/push-ECX
+295     # . . call
+296     e8/call  slice-equal?/disp32
+297     # . . discard args
+298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+299     # check-ints-equal(EAX, 0, msg)
+300     # . . push args
+301     68/push  "F - test-slice-equal-empty"/imm32
+302     68/push  0/imm32
+303     50/push-EAX
+304     # . . call
+305     e8/call  check-ints-equal/disp32
+306     # . . discard args
+307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+308     # . epilog
+309     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+310     5d/pop-to-EBP
+311     c3/return
+312 
+313 test-slice-equal-with-empty:
+314     # - slice-equal?(slice("Ab"), "") == 0
+315     # . prolog
+316     55/push-EBP
+317     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+318     # var slice/ECX
+319     68/push  _test-slice-data-2/imm32/end
+320     68/push  _test-slice-data-0/imm32/start
+321     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+322     # EAX = slice-equal?(ECX, "")
+323     # . . push args
+324     68/push  ""/imm32
+325     51/push-ECX
+326     # . . call
+327     e8/call  slice-equal?/disp32
+328     # . . discard args
+329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+330     # check-ints-equal(EAX, 0, msg)
+331     # . . push args
+332     68/push  "F - test-slice-equal-with-empty"/imm32
+333     68/push  0/imm32
+334     50/push-EAX
+335     # . . call
+336     e8/call  check-ints-equal/disp32
+337     # . . discard args
+338     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+339     # . epilog
+340     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+341     5d/pop-to-EBP
+342     c3/return
+343 
+344 test-slice-equal-empty-with-empty:
+345     # - slice-equal?(slice(""), "") == 1
+346     # . prolog
+347     55/push-EBP
+348     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+349     # var slice/ECX
+350     68/push  _test-slice-data-0/imm32/end
+351     68/push  _test-slice-data-0/imm32/start
+352     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+353     # EAX = slice-equal?(ECX, "")
+354     # . . push args
+355     68/push  ""/imm32
+356     51/push-ECX
+357     # . . call
+358     e8/call  slice-equal?/disp32
+359     # . . discard args
+360     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+361     # check-ints-equal(EAX, 1, msg)
+362     # . . push args
+363     68/push  "F - test-slice-equal-empty-with-empty"/imm32
+364     68/push  1/imm32
+365     50/push-EAX
+366     # . . call
+367     e8/call  check-ints-equal/disp32
+368     # . . discard args
+369     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+370     # . epilog
+371     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+372     5d/pop-to-EBP
+373     c3/return
+374 
+375 write-slice:  # out : (address buffered-file), s : (address slice)
+376     # . prolog
+377     55/push-EBP
+378     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+379     # . save registers
+380     50/push-EAX
+381     51/push-ECX
+382     52/push-EDX
+383     53/push-EBX
+384     56/push-ESI
+385     57/push-EDI
+386     # ESI = s
+387     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
+388     # curr/ECX = s->start
+389     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+390     # max/ESI = s->end
+391     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           6/r32/ESI   4/disp8         .                 # copy *(ESI+4) to ESI
+392     # EDI = f
+393     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
+394     # EDX = f->length
+395     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EDI+12) to EDX
+396     # EBX = f->write
+397     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy *(EDI+4) to EBX
+398 $write-slice:loop:
+399     # if (curr >= max) break
+400     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # compare ECX with ESI
+401     7d/jump-if-greater-or-equal  $write-slice:loop-end/disp8
+402     # if (f->write >= f->length) flush and clear f's stream
+403     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX with EDX
+404     7c/jump-if-lesser  $write-slice:to-stream/disp8
+405     # . persist f->write
+406     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
+407     # . flush(f)
+408     # . . push args
+409     57/push-EDI
+410     # . . call
+411     e8/call  flush/disp32
+412     # . . discard args
+413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+414     # . clear-stream(stream = f+4)
+415     # . . push args
+416     8d/copy-address                 1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EDI+4 to EAX
+417     50/push-EAX
+418     # . . call
+419     e8/call  clear-stream/disp32
+420     # . . discard args
+421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+422     # . f->write must now be 0; update its cache at EBX
+423     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+424 $write-slice:to-stream:
+425     # f->data[f->write] = *in
+426     # . AL = *in
+427     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+428     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+429     # . f->data[f->write] = AL
+430     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)
+431     # ++f->write
+432     43/increment-EBX
+433     # ++in
+434     41/increment-ECX
+435     eb/jump  $write-slice:loop/disp8
+436 $write-slice:loop-end:
+437     # persist necessary variables from registers
+438     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EDI+4)
+439 $write-slice:end:
+440     # . restore registers
+441     5f/pop-to-EDI
+442     5e/pop-to-ESI
+443     5b/pop-to-EBX
+444     5a/pop-to-EDX
+445     59/pop-to-ECX
+446     58/pop-to-EAX
+447     # . epilog
+448     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+449     5d/pop-to-EBP
+450     c3/return
+451 
+452 test-write-slice:
+453     # . prolog
+454     55/push-EBP
+455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+456     # setup
+457     # . clear-stream(_test-stream)
+458     # . . push args
+459     68/push  _test-stream/imm32
+460     # . . call
+461     e8/call  clear-stream/disp32
+462     # . . discard args
+463     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+464     # . clear-stream(_test-buffered-file+4)
+465     # . . push args
+466     b8/copy-to-EAX  _test-buffered-file/imm32
+467     05/add-to-EAX  4/imm32
+468     50/push-EAX
+469     # . . call
+470     e8/call  clear-stream/disp32
+471     # . . discard args
+472     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+473     # var slice/ECX = "Abc"
+474     68/push  _test-slice-data-3/imm32/end
+475     68/push  _test-slice-data-0/imm32/start
+476     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+477     # write-slice(_test-buffered-file, slice)
+478     # . . push args
+479     51/push-ECX
+480     68/push  _test-buffered-file/imm32
+481     # . . call
+482     e8/call  write-slice/disp32
+483     # . . discard args
+484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+485     # flush(_test-buffered-file)
+486     # . . push args
+487     68/push  _test-buffered-file/imm32
+488     # . . call
+489     e8/call  flush/disp32
+490     # . . discard args
+491     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+492     # check-stream-equal(_test-stream, "Abc", msg)
+493     # . . push args
+494     68/push  "F - test-write-slice"/imm32
+495     68/push  "Abc"/imm32
+496     68/push  _test-stream/imm32
+497     # . . call
+498     e8/call  check-stream-equal/disp32
+499     # . . discard args
+500     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+501     # . epilog
+502     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+503     5d/pop-to-EBP
+504     c3/return
+505 
+506 == data
+507 
+508 _test-slice-data-0:
+509     41/A
+510 _test-slice-data-1:
+511     62/b
+512 _test-slice-data-2:
+513     63/c
+514 _test-slice-data-3:
+515     64/d
+516 _test-slice-data-4:
+517 
+518 # . _. vim:nowrap:textwidth=0
+
+ + + diff --git a/html/subx/073next-token.subx.html b/html/subx/073next-token.subx.html new file mode 100644 index 00000000..d5f3cdd0 --- /dev/null +++ b/html/subx/073next-token.subx.html @@ -0,0 +1,914 @@ + + + + +Mu - subx/073next-token.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/subx/073next-token.subx +
+  1 == code
+  2 #   instruction                     effective address                                                   register    displacement    immediate
+  3 # . op          subop               mod             rm32          base        index         scale       r32
+  4 # . 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
+  5 
+  6 # main:
+  7 #? e8/call test-next-token-from-slice/disp32
+  8     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+  9     # syscall(exit, Num-test-failures)
+ 10     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 11     b8/copy-to-EAX  1/imm32/exit
+ 12     cd/syscall  0x80/imm8
+ 13 
+ 14 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
+ 15 # on eof return an empty interval
+ 16 next-token:  # in : (address stream), delimiter : byte, out : (address slice)
+ 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     50/push-EAX
+ 22     51/push-ECX
+ 23     56/push-ESI
+ 24     57/push-EDI
+ 25     # ESI = in
+ 26     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+ 27     # EDI = out
+ 28     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x10/disp8      .                 # copy *(EBP+16) to EDI
+ 29     # skip-chars-matching(in, delimiter)
+ 30     # . . push args
+ 31     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 32     56/push-ESI
+ 33     # . . call
+ 34     e8/call  skip-chars-matching/disp32
+ 35     # . . discard args
+ 36     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 37     # out->start = &in->data[in->read]
+ 38     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 39     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
+ 40     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+ 41     # skip-chars-not-matching(in, delimiter)
+ 42     # . . push args
+ 43     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 44     56/push-ESI
+ 45     # . . call
+ 46     e8/call  skip-chars-not-matching/disp32
+ 47     # . . discard args
+ 48     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 49     # out->end = &in->data[in->read]
+ 50     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+ 51     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
+ 52     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+ 53     # . restore registers
+ 54     5f/pop-to-EDI
+ 55     5e/pop-to-ESI
+ 56     59/pop-to-ECX
+ 57     58/pop-to-EAX
+ 58     # . epilog
+ 59     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 60     5d/pop-to-EBP
+ 61     c3/return
+ 62 
+ 63 test-next-token:
+ 64     # . prolog
+ 65     55/push-EBP
+ 66     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 67     # setup
+ 68     # . clear-stream(_test-stream)
+ 69     # . . push args
+ 70     68/push  _test-stream/imm32
+ 71     # . . call
+ 72     e8/call  clear-stream/disp32
+ 73     # . . discard args
+ 74     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 75     # var slice/ECX = {0, 0}
+ 76     68/push  0/imm32/end
+ 77     68/push  0/imm32/start
+ 78     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 79     # write(_test-stream, "  ab")
+ 80     # . . push args
+ 81     68/push  "  ab"/imm32
+ 82     68/push  _test-stream/imm32
+ 83     # . . call
+ 84     e8/call  write/disp32
+ 85     # . . discard args
+ 86     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 87     # next-token(_test-stream, 0x20/space, slice)
+ 88     # . . push args
+ 89     51/push-ECX
+ 90     68/push  0x20/imm32
+ 91     68/push  _test-stream/imm32
+ 92     # . . call
+ 93     e8/call  next-token/disp32
+ 94     # . . discard args
+ 95     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 96     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+ 97     # . check-ints-equal(slice->start - _test-stream, 14, msg)
+ 98     # . . push args
+ 99     68/push  "F - test-next-token: start"/imm32
+100     68/push  0xe/imm32
+101     # . . push slice->start - _test-stream
+102     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+103     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+104     50/push-EAX
+105     # . . call
+106     e8/call  check-ints-equal/disp32
+107     # . . discard args
+108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+109     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
+110     # . check-ints-equal(slice->end - _test-stream, 16, msg)
+111     # . . push args
+112     68/push  "F - test-next-token: end"/imm32
+113     68/push  0x10/imm32
+114     # . . push slice->end - _test-stream
+115     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+116     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+117     50/push-EAX
+118     # . . call
+119     e8/call  check-ints-equal/disp32
+120     # . . discard args
+121     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+122     # . epilog
+123     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+124     5d/pop-to-EBP
+125     c3/return
+126 
+127 test-next-token-eof:
+128     # . prolog
+129     55/push-EBP
+130     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+131     # setup
+132     # . clear-stream(_test-stream)
+133     # . . push args
+134     68/push  _test-stream/imm32
+135     # . . call
+136     e8/call  clear-stream/disp32
+137     # . . discard args
+138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+139     # var slice/ECX = {0, 0}
+140     68/push  0/imm32/end
+141     68/push  0/imm32/start
+142     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+143     # write nothing to _test-stream
+144     # next-token(_test-stream, 0x20/space, slice)
+145     # . . push args
+146     51/push-ECX
+147     68/push  0x20/imm32
+148     68/push  _test-stream/imm32
+149     # . . call
+150     e8/call  next-token/disp32
+151     # . . discard args
+152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+153     # check-ints-equal(slice->end, slice->start, msg)
+154     # . . push args
+155     68/push  "F - test-next-token-eof"/imm32
+156     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+157     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
+158     # . . call
+159     e8/call  check-ints-equal/disp32
+160     # . . discard args
+161     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+162     # . epilog
+163     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+164     5d/pop-to-EBP
+165     c3/return
+166 
+167 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
+168 # on eof return an empty interval
+169 next-token-from-slice:  # start : (address byte), end : (address byte), delimiter : byte, out : (address slice) -> <void>
+170     # . prolog
+171     55/push-EBP
+172     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+173     # . save registers
+174     50/push-EAX
+175     51/push-ECX
+176     52/push-EDX
+177     57/push-EDI
+178     # ECX = end
+179     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
+180     # EDX = delimiter
+181     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
+182     # EDI = out
+183     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x14/disp8      .                 # copy *(EBP+20) to EDI
+184     # EAX = skip-chars-matching-in-slice(start, end, delimiter)
+185     # . . push args
+186     52/push-EDX
+187     51/push-ECX
+188     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+189     # . . call
+190     e8/call  skip-chars-matching-in-slice/disp32
+191     # . . discard args
+192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+193     # out->start = EAX
+194     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+195     # EAX = skip-chars-not-matching-in-slice(EAX, end, delimiter)
+196     # . . push args
+197     52/push-EDX
+198     51/push-ECX
+199     50/push-EAX
+200     # . . call
+201     e8/call  skip-chars-not-matching-in-slice/disp32
+202     # . . discard args
+203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+204     # out->end = EAX
+205     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+206     # . restore registers
+207     5f/pop-to-EDI
+208     5a/pop-to-EDX
+209     59/pop-to-ECX
+210     58/pop-to-EAX
+211     # . epilog
+212     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+213     5d/pop-to-EBP
+214     c3/return
+215 
+216 test-next-token-from-slice:
+217     # . prolog
+218     55/push-EBP
+219     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+220     # (EAX..ECX) = "  ab"
+221     b8/copy-to-EAX  "  ab"/imm32
+222     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+223     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
+224     05/add-to-EAX  4/imm32
+225     # var out/EDI : (address slice) = {0, 0}
+226     68/push  0/imm32/end
+227     68/push  0/imm32/start
+228     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+229     # next-token-from-slice(EAX, ECX, 0x20/space, out)
+230     # . . push args
+231     57/push-EDI
+232     68/push  0x20/imm32
+233     51/push-ECX
+234     50/push-EAX
+235     # . . call
+236     e8/call  next-token-from-slice/disp32
+237     # . . discard args
+238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+239     # out->start should be at the 'a'
+240     # . check-ints-equal(out->start - in->start, 2, msg)
+241     # . . push args
+242     68/push  "F - test-next-token-from-slice: start"/imm32
+243     68/push  2/imm32
+244     # . . push out->start - in->start
+245     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+246     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
+247     51/push-ECX
+248     # . . call
+249     e8/call  check-ints-equal/disp32
+250     # . . discard args
+251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+252     # out->end should be after the 'b'
+253     # check-ints-equal(out->end - in->start, 4, msg)
+254     # . . push args
+255     68/push  "F - test-next-token-from-slice: end"/imm32
+256     68/push  4/imm32
+257     # . . push out->end - in->start
+258     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+259     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
+260     51/push-ECX
+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-next-token-from-slice-eof:
+271     # . prolog
+272     55/push-EBP
+273     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+274     # var out/EDI : (address slice) = {0, 0}
+275     68/push  0/imm32/end
+276     68/push  0/imm32/start
+277     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+278     # next-token-from-slice(0, 0, 0x20/space, out)
+279     # . . push args
+280     57/push-EDI
+281     68/push  0x20/imm32
+282     68/push  0/imm32
+283     68/push  0/imm32
+284     # . . call
+285     e8/call  next-token-from-slice/disp32
+286     # . . discard args
+287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+288     # out should be empty
+289     # . check-ints-equal(out->end - out->start, 0, msg)
+290     # . . push args
+291     68/push  "F - test-next-token-from-slice-eof"/imm32
+292     68/push  0/imm32
+293     # . . push out->start - in->start
+294     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+295     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
+296     51/push-ECX
+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     # . epilog
+302     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+303     5d/pop-to-EBP
+304     c3/return
+305 
+306 test-next-token-from-slice-nothing:
+307     # . prolog
+308     55/push-EBP
+309     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+310     # (EAX..ECX) = "    "
+311     b8/copy-to-EAX  "    "/imm32
+312     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+313     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
+314     05/add-to-EAX  4/imm32
+315     # var out/EDI : (address slice) = {0, 0}
+316     68/push  0/imm32/end
+317     68/push  0/imm32/start
+318     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
+319     # next-token-from-slice(in, 0x20/space, out)
+320     # . . push args
+321     57/push-EDI
+322     68/push  0x20/imm32
+323     51/push-ECX
+324     50/push-EAX
+325     # . . call
+326     e8/call  next-token-from-slice/disp32
+327     # . . discard args
+328     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+329     # out should be empty
+330     # . check-ints-equal(out->end - out->start, 0, msg)
+331     # . . push args
+332     68/push  "F - test-next-token-from-slice-eof"/imm32
+333     68/push  0/imm32
+334     # . . push out->start - in->start
+335     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
+336     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
+337     51/push-ECX
+338     # . . call
+339     e8/call  check-ints-equal/disp32
+340     # . . discard args
+341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+342     # . epilog
+343     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+344     5d/pop-to-EBP
+345     c3/return
+346 
+347 skip-chars-matching:  # in : (address stream), delimiter : byte
+348     # . prolog
+349     55/push-EBP
+350     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+351     # . save registers
+352     50/push-EAX
+353     51/push-ECX
+354     52/push-EDX
+355     56/push-ESI
+356     # ESI = in
+357     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+358     # ECX = in->read
+359     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+360     # EBX = in->write
+361     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
+362     # EDX = delimiter
+363     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+364 $skip-chars-matching:loop:
+365     # if (in->read >= in->write) break
+366     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
+367     7d/jump-if-greater-or-equal  $skip-chars-matching:end/disp8
+368     # EAX = in->data[in->read]
+369     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+370     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
+371     # if (EAX != delimiter) break
+372     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
+373     75/jump-if-not-equal  $skip-chars-matching:end/disp8
+374     # ++in->read
+375     41/inc-ECX
+376     eb/jump  $skip-chars-matching:loop/disp8
+377 $skip-chars-matching:end:
+378     # persist in->read
+379     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
+380     # . restore registers
+381     5e/pop-to-ESI
+382     5a/pop-to-EDX
+383     59/pop-to-ECX
+384     58/pop-to-EAX
+385     # . epilog
+386     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+387     5d/pop-to-EBP
+388     c3/return
+389 
+390 test-skip-chars-matching:
+391     # setup
+392     # . clear-stream(_test-stream)
+393     # . . push args
+394     68/push  _test-stream/imm32
+395     # . . call
+396     e8/call  clear-stream/disp32
+397     # . . discard args
+398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+399     # write(_test-stream, "  ab")
+400     # . . push args
+401     68/push  "  ab"/imm32
+402     68/push  _test-stream/imm32
+403     # . . call
+404     e8/call  write/disp32
+405     # . . discard args
+406     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+407     # skip-chars-matching(_test-stream, 0x20/space)
+408     # . . push args
+409     68/push  0x20/imm32
+410     68/push  _test-stream/imm32
+411     # . . call
+412     e8/call  skip-chars-matching/disp32
+413     # . . discard args
+414     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+415     # check-ints-equal(_test-stream->read, 2, msg)
+416     # . . push args
+417     68/push  "F - test-skip-chars-matching"/imm32
+418     68/push  2/imm32
+419     # . . push *_test-stream->read
+420     b8/copy-to-EAX  _test-stream/imm32
+421     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+422     # . . call
+423     e8/call  check-ints-equal/disp32
+424     # . . discard args
+425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+426     # end
+427     c3/return
+428 
+429 test-skip-chars-matching-none:
+430     # setup
+431     # . clear-stream(_test-stream)
+432     # . . push args
+433     68/push  _test-stream/imm32
+434     # . . call
+435     e8/call  clear-stream/disp32
+436     # . . discard args
+437     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+438     # write(_test-stream, "ab")
+439     # . . push args
+440     68/push  "ab"/imm32
+441     68/push  _test-stream/imm32
+442     # . . call
+443     e8/call  write/disp32
+444     # . . discard args
+445     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+446     # skip-chars-matching(_test-stream, 0x20/space)
+447     # . . push args
+448     68/push  0x20/imm32
+449     68/push  _test-stream/imm32
+450     # . . call
+451     e8/call  skip-chars-matching/disp32
+452     # . . discard args
+453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+454     # check-ints-equal(_test-stream->read, 0, msg)
+455     # . . push args
+456     68/push  "F - test-skip-chars-matching-none"/imm32
+457     68/push  0/imm32
+458     # . . push *_test-stream->read
+459     b8/copy-to-EAX  _test-stream/imm32
+460     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+461     # . . call
+462     e8/call  check-ints-equal/disp32
+463     # . . discard args
+464     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+465     # end
+466     c3/return
+467 
+468 # minor fork of 'skip-chars-matching'
+469 skip-chars-not-matching:  # in : (address stream), delimiter : byte
+470     # . prolog
+471     55/push-EBP
+472     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+473     # . save registers
+474     50/push-EAX
+475     51/push-ECX
+476     52/push-EDX
+477     56/push-ESI
+478     # ESI = in
+479     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+480     # ECX = in->read
+481     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+482     # EBX = in->write
+483     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
+484     # EDX = delimiter
+485     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
+486 $skip-chars-not-matching:loop:
+487     # if (in->read >= in->write) break
+488     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
+489     7d/jump-if-greater-or-equal  $skip-chars-not-matching:end/disp8
+490     # EAX = in->data[in->read]
+491     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+492     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
+493     # if (EAX == delimiter) break
+494     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
+495     74/jump-if-equal  $skip-chars-not-matching:end/disp8
+496     # ++in->read
+497     41/inc-ECX
+498     eb/jump  $skip-chars-not-matching:loop/disp8
+499 $skip-chars-not-matching:end:
+500     # persist in->read
+501     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
+502     # . restore registers
+503     5e/pop-to-ESI
+504     5a/pop-to-EDX
+505     59/pop-to-ECX
+506     58/pop-to-EAX
+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-skip-chars-not-matching:
+513     # setup
+514     # . clear-stream(_test-stream)
+515     # . . push args
+516     68/push  _test-stream/imm32
+517     # . . call
+518     e8/call  clear-stream/disp32
+519     # . . discard args
+520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+521     # write(_test-stream, "ab ")
+522     # . . push args
+523     68/push  "ab "/imm32
+524     68/push  _test-stream/imm32
+525     # . . call
+526     e8/call  write/disp32
+527     # . . discard args
+528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+529     # skip-chars-not-matching(_test-stream, 0x20/space)
+530     # . . push args
+531     68/push  0x20/imm32
+532     68/push  _test-stream/imm32
+533     # . . call
+534     e8/call  skip-chars-not-matching/disp32
+535     # . . discard args
+536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+537     # check-ints-equal(_test-stream->read, 2, msg)
+538     # . . push args
+539     68/push  "F - test-skip-chars-not-matching"/imm32
+540     68/push  2/imm32
+541     # . . push *_test-stream->read
+542     b8/copy-to-EAX  _test-stream/imm32
+543     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+544     # . . call
+545     e8/call  check-ints-equal/disp32
+546     # . . discard args
+547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+548     # end
+549     c3/return
+550 
+551 test-skip-chars-not-matching-none:
+552     # setup
+553     # . clear-stream(_test-stream)
+554     # . . push args
+555     68/push  _test-stream/imm32
+556     # . . call
+557     e8/call  clear-stream/disp32
+558     # . . discard args
+559     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+560     # write(_test-stream, " ab")
+561     # . . push args
+562     68/push  " ab"/imm32
+563     68/push  _test-stream/imm32
+564     # . . call
+565     e8/call  write/disp32
+566     # . . discard args
+567     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+568     # skip-chars-not-matching(_test-stream, 0x20/space)
+569     # . . push args
+570     68/push  0x20/imm32
+571     68/push  _test-stream/imm32
+572     # . . call
+573     e8/call  skip-chars-not-matching/disp32
+574     # . . discard args
+575     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+576     # check-ints-equal(_test-stream->read, 0, msg)
+577     # . . push args
+578     68/push  "F - test-skip-chars-not-matching-none"/imm32
+579     68/push  0/imm32
+580     # . . push *_test-stream->read
+581     b8/copy-to-EAX  _test-stream/imm32
+582     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+583     # . . call
+584     e8/call  check-ints-equal/disp32
+585     # . . discard args
+586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+587     # end
+588     c3/return
+589 
+590 test-skip-chars-not-matching-all:
+591     # setup
+592     # . clear-stream(_test-stream)
+593     # . . push args
+594     68/push  _test-stream/imm32
+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     # write(_test-stream, "ab")
+600     # . . push args
+601     68/push  "ab"/imm32
+602     68/push  _test-stream/imm32
+603     # . . call
+604     e8/call  write/disp32
+605     # . . discard args
+606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+607     # skip-chars-not-matching(_test-stream, 0x20/space)
+608     # . . push args
+609     68/push  0x20/imm32
+610     68/push  _test-stream/imm32
+611     # . . call
+612     e8/call  skip-chars-not-matching/disp32
+613     # . . discard args
+614     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+615     # check-ints-equal(_test-stream->read, 2, msg)
+616     # . . push args
+617     68/push  "F - test-skip-chars-not-matching-all"/imm32
+618     68/push  2/imm32
+619     # . . push *_test-stream->read
+620     b8/copy-to-EAX  _test-stream/imm32
+621     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+622     # . . call
+623     e8/call  check-ints-equal/disp32
+624     # . . discard args
+625     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+626     # end
+627     c3/return
+628 
+629 skip-chars-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
+630     # . prolog
+631     55/push-EBP
+632     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+633     # . save registers
+634     51/push-ECX
+635     52/push-EDX
+636     53/push-EBX
+637     # EAX = curr
+638     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+639     # ECX = end
+640     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
+641     # EDX = delimiter
+642     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
+643     # EBX = 0
+644     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+645 $skip-chars-matching-in-slice:loop:
+646     # if (curr >= end) break
+647     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+648     7d/jump-if-greater-or-equal  $skip-chars-matching-in-slice:end/disp8
+649     # if (*curr != delimiter) break
+650     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
+651     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
+652     75/jump-if-not-equal  $skip-chars-matching-in-slice:end/disp8
+653     # ++in->read
+654     40/inc-EAX
+655     eb/jump  $skip-chars-matching-in-slice:loop/disp8
+656 $skip-chars-matching-in-slice:end:
+657     # . restore registers
+658     5b/pop-to-EBX
+659     5a/pop-to-EDX
+660     59/pop-to-ECX
+661     # . epilog
+662     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+663     5d/pop-to-EBP
+664     c3/return
+665 
+666 test-skip-chars-matching-in-slice:
+667     # (EAX..ECX) = "  ab"
+668     b8/copy-to-EAX  "  ab"/imm32
+669     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+670     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
+671     05/add-to-EAX  4/imm32
+672     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
+673     # . . push args
+674     68/push  0x20/imm32
+675     51/push-ECX
+676     50/push-EAX
+677     # . . call
+678     e8/call  skip-chars-matching-in-slice/disp32
+679     # . . discard args
+680     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+681     # check-ints-equal(ECX-EAX, 2, msg)
+682     # . . push args
+683     68/push  "F - test-skip-chars-matching-in-slice"/imm32
+684     68/push  2/imm32
+685     # . . push ECX-EAX
+686     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+687     51/push-ECX
+688     # . . call
+689     e8/call  check-ints-equal/disp32
+690     # . . discard args
+691     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+692     # end
+693     c3/return
+694 
+695 test-skip-chars-matching-in-slice-none:
+696     # (EAX..ECX) = "ab"
+697     b8/copy-to-EAX  "ab"/imm32
+698     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+699     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
+700     05/add-to-EAX  4/imm32
+701     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
+702     # . . push args
+703     68/push  0x20/imm32
+704     51/push-ECX
+705     50/push-EAX
+706     # . . call
+707     e8/call  skip-chars-matching-in-slice/disp32
+708     # . . discard args
+709     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+710     # check-ints-equal(ECX-EAX, 2, msg)
+711     # . . push args
+712     68/push  "F - test-skip-chars-matching-in-slice-none"/imm32
+713     68/push  2/imm32
+714     # . . push ECX-EAX
+715     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+716     51/push-ECX
+717     # . . call
+718     e8/call  check-ints-equal/disp32
+719     # . . discard args
+720     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+721     # end
+722     c3/return
+723 
+724 # minor fork of 'skip-chars-matching-in-slice'
+725 skip-chars-not-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
+726     # . prolog
+727     55/push-EBP
+728     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+729     # . save registers
+730     51/push-ECX
+731     52/push-EDX
+732     53/push-EBX
+733     # EAX = curr
+734     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+735     # ECX = end
+736     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
+737     # EDX = delimiter
+738     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
+739     # EBX = 0
+740     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+741 $skip-chars-not-matching-in-slice:loop:
+742     # if (curr >= end) break
+743     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
+744     7d/jump-if-greater-or-equal  $skip-chars-not-matching-in-slice:end/disp8
+745     # if (*curr == delimiter) break
+746     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
+747     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
+748     74/jump-if-equal  $skip-chars-not-matching-in-slice:end/disp8
+749     # ++in->read
+750     40/inc-EAX
+751     eb/jump  $skip-chars-not-matching-in-slice:loop/disp8
+752 $skip-chars-not-matching-in-slice:end:
+753     # . restore registers
+754     5b/pop-to-EBX
+755     5a/pop-to-EDX
+756     59/pop-to-ECX
+757     # . epilog
+758     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+759     5d/pop-to-EBP
+760     c3/return
+761 
+762 test-skip-chars-not-matching-in-slice:
+763     # (EAX..ECX) = "ab "
+764     b8/copy-to-EAX  "ab "/imm32
+765     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+766     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
+767     05/add-to-EAX  4/imm32
+768     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
+769     # . . push args
+770     68/push  0x20/imm32
+771     51/push-ECX
+772     50/push-EAX
+773     # . . call
+774     e8/call  skip-chars-not-matching-in-slice/disp32
+775     # . . discard args
+776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+777     # check-ints-equal(ECX-EAX, 1, msg)
+778     # . . push args
+779     68/push  "F - test-skip-chars-not-matching-in-slice"/imm32
+780     68/push  1/imm32
+781     # . . push ECX-EAX
+782     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+783     51/push-ECX
+784     # . . call
+785     e8/call  check-ints-equal/disp32
+786     # . . discard args
+787     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+788     # end
+789     c3/return
+790 
+791 test-skip-chars-not-matching-in-slice-none:
+792     # (EAX..ECX) = " ab"
+793     b8/copy-to-EAX  " ab"/imm32
+794     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+795     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
+796     05/add-to-EAX  4/imm32
+797     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
+798     # . . push args
+799     68/push  0x20/imm32
+800     51/push-ECX
+801     50/push-EAX
+802     # . . call
+803     e8/call  skip-chars-not-matching-in-slice/disp32
+804     # . . discard args
+805     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+806     # check-ints-equal(ECX-EAX, 3, msg)
+807     # . . push args
+808     68/push  "F - test-skip-chars-not-matching-in-slice-none"/imm32
+809     68/push  3/imm32
+810     # . . push ECX-EAX
+811     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+812     51/push-ECX
+813     # . . call
+814     e8/call  check-ints-equal/disp32
+815     # . . discard args
+816     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+817     # end
+818     c3/return
+819 
+820 test-skip-chars-not-matching-in-slice-all:
+821     # (EAX..ECX) = "ab"
+822     b8/copy-to-EAX  "ab"/imm32
+823     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+824     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
+825     05/add-to-EAX  4/imm32
+826     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
+827     # . . push args
+828     68/push  0x20/imm32
+829     51/push-ECX
+830     50/push-EAX
+831     # . . call
+832     e8/call  skip-chars-not-matching-in-slice/disp32
+833     # . . discard args
+834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+835     # check-ints-equal(ECX-EAX, 0, msg)
+836     # . . push args
+837     68/push  "F - test-skip-chars-not-matching-in-slice-all"/imm32
+838     68/push  0/imm32
+839     # . . push ECX-EAX
+840     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+841     51/push-ECX
+842     # . . call
+843     e8/call  check-ints-equal/disp32
+844     # . . discard args
+845     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+846     # end
+847     c3/return
+848 
+849 # . . vim:nowrap:textwidth=0
+
+ + + -- cgit 1.4.1-2-gfad0