https://github.com/akkartik/mu/blob/main/127next-word.subx
  1 # Tokenize by whitespace.
  2 
  3 == code
  4 #   instruction                     effective address                                                   register    displacement    immediate
  5 # . op          subop               mod             rm32          base        index         scale       r32
  6 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
  7 
  8 # (re)compute the bounds of the next word in the line (surrounded by whitespace,
  9 # treating '#' comments as a single word)
 10 # return empty string on reaching end of file
 11 next-word:  # line: (addr stream byte), out: (addr slice)
 12     # . prologue
 13     55/push-ebp
 14     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 15     # . save registers
 16     50/push-eax
 17     51/push-ecx
 18     56/push-esi
 19     57/push-edi
 20     # esi = line
 21     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 22     # edi = out
 23     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
 24     # skip-chars-matching-whitespace(line)
 25     # . . push args
 26     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 27     # . . call
 28     e8/call  skip-chars-matching-whitespace/disp32
 29     # . . discard args
 30     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 31 $next-word:check0:
 32     # if (line->read >= line->write) clear out and return
 33     # . eax = line->read
 34     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
 35     # . if (eax < line->write) goto next check
 36     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
 37     7c/jump-if-<  $next-word:check-for-comment/disp8
 38     # . return out
 39     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
 40     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
 41     eb/jump  $next-word:end/disp8
 42 $next-word:check-for-comment:
 43     # out->start = &line->data[line->read]
 44     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 45     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
 46     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
 47     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
 48     # . eax = line->data[line->read]
 49     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 50     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
 51     # . compare
 52     3d/compare-eax-and  0x23/imm32/pound
 53     75/jump-if-!=  $next-word:regular-word/disp8
 54 $next-word:comment:
 55     # . out->end = &line->data[line->write]
 56     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
 57     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 line);
extern void pipe_spawn(char *buf, unsigned int len, Display *dpy, char *argv[]);
extern void spawn(Display *dpy, char *argv[]);
extern void swap(void **p1, void **p2);
extern unsigned int tokenize(char **result, unsigned int reslen,
		char *str, char delim);
