https://github.com/akkartik/mu/blob/master/128subx-words.subx
  1 # Helpers for parsing SubX words, with their rules for hex, labels and metadata.
  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 has-metadata?:  # word: (addr slice), s: (addr string) -> result/eax: boolean
  9     # pseudocode:
 10     #   var twig: &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
 11     #   curr = twig->end
 12     #   while true
 13     #     twig = next-token-from-slice(curr, word->end, '/')
 14     #     if (twig.empty()) break
 15     #     if (slice-equal?(twig, s)) return true
 16     #     curr = twig->end
 17     #   return false
 18     # . prologue
 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 = word
 27     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 28     # var edx: (addr byte) = word->end
 29     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
 30     # var twig/edi: slice
 31     68/push  0/imm32/end
 32     68/push  0/imm32/start
 33     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
 34     # next-token-from-slice(word->start, word->end, '/', twig)
 35     # . . push args
 36     57/push-edi
 37     68/push  0x2f/imm32/slash
 38     52/push-edx
 39     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
 40     # . . call
 41     e8/call  next-token-from-slice/disp32
 42     # . . discard args
 43     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 44 $has-metadata?:loop:
 45     # next-token-from-slice(curr, word->end, '/', twig)
 46     # . . push args
 47     57/push-edi
 48     68/push  0x2f/imm32/slash
 49     52/push-edx
 50     ff          6/subop/push        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # push *(edi+4)
 51     # . . call
 52     e8/call  next-token-from-slice/disp32
 53     # . . discard args
 54     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 55     # if (slice-empty?(twig)) return false
 56     # . eax = slice-empty?(twig)
 57     # . . push args
 58     57/push-edi
 59     # . . call
 60     e8/call  slice-empty?/disp32
 61     # . . discard args
 62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 63     # . if (eax != false) return false
 64     3d/compare-eax-and  0/imm32/false
 65     75/jump-if-!=  $has-metadata?:false/disp8
 66     # if (slice-equal?(twig, s)) return true
 67     # . eax = slice-equal?(twig, s)
 68     # . . push args
 69     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 70     57/push-edi
 71     # . . call
 72     e8/call  slice-equal?/disp32
 73     # . . discard args
 74     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 75     # . if (eax != false) return true
 76     3d/compare-eax-and  0/imm32/false
 77     # eax already contains truepre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
### A debugging helper that lets you zoom in/out on a trace.

To try it out, first create an example trace (from the top-level `mu/`
directory):

  ```shell
  ./subx --trace run apps/factorial
  ```

This command will save a trace of its execution in a file called `last_run`.
The trace consists of a series of lines, each starting with an integer depth
and a single-word 'label', followed by a colon and whitespace.

Now browse this trace:

  ```shell
  tools/browse_trace last_run
  ```

You should now find yourself in a UI showing a subsequence of lines from the
trace, each line starting with a numeric depth, and ending with a parenthetical
count of trace lines hidden after it with greater depths.

For example, this line:

  ```
  2 app: line1 (30)
  ```

indicates that it was logged with depth 2, and that 30 following lines have
been hidden at a depth greater than 2.

(As an experiment, hidden counts of 1000 or more are highlighted in red.)

The UI provides the following hotkeys:

* `q` or `ctrl-c`: Quit.

* `Enter`: 'Zoom into' this line. Expand lines hidden after it that were at
  the next higher level.

* `Backspace`: 'Zoom out' on a line after zooming in, collapsing lines below
  expanded by some series of `Enter` commands.

* `j` or `down-arrow`: Move cursor down one line.
* `k` or `up-arrow`: Move cursor up one line.
* `J` or `ctrl-f` or `page-down`: Scroll cursor down one page.
* `K` or `ctrl-b` or `page-up`: Scroll cursor up one page.
* `h` or `left-arrow`: Scroll cursor left one character.
* `l` or `right-arrow`: Scroll cursor right one character.
* `H`: Scroll cursor left one screen-width.
* `L`: Scroll cursor right one screen-width.

* `g` or `home`: Move cursor to start of trace.
* `G` or `end`: Move cursor to end of trace.

* `t`: Move cursor to top line on screen.
* `c`: Move cursor to center line on screen.
* `b`: Move cursor to bottom line on screen.
* `T`: Scroll line at cursor to top of screen.

* `/`: Search forward for a pattern.
* `?`: Search backward for a pattern.
* `n`: Repeat the previous `/` or `?`.
* `N`: Repeat the previous `/` or `?` in the opposite direction.

After hitting `/`, the mini-editor on the bottom-most line supports the
following hotkeys:
* ascii characters: add the key to the pattern.
* `Enter`: search for the pattern.
* `Esc` or `ctrl-c`: cancel the current search, setting the screen back
  to its state before the search.