esp . . . . . 4/imm32 # add to esp 72 # out->end = &line->data[line->read] 73 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 74 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 75 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) 76 $next-word:end: 77 # . restore registers 78 5f/pop-to-edi 79 5e/pop-to-esi 80 59/pop-to-ecx 81 58/pop-to-eax 82 # . epilogue 83 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 84 5d/pop-to-ebp 85 c3/return 86 87 test-next-word: 88 # . prologue 89 55/push-ebp 90 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 91 # setup 92 # . clear-stream(_test-stream) 93 # . . push args 94 68/push _test-stream/imm32 95 # . . call 96 e8/call clear-stream/disp32 97 # . . discard args 98 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 99 # var slice/ecx: slice 100 68/push 0/imm32/end 101 68/push 0/imm32/start 102 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 103 # write(_test-stream, " ab") 104 # . . push args 105 68/push " ab"/imm32 106 68/push _test-stream/imm32 107 # . . call 108 e8/call write/disp32 109 # . . discard args 110 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 111 # next-word(_test-stream, slice) 112 # . . push args 113 51/push-ecx 114 68/push _test-stream/imm32 115 # . . call 116 e8/call next-word/disp32 117 # . . discard args 118 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 119 # check-ints-equal(slice->start - _test-stream->data, 2, msg) 120 # . check-ints-equal(slice->start - _test-stream, 14, msg) 121 # . . push args 122 68/push "F - test-next-word: start"/imm32 123 68/push 0xe/imm32 124 # . . push slice->start - _test-stream 125 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax 126 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax 127 50/push-eax 128 # . . call 129 e8/call check-ints-equal/disp32 130 # . . discard args 131 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 132 # check-ints-equal(slice->end - _test-stream->data, 4, msg) 133 # . check-ints-equal(slice->end - _test-stream, 16, msg) 134 # . . push args 135 68/push "F - test-next-word: end"/imm32 136 68/push 0x10/imm32 137 # . . push slice->end - _test-stream 138 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax 139 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax 140 50/push-eax 141 # . . call 142 e8/call check-ints-equal/disp32 143 # . . discard args 144 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 145 # . epilogue 146 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 147 5d/pop-to-ebp 148 c3/return 149 150 test-next-word-returns-whole-comment: 151 # . prologue 152 55/push-ebp 153 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 154 # setup 155 # . clear-stream(_test-stream) 156 # . . push args 157 68/push _test-stream/imm32 158 # . . call 159 e8/call clear-stream/disp32 160 # . . discard args 161 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 162 # var slice/ecx: slice 163 68/push 0/imm32/end 164 68/push 0/imm32/start 165 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 166 # write(_test-stream, " # a") 167 # . . push args 168 68/push " # a"/imm32 169 68/push _test-stream/imm32 170 # . . call 171 e8/call write/disp32 172 # . . discard args 173 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 174 # next-word(_test-stream, slice) 175 # . . push args 176 51/push-ecx 177 68/push _test-stream/imm32 178 # . . call 179 e8/call next-word/disp32 180 # . . discard args 181 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 182 # check-ints-equal(slice->start - _test-stream->data, 2, msg) 183 # . check-ints-equal(slice->start - _test-stream, 14, msg) 184 # . . push args 185 68/push "F - test-next-word-returns-whole-comment: start"/imm32 186 68/push 0xe/imm32 187 # . . push slice->start - _test-stream 188 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax 189 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax 190 50/push-eax 191 # . . call 192 e8/call check-ints-equal/disp32 193 # . . discard args 194 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 195 # check-ints-equal(slice->end - _test-stream->data, 5, msg) 196 # . check-ints-equal(slice->end - _test-stream, 17, msg) 197 # . . push args 198 68/push "F - test-next-word-returns-whole-comment: end"/imm32 199 68/push 0x11/imm32 200 # . . push slice->end - _test-stream 201 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax 202 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax 203 50/push-eax 204 # . . call 205 e8/call check-ints-equal/disp32 206 # . . discard args 207 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 208 # . epilogue 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 test-next-word-returns-empty-string-on-eof: 214 # . prologue 215 55/push-ebp 216 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 217 # setup 218 # . clear-stream(_test-stream) 219 # . . push args 220 68/push _test-stream/imm32 221 # . . call 222 e8/call clear-stream/disp32 223 # . . discard args 224 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 225 # var slice/ecx: slice 226 68/push 0/imm32/end 227 68/push 0/imm32/start 228 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 229 # write nothing to _test-stream 230 # next-word(_test-stream, slice) 231 # . . push args 232 51/push-ecx 233 68/push _test-stream/imm32 234 # . . call 235 e8/call next-word/disp32 236 # . . discard args 237 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 238 # check-ints-equal(slice->end - slice->start, 0, msg) 239 # . . push args 240 68/push "F - test-next-word-returns-empty-string-on-eof"/imm32 241 68/push 0/imm32 242 # . . push slice->end - slice->start 243 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax 244 2b/subtract 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # subtract *ecx from eax 245 50/push-eax 246 # . . call 247 e8/call check-ints-equal/disp32 248 # . . discard args 249 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 250 # . epilogue 251 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 252 5d/pop-to-ebp 253 c3/return 254 255 test-next-word-returns-empty-string-on-newline: 256 # . prologue 257 55/push-ebp 258 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 259 # setup 260 # . clear-stream(_test-stream) 261 # . . push args 262 68/push _test-stream/imm32 263 # . . call 264 e8/call clear-stream/disp32 265 # . . discard args 266 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 267 # var slice/ecx: slice 268 68/push 0/imm32/end 269 68/push 0/imm32/start 270 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 271 # write some whitespace and a newline 272 # . . push args 273 68/push " \n"/imm32 274 68/push _test-stream/imm32 275 # . . call 276 e8/call write/disp32 277 # . . discard args 278 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 279 # next-word(_test-stream, slice) 280 # . . push args 281 51/push-ecx 282 68/push _test-stream/imm32 283 # . . call 284 e8/call next-word/disp32 285 # . . discard args 286 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 287 # check-ints-equal(slice->end - slice->start, 0, msg) 288 # . . push args 289 68/push "F - test-next-word-returns-empty-string-on-newline"/imm32 290 68/push 0/imm32 291 # . . push slice->end - slice->start 292 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax 293 2b/subtract 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # subtract *ecx from eax 294 50/push-eax 295 # . . call 296 e8/call check-ints-equal/disp32 297 # . . discard args 298 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 299 # . epilogue 300 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 301 5d/pop-to-ebp 302 c3/return 303 304 # (re)compute the bounds of the next word in the line (separated by whitespace) 305 # return empty string on reaching end of file 306 next-raw-word: # line: (addr stream byte), out: (addr slice) 307 # . prologue 308 55/push-ebp 309 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 310 # . save registers 311 50/push-eax 312 51/push-ecx 313 56/push-esi 314 57/push-edi 315 # esi = line 316 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi 317 # edi = out 318 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi 319 # skip-chars-matching-whitespace(line) 320 # . . push args 321 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 322 # . . call 323 e8/call skip-chars-matching-whitespace/disp32 324 # . . discard args 325 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 326 $next-raw-word:check0: 327 # if (line->read >= line->write) clear out and return 328 # . eax = line->read 329 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax 330 # . if (eax < line->write) goto next check 331 3b/compare 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # compare eax with *esi 332 7c/jump-if-< $next-raw-word:word-exists/disp8 333 # . return out 334 c7 0/subop/copy 0/mod/direct 7/rm32/edi . . . . . 0/imm32 # copy to *edi 335 c7 0/subop/copy 1/mod/*+disp8 7/rm32/edi . . . . 4/disp8 0/imm32 # copy to *(edi+4) 336 eb/jump $next-raw-word:end/disp8 337 $next-raw-word:word-exists: 338 # out->start = &line->data[line->read] 339 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 340 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 341 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi 342 # skip-chars-not-matching-whitespace(line) # including trailing newline 343 # . . push args 344 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 345 # . . call 346 e8/call skip-chars-not-matching-whitespace/disp32 347 # . . discard args 348 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 349 # out->end = &line->data[line->read] 350 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx 351 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 352 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) 353 $next-raw-word:end: 354 # . restore registers 355 5f/pop-to-edi 356 5e/pop-to-esi 357 59/pop-to-ecx 358 58/pop-to-eax 359 # . epilogue 360 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 361 5d/pop-to-ebp 362 c3/return