* `left-arrow`: move cursor left.
* `right-arrow`: move cursor right.
* `ctrl-a` or `home`: move cursor to start of search pattern.
* `ctrl-e` or `end`: move cursor to end of search pattern.
* `ctrl-u`: clear search pattern before cursor
* `ctrl-k`: clear search pattern at and after cursor

## wish list

* Simple regular expression search: `.` and `*`.
* Expand into lower depths as necessary when searching.
* Zoom out everything.
* Zoom out lines around the cursor to the highest (or specified) depth.
  Maybe a number followed by `]`?
ent"># copy ebp to esp 162 5d/pop-to-ebp 163 c3/return 164 165 test-has-metadata-ignore-name: 166 # . prologue 167 55/push-ebp 168 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 169 # (eax..ecx) = "a/b" 170 b8/copy-to-eax "a/b"/imm32 171 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 172 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 173 05/add-to-eax 4/imm32 174 # var in/esi: slice = {eax, ecx} 175 51/push-ecx 176 50/push-eax 177 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi 178 # eax = has-metadata?(esi, "a") 179 # . . push args 180 68/push "a"/imm32 181 56/push-esi 182 # . . call 183 e8/call has-metadata?/disp32 184 # . . discard args 185 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 186 # check-ints-equal(eax, 0, msg) 187 # . . push args 188 68/push "F - test-has-metadata-ignore-name"/imm32 189 68/push 0/imm32/false 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 # . epilogue 196 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 197 5d/pop-to-ebp 198 c3/return 199 200 test-has-metadata-multiple-true: 201 # . prologue 202 55/push-ebp 203 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 204 # (eax..ecx) = "a/b/c" 205 b8/copy-to-eax "a/b/c"/imm32 206 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 207 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 208 05/add-to-eax 4/imm32 209 # var in/esi: slice = {eax, ecx} 210 51/push-ecx 211 50/push-eax 212 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi 213 # eax = has-metadata?(esi, "c") 214 # . . push args 215 68/push "c"/imm32 216 56/push-esi 217 # . . call 218 e8/call has-metadata?/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, 1, msg) 222 # . . push args 223 68/push "F - test-has-metadata-multiple-true"/imm32 224 68/push 1/imm32/true 225 50/push-eax 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 # . epilogue 231 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 232 5d/pop-to-ebp 233 c3/return 234 235 test-has-metadata-multiple-false: 236 # . prologue 237 55/push-ebp 238 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 239 # (eax..ecx) = "a/b/c" 240 b8/copy-to-eax "a/b/c"/imm32 241 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 242 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 243 05/add-to-eax 4/imm32 244 # var in/esi: slice = {eax, ecx} 245 51/push-ecx 246 50/push-eax 247 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi 248 # eax = has-metadata?(esi, "d") 249 # . . push args 250 68/push "d"/imm32 251 56/push-esi 252 # . . call 253 e8/call has-metadata?/disp32 254 # . . discard args 255 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 256 # check-ints-equal(eax, 0, msg) 257 # . . push args 258 68/push "F - test-has-metadata-multiple-false"/imm32 259 68/push 0/imm32/false 260 50/push-eax 261 # . . call 262 e8/call check-ints-equal/disp32 263 # . . discard args 264 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 265 # . epilogue 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 # conditions for 'valid' names that are not at risk of looking like hex numbers 271 # keep in sync with the rules in labels.cc 272 #: - if it starts with a digit, it's treated as a number. If it can't be 273 #: parsed as hex it will raise an error. 274 #: - if it starts with '-' it's treated as a number. 275 #: - if it starts with '0x' it's treated as a number. (redundant) 276 #: - if it's two characters long, it can't be a name. Either it's a hex 277 #: byte, or it raises an error. 278 is-valid-name?: # in: (addr slice) -> result/eax: boolean 279 # . prologue 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 56/push-esi 285 # esi = in 286 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi 287 # var start/ecx: (addr byte) = in->start 288 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx 289 $is-valid-name?:check0: 290 # if (start >= in->end) return false 291 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # compare ecx with *(esi+4) 292 73/jump-if-addr>= $is-valid-name?:false/disp8 293 $is-valid-name?:check1: 294 # var len/eax: int = in->end - start 295 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax 296 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax 297 # if (eax == 2) return false 298 3d/compare-eax-and 2/imm32 299 74/jump-if-= $is-valid-name?:false/disp8 300 $is-valid-name?:check2: 301 # var c/eax: (addr byte) = *start 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 # if (c == "-") return false 305 3d/compare-eax-and 2d/imm32/- 306 74/jump-if-= $is-valid-name?:false/disp8 307 $is-valid-name?:check3a: 308 # if (c < "0") return true 309 3d/compare-eax-with 30/imm32/0 310 7c/jump-if-< $is-valid-name?:true/disp8 311 $is-valid-name?:check3b: 312 # if (c > "9") return true 313 3d/compare-eax-with 39/imm32/9 314 7f/jump-if-> $is-valid-name?:true/disp8 315 $is-valid-name?:false: 316 # return false 317 b8/copy-to-eax 0/imm32/false 318 eb/jump $is-valid-name?:end/disp8 319 $is-valid-name?:true: 320 # return true 321 b8/copy-to-eax 1/imm32/true 322 $is-valid-name?:end: 323 # . restore registers 324 5e/pop-to-esi 325 59/pop-to-ecx 326 # . epilogue 327 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 328 5d/pop-to-ebp 329 c3/return 330 331 test-is-valid-name-digit-prefix: 332 # . prologue 333 55/push-ebp 334 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 335 # (eax..ecx) = "34" 336 b8/copy-to-eax "34"/imm32 337 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 338 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 339 05/add-to-eax 4/imm32 340 # var slice/ecx: slice = {eax, ecx} 341 51/push-ecx 342 50/push-eax 343 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 344 # eax = is-valid-name?(slice) 345 # . . push args 346 51/push-ecx 347 # . . call 348 e8/call is-valid-name?/disp32 349 # . . discard args 350 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 351 # check-ints-equal(eax, 0, msg) 352 # . . push args 353 68/push "F - test-is-valid-name-digit-prefix"/imm32 354 68/push 0/imm32/false 355 50/push-eax 356 # . . call 357 e8/call check-ints-equal/disp32 358 # . . discard args 359 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 360 # . epilogue 361 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 362 5d/pop-to-ebp 363 c3/return 364 365 test-is-valid-name-negative-prefix: 366 # . prologue 367 55/push-ebp 368 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 369 # (eax..ecx) = "-0x34" 370 b8/copy-to-eax "-0x34"/imm32 371 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 372 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 373 05/add-to-eax 4/imm32 374 # var slice/ecx: slice = {eax, ecx} 375 51/push-ecx 376 50/push-eax 377 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 378 # eax = is-valid-name?(slice) 379 # . . push args 380 51/push-ecx 381 # . . call 382 e8/call is-valid-name?/disp32 383 # . . discard args 384 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 385 # check-ints-equal(eax, 0, msg) 386 # . . push args 387 68/push "F - test-is-valid-name-negative-prefix"/imm32 388 68/push 0/imm32/false 389 50/push-eax 390 # . . call 391 e8/call check-ints-equal/disp32 392 # . . discard args 393 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 394 # . epilogue 395 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 396 5d/pop-to-ebp 397 c3/return 398 399 test-is-valid-name-0x-prefix: 400 # . prologue 401 55/push-ebp 402 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 403 # (eax..ecx) = "0x34" 404 b8/copy-to-eax "0x34"/imm32 405 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 406 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 407 05/add-to-eax 4/imm32 408 # var slice/ecx: slice = {eax, ecx} 409 51/push-ecx 410 50/push-eax 411 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 412 # eax = is-valid-name?(slice) 413 # . . push args 414 51/push-ecx 415 # . . call 416 e8/call is-valid-name?/disp32 417 # . . discard args 418 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 419 # check-ints-equal(eax, 0, msg) 420 # . . push args 421 68/push "F - test-is-valid-name-0x-prefix"/imm32 422 68/push 0/imm32/false 423 50/push-eax 424 # . . call 425 e8/call check-ints-equal/disp32 426 # . . discard args 427 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 428 # . epilogue 429 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 430 5d/pop-to-ebp 431 c3/return 432 433 test-is-valid-name-starts-with-pre-digit: 434 # . prologue 435 55/push-ebp 436 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 437 # (eax..ecx) = "/03" 438 b8/copy-to-eax "/03"/imm32 439 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 440 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 441 05/add-to-eax 4/imm32 442 # var slice/ecx: slice = {eax, ecx} 443 51/push-ecx 444 50/push-eax 445 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 446 # eax = is-valid-name?(slice) 447 # . . push args 448 51/push-ecx 449 # . . call 450 e8/call is-valid-name?/disp32 451 # . . discard args 452 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 453 # check-ints-equal(eax, 1, msg) 454 # . . push args 455 68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32 456 68/push 1/imm32/true 457 50/push-eax 458 # . . call 459 e8/call check-ints-equal/disp32 460 # . . discard args 461 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 462 # . epilogue 463 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 464 5d/pop-to-ebp 465 c3/return 466 467 test-is-valid-name-starts-with-post-digit: 468 # . prologue 469 55/push-ebp 470 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 471 # (eax..ecx) = "q34" 472 b8/copy-to-eax "q34"/imm32 473 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 474 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 475 05/add-to-eax 4/imm32 476 # var slice/ecx: slice = {eax, ecx} 477 51/push-ecx 478 50/push-eax 479 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 480 # eax = is-valid-name?(slice) 481 # . . push args 482 51/push-ecx 483 # . . call 484 e8/call is-valid-name?/disp32 485 # . . discard args 486 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 487 # check-ints-equal(eax, 1, msg) 488 # . . push args 489 68/push "F - test-is-valid-name-starts-with-post-digit"/imm32 490 68/push 1/imm32/true 491 50/push-eax 492 # . . call 493 e8/call check-ints-equal/disp32 494 # . . discard args 495 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 496 # . epilogue 497 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 498 5d/pop-to-ebp 499 c3/return 500 501 test-is-valid-name-starts-with-digit: 502 # . prologue 503 55/push-ebp 504 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 505 # (eax..ecx) = "0x34" 506 b8/copy-to-eax "0x34"/imm32 507 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 508 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 509 05/add-to-eax 4/imm32 510 # var slice/ecx: slice = {eax, ecx} 511 51/push-ecx 512 50/push-eax 513 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 514 # eax = is-valid-name?(slice) 515 # . . push args 516 51/push-ecx 517 # . . call 518 e8/call is-valid-name?/disp32 519 # . . discard args 520 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 521 # check-ints-equal(eax, 0, msg) 522 # . . push args 523 68/push "F - test-is-valid-name-starts-with-digit"/imm32 524 68/push 0/imm32/false 525 50/push-eax 526 # . . call 527 e8/call check-ints-equal/disp32 528 # . . discard args 529 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 530 # . epilogue 531 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 532 5d/pop-to-ebp 533 c3/return 534 535 is-label?: # word: (addr slice) -> result/eax: boolean 536 # . prologue 537 55/push-ebp 538 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 539 # . save registers 540 51/push-ecx 541 # ecx = word 542 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx 543 # var end/ecx: (addr byte) = word->end 544 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 1/r32/ecx 4/disp8 . # copy *(ecx+4) to ecx 545 # return *(end - 1) == ':' 546 # . eax = *(end-1) 547 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 548 8a/copy-byte 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/AL -1/disp8 . # copy byte at *(ecx-1) to AL 549 # . return (eax == ':') 550 3d/compare-eax-and 0x3a/imm32/colon 551 b8/copy-to-eax 1/imm32/true 552 74/jump-if-= $is-label?:end/disp8 553 b8/copy-to-eax 0/imm32/false 554 $is-label?:end: 555 # . restore registers 556 59/pop-to-ecx 557 # . epilogue 558 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 559 5d/pop-to-ebp 560 c3/return 561 562 test-is-label?: 563 # . prologue 564 55/push-ebp 565 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 566 $test-is-label?:true: 567 # (eax..ecx) = "AAA:" 568 b8/copy-to-eax "AAA:"/imm32 569 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 570 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 571 05/add-to-eax 4/imm32 572 # var slice/ecx: slice = {eax, ecx} 573 51/push-ecx 574 50/push-eax 575 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 576 # is-label?(slice/ecx) 577 # . . push args 578 51/push-ecx 579 # . . call 580 e8/call is-label?/disp32 581 # . . discard args 582 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 583 # check-ints-equal(eax, 1, msg) 584 # . . push args 585 68/push "F - test-is-label?:true"/imm32 586 68/push 1/imm32 587 50/push-eax 588 # . . call 589 e8/call check-ints-equal/disp32 590 # . . discard args 591 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 592 $test-is-label?:false: 593 # (eax..ecx) = "AAA" 594 b8/copy-to-eax "AAA"/imm32 595 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 596 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 597 05/add-to-eax 4/imm32 598 # var slice/ecx: slice = {eax, ecx} 599 51/push-ecx 600 50/push-eax 601 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 602 # is-label?(slice/ecx) 603 # . . push args 604 51/push-ecx 605 # . . call 606 e8/call is-label?/disp32 607 # . . discard args 608 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 609 # check-ints-equal(eax, 0, msg) 610 # . . push args 611 68/push "F - test-is-label?:false"/imm32 612 68/push 0/imm32 613 50/push-eax 614 # . . call 615 e8/call check-ints-equal/disp32 616 # . . discard args 617 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 618 # . epilogue 619 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 620 5d/pop-to-ebp 621 c3/return 622 623 # . . vim:nowrap:textwidth=0