https://github.com/akkartik/mu/blob/main/118parse-hex-int.subx
  1 # some utilities for converting numbers 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 is-hex-int?:  # in: (addr slice) -> result/eax: boolean
 10     # . prologue
 11     55/push-ebp
 12     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 13     # . save registers
 14     51/push-ecx
 15     52/push-edx
 16     53/push-ebx
 17     # ecx = s
 18     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
 19     # edx = s->end
 20     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
 21     # var curr/ecx: (addr byte) = s->start
 22     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # copy *ecx to ecx
 23     # if s is empty return false
 24     b8/copy-to-eax  0/imm32/false
 25     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 26     73/jump-if-addr>=  $is-hex-int?:end/disp8
 27     # skip past leading '-'
 28     # . if (*curr == '-') ++curr
 29     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 30     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
 31     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x2d/imm32/-      # compare ebx
 32     75/jump-if-!=  $is-hex-int?:initial-0/disp8
 33     # . ++curr
 34     41/increment-ecx
 35     # skip past leading '0x'
 36 $is-hex-int?:initial-0:
 37     # . if (*curr != '0') jump to loop
 38     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 39     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
 40     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x30/imm32/0      # compare ebx
 41     75/jump-if-!=  $is-hex-int?:loop/disp8
 42     # . ++curr
 43     41/increment-ecx
 44 $is-hex-int?:initial-0x:
 45     # . if (curr >= in->end) return true
 46     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 47     73/jump-if-addr>=  $is-hex-int?:true/disp8
 48     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
 49     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 50     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
 51     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x78/imm32/x      # compare ebx
 52     75/jump-if-!=  $is-hex-int?:loop/disp8
 53     # . ++curr
 54     41/increment-ecx
 55 $is-hex-int?:loop:
 56     # if (curr >= in->end) return true
 57     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 58     73/jump-if-addr>=  $is-hex-int?:true/disp8
 59     # var eax: boolean = is-hex-digit?(*curr)
 60     # . . push args
 61     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
 62     50/push-eax
 63     # . . call
 64     e8/call  is-hex-digit?/disp32
 65     # . . discard args
 66     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 67     # if (eax == false) return false
 68     3d/compare-eax-and  0/imm32/false
 69     74/jump-if-=  $is-hex-int?:end/disp8
 70     # ++curr
 71     41/increment-ecx
 72     # loop
 73     eb/jump  $is-hex-int?:loop/disp8
 74 $is-hex-int?:true:
 75     # return true
 76     b8/copy-to-eax  1/imm32/true
 77 $is-hex-int?:end:
 78     # . restore registers
 79     5b/pop-to-ebx
 80     5a/pop-to-edx
 81     59/pop-to-ecx
 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-is-hex-int:
 88     # . prologue
 89     55/push-ebp
 90     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 91     # (eax..ecx) = "34"
 92     b8/copy-to-eax  "34"/imm32
 93     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 94     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
 95     05/add-to-eax  4/imm32
 96     # var slice/ecx: slice = {eax, ecx}
 97     51/push-ecx
 98     50/push-eax
 99     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
100     # eax = is-hex-int?(slice)
101     # . . push args
102     51/push-ecx
103     # . . call
104     e8/call  is-hex-int?/disp32
105     # . . discard args
106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
107     # check-ints-equal(eax, 1, msg)
108     # . . push args
109     68/push  "F - test-is-hex-int"/imm32
110     68/push  1/imm32/true
111     50/push-eax
112     # . . call
113     e8/call  check-ints-equal/disp32
114     # . . discard args
115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
116     # . epilogue
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-is-hex-int-handles-letters:
122     # . prologue
123     55/push-ebp
124     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
125     # (eax..ecx) = "34a"
126     b8/copy-to-eax  "34a"/imm32
127     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
128     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
129     05/add-to-eax  4/imm32
130     # var slice/ecx: slice = {eax, ecx}
131     51/push-ecx
132     50/push-eax
133     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
134     # eax = is-hex-int?(slice)
135     # . . push args
136     51/push-ecx
137     # . . call
138     e8/call  is-hex-int?/disp32
139     # . . discard args
140     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
141     # check-ints-equal(eax, 1, msg)
142     # . . push args
143     68/push  "F - test-is-hex-int-handles-letters"/imm32
144     68/push  1/imm32/true
145     50/push-eax
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     # . epilogue
151     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
152     5d/pop-to-ebp
153     c3/return
154 
155 test-is-hex-int-with-trailing-char:
156     # . prologue
157     55/push-ebp
158     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
159     # (eax..ecx) = "34q"
160     b8/copy-to-eax  "34q"/imm32
161     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
162     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
163     05/add-to-eax  4/imm32
164     # var slice/ecx: slice = {eax, ecx}
165     51/push-ecx
166     50/push-eax
167     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
168     # eax = is-hex-int?(slice)
169     # . . push args
170     51/push-ecx
171     # . . call
172     e8/call  is-hex-int?/disp32
173     # . . discard args
174     81          0/subop/add         3/mod/direct    4/rm32/esp    x
      tmpx <- shift-left 1
      tmpx <- increment
      add-to err, tmpx
    }
    # loop termination condition
    compare x, 0
    loop-if-<
  }
}

fn draw-disc screen: (addr screen), cx: int, cy: int, radius: int, color: int, border-color: int {
  var r/eax: int <- copy 0
  {
    compare r, radius
    break-if->=
    draw-circle screen, cx cy, r, color
    r <- increment
    loop
  }
  draw-circle screen, cx cy, r, border-color
}
"L205" class="LineNr">205 # . . call 206 e8/call is-hex-int?/disp32 207 # . . discard args 208 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 209 # check-ints-equal(eax, 0, msg) 210 # . . push args 211 68/push "F - test-is-hex-int-with-leading-char"/imm32 212 68/push 0/imm32/false 213 50/push-eax 214 # . . call 215 e8/call check-ints-equal/disp32 216 # . . discard args 217 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 218 # . epilogue 219 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 220 5d/pop-to-ebp 221 c3/return 222 223 test-is-hex-int-empty: 224 # . prologue 225 55/push-ebp 226 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 227 # var slice/ecx: slice = "" 228 68/push 0/imm32 229 68/push 0/imm32 230 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 231 # eax = is-hex-int?(slice) 232 # . . push args 233 51/push-ecx 234 # . . call 235 e8/call is-hex-int?/disp32 236 # . . discard args 237 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 238 # check-ints-equal(eax, 0, msg) 239 # . . push args 240 68/push "F - test-is-hex-int-empty"/imm32 241 68/push 0/imm32/false 242 50/push-eax 243 # . . call 244 e8/call check-ints-equal/disp32 245 # . . discard args 246 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 247 # . epilogue 248 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 249 5d/pop-to-ebp 250 c3/return 251 252 test-is-hex-int-handles-0x-prefix: 253 # . prologue 254 55/push-ebp 255 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 256 # (eax..ecx) = "0x3a" 257 b8/copy-to-eax "0x3a"/imm32 258 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 259 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 260 05/add-to-eax 4/imm32 261 # var slice/ecx: slice = {eax, ecx} 262 51/push-ecx 263 50/push-eax 264 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 265 # eax = is-hex-int?(slice) 266 # . . push args 267 51/push-ecx 268 # . . call 269 e8/call is-hex-int?/disp32 270 # . . discard args 271 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 272 # check-ints-equal(eax, 1, msg) 273 # . . push args 274 68/push "F - test-is-hex-int-handles-0x-prefix"/imm32 275 68/push 1/imm32/true 276 50/push-eax 277 # . . call 278 e8/call check-ints-equal/disp32 279 # . . discard args 280 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 281 # . epilogue 282 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 283 5d/pop-to-ebp 284 c3/return 285 286 test-is-hex-int-handles-negative: 287 # . prologue 288 55/push-ebp 289 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 290 # (eax..ecx) = "-34a" 291 b8/copy-to-eax "-34a"/imm32 292 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 293 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 294 05/add-to-eax 4/imm32 295 # var slice/ecx: slice = {eax, ecx} 296 51/push-ecx 297 50/push-eax 298 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 299 # eax = is-hex-int?(slice) 300 # . . push args 301 51/push-ecx 302 # . . call 303 e8/call is-hex-int?/disp32 304 # . . discard args 305 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 306 # check-ints-equal(eax, 1, msg) 307 # . . push args 308 68/push "F - test-is-hex-int-handles-negative"/imm32 309 68/push 1/imm32/true 310 50/push-eax 311 # . . call 312 e8/call check-ints-equal/disp32 313 # . . discard args 314 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 315 # . epilogue 316 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 317 5d/pop-to-ebp 318 c3/return 319 320 test-is-hex-int-handles-negative-0x-prefix: 321 # . prologue 322 55/push-ebp 323 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 324 # (eax..ecx) = "-0x3a" 325 b8/copy-to-eax "-0x3a"/imm32 326 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 327 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 328 05/add-to-eax 4/imm32 329 # var slice/ecx: slice = {eax, ecx} 330 51/push-ecx 331 50/push-eax 332 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 333 # eax = is-hex-int?(slice) 334 # . . push args 335 51/push-ecx 336 # . . call 337 e8/call is-hex-int?/disp32 338 # . . discard args 339 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 340 # check-ints-equal(eax, 1, msg) 341 # . . push args 342 68/push "F - test-is-hex-int-handles-negative-0x-prefix"/imm32 343 68/push 1/imm32/true 344 50/push-eax 345 # . . call 346 e8/call check-ints-equal/disp32 347 # . . discard args 348 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 349 # . epilogue 350 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 351 5d/pop-to-ebp 352 c3/return 353 354 parse-hex-int: # in: (addr array byte) -> result/eax: int 355 # . prologue 356 55/push-ebp 357 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 358 # . save registers 359 51/push-ecx 360 52/push-edx 361 # eax = in 362 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax 363 # var curr/ecx: (addr byte) = &in->data 364 8d/copy-address 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 4/disp8 . # copy eax+4 to ecx 365 # var max/edx: (addr byte) = &in->data[in->size] 366 # . edx = in->size 367 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx 368 # . edx = in->data + in->size 369 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx 370 # return parse-hex-int-helper(curr, max) 371 # . . push args 372 52/push-edx 373 51/push-ecx 374 # . . call 375 e8/call parse-hex-int-helper/disp32 376 # . . discard args 377 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 378 $parse-hex-int:end: 379 # . restore registers 380 5a/pop-to-edx 381 59/pop-to-ecx 382 # . epilogue 383 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 384 5d/pop-to-ebp 385 c3/return 386 387 parse-hex-int-from-slice: # in: (addr slice) -> result/eax: int 388 # . prologue 389 55/push-ebp 390 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 391 # . save registers 392 51/push-ecx 393 52/push-edx 394 # ecx = in 395 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx 396 # var max/edx: (addr byte) = in->end 397 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx 398 # var curr/ecx: (addr byte) = in->start 399 8b/copy 0/mod/indirect 1/rm32/ecx . . . 1/r32/ecx . . # copy *ecx to ecx 400 # return parse-hex-int-helper(curr, max) 401 # . . push args 402 52/push-edx 403 51/push-ecx 404 # . . call 405 e8/call parse-hex-int-helper/disp32 406 # . . discard args 407 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 408 $parse-hex-int-from-slice:end: 409 # . restore registers 410 5a/pop-to-edx 411 59/pop-to-ecx 412 # . epilogue 413 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 414 5d/pop-to-ebp 415 c3/return 416 417 parse-hex-int-helper: # start: (addr byte), end: (addr byte) -> result/eax: int 418 # . prologue 419 55/push-ebp 420 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 421 # . save registers 422 51/push-ecx 423 52/push-edx 424 53/push-ebx 425 56/push-esi 426 # var curr/ecx: (addr byte) = start 427 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx 428 # edx = max 429 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx 430 # var result/ebx: int = 0 431 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx 432 # var negate?/esi: boolean = false 433 31/xor 3/mod/direct 6/rm32/esi . . . 6/r32/esi . . # clear esi 434 $parse-hex-int-helper:negative: 435 # if (*curr == '-') ++curr, negate = true 436 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 437 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL 438 3d/compare-eax-and 0x2d/imm32/- 439 75/jump-if-!= $parse-hex-int-helper:initial-0/disp8 440 # . ++curr 441 41/increment-ecx 442 # . negate = true 443 be/copy-to-esi 1/imm32/true 444 $parse-hex-int-helper:initial-0: 445 # skip past leading '0x' 446 # . if (*curr != '0') jump to loop 447 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL 448 3d/compare-eax-and 0x30/imm32/0 449 75/jump-if-!= $parse-hex-int-helper:loop/disp8 450 # . ++curr 451 41/increment-ecx 452 $parse-hex-int-helper:initial-0x: 453 # . if (curr >= in->end) return result 454 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx 455 73/jump-if-addr>= $parse-hex-int-helper:end/disp8 456 # . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again 457 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 458 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL 459 3d/compare-eax-and 0x78/imm32/x 460 75/jump-if-!= $parse-hex-int-helper:loop/disp8 461 # . ++curr 462 41/increment-ecx 463 $parse-hex-int-helper:loop: 464 # if (curr >= in->end) break 465 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx 466 73/jump-if-addr>= $parse-hex-int-helper:negate/disp8 467 # var eax: int = from-hex-char(*curr) 468 # . . copy arg to eax 469 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL 470 # . . call 471 e8/call from-hex-char/disp32 472 # result = result * 16 + eax 473 c1/shift 4/subop/left 3/mod/direct 3/rm32/ebx . . . . . 4/imm8 # shift ebx left by 4 bits 474 01/add 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # add eax to ebx 475 # ++curr 476 41/increment-ecx 477 # loop 478 eb/jump $parse-hex-int-helper:loop/disp8 479 $parse-hex-int-helper:negate: 480 # if (negate?) result = -result 481 81 7/subop/compare 3/mod/direct 6/rm32/esi . . . . . 0/imm32/false # compare esi 482 74/jump-if-= $parse-hex-int-helper:end/disp8 483 f7 3/subop/negate 3/mod/direct 3/rm32/ebx . . . . . . # negate ebx 484 $parse-hex-int-helper:end: 485 # return result 486 89/copy 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # copy ebx to eax 487 # . restore registers 488 5e/pop-to-esi 489 5b/pop-to-ebx 490 5a/pop-to-edx 491 59/pop-to-ecx 492 # . epilogue 493 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 494 5d/pop-to-ebp 495 c3/return 496 497 test-parse-hex-int-from-slice-single-digit: 498 # . prologue 499 55/push-ebp 500 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 501 # (eax..ecx) = "a" 502 b8/copy-to-eax "a"/imm32 503 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 504 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 505 05/add-to-eax 4/imm32 506 # var slice/ecx: slice = {eax, ecx} 507 51/push-ecx 508 50/push-eax 509 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 510 # eax = parse-hex-int-from-slice(slice) 511 # . . push args 512 51/push-ecx 513 # . . call 514 e8/call parse-hex-int-from-slice/disp32 515 # . . discard args 516 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 517 # check-ints-equal(eax, 0xa, msg) 518 # . . push args 519 68/push "F - test-parse-hex-int-from-slice-single-digit"/imm32 520 68/push 0xa/imm32 521 50/push-eax 522 # . . call 523 e8/call check-ints-equal/disp32 524 # . . discard args 525 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 526 # . epilogue 527 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 528 5d/pop-to-ebp 529 c3/return 530 531 test-parse-hex-int-from-slice-multi-digit: 532 # . prologue 533 55/push-ebp 534 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 535 # (eax..ecx) = "34a" 536 b8/copy-to-eax "34a"/imm32 537 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 538 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 539 05/add-to-eax 4/imm32 540 # var slice/ecx: slice = {eax, ecx} 541 51/push-ecx 542 50/push-eax 543 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 544 # eax = parse-hex-int-from-slice(slice) 545 # . . push args 546 51/push-ecx 547 # . . call 548 e8/call parse-hex-int-from-slice/disp32 549 # . . discard args 550 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 551 # check-ints-equal(eax, 0x34a, msg) 552 # . . push args 553 68/push "F - test-parse-hex-int-from-slice-multi-digit"/imm32 554 68/push 0x34a/imm32 555 50/push-eax 556 # . . call 557 e8/call check-ints-equal/disp32 558 # . . discard args 559 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 560 # . epilogue 561 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 562 5d/pop-to-ebp 563 c3/return 564 565 test-parse-hex-int-from-slice-0x-prefix: 566 # . prologue 567 55/push-ebp 568 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 569 # (eax..ecx) = "0x34" 570 b8/copy-to-eax "0x34"/imm32 571 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 572 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 573 05/add-to-eax 4/imm32 574 # var slice/ecx: slice = {eax, ecx} 575 51/push-ecx 576 50/push-eax 577 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 578 # eax = parse-hex-int-from-slice(slice) 579 # . . push args 580 51/push-ecx 581 # . . call 582 e8/call parse-hex-int-from-slice/disp32 583 # . . discard args 584 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 585 # check-ints-equal(eax, 0x34, msg) 586 # . . push args 587 68/push "F - test-parse-hex-int-from-slice-0x-prefix"/imm32 588 68/push 0x34/imm32 589 50/push-eax 590 # . . call 591 e8/call check-ints-equal/disp32 592 # . . discard args 593 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 594 # . epilogue 595 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 596 5d/pop-to-ebp 597 c3/return 598 599 test-parse-hex-int-from-slice-zero: 600 # . prologue 601 55/push-ebp 602 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 603 # (eax..ecx) = "0" 604 b8/copy-to-eax "0"/imm32 605 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 606 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 607 05/add-to-eax 4/imm32 608 # var slice/ecx: slice = {eax, ecx} 609 51/push-ecx 610 50/push-eax 611 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 612 # eax = parse-hex-int-from-slice(slice) 613 # . . push args 614 51/push-ecx 615 # . . call 616 e8/call parse-hex-int-from-slice/disp32 617 # . . discard args 618 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 619 # check-ints-equal(eax, 0, msg) 620 # . . push args 621 68/push "F - test-parse-hex-int-from-slice-zero"/imm32 622 68/push 0/imm32 623 50/push-eax 624 # . . call 625 e8/call check-ints-equal/disp32 626 # . . discard args 627 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 628 # . epilogue 629 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 630 5d/pop-to-ebp 631 c3/return 632 633 test-parse-hex-int-from-slice-0-prefix: 634 # . prologue 635 55/push-ebp 636 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 637 # (eax..ecx) = "03" 638 b8/copy-to-eax "03"/imm32 639 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 640 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 641 05/add-to-eax 4/imm32 642 # var slice/ecx: slice = {eax, ecx} 643 51/push-ecx 644 50/push-eax 645 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 646 # eax = parse-hex-int-from-slice(slice) 647 # . . push args 648 51/push-ecx 649 # . . call 650 e8/call parse-hex-int-from-slice/disp32 651 # . . discard args 652 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 653 # check-ints-equal(eax, 0x3, msg) 654 # . . push args 655 68/push "F - test-parse-hex-int-from-slice-0-prefix"/imm32 656 68/push 0x3/imm32 657 50/push-eax 658 # . . call 659 e8/call check-ints-equal/disp32 660 # . . discard args 661 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 662 # . epilogue 663 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 664 5d/pop-to-ebp 665 c3/return 666 667 test-parse-hex-int-from-slice-negative: 668 # . prologue 669 55/push-ebp 670 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 671 # (eax..ecx) = "-03" 672 b8/copy-to-eax "-03"/imm32 673 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 674 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 675 05/add-to-eax 4/imm32 676 # var slice/ecx: slice = {eax, ecx} 677 51/push-ecx 678 50/push-eax 679 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 680 # eax = parse-hex-int-from-slice(slice) 681 # . . push args 682 51/push-ecx 683 # . . call 684 e8/call parse-hex-int-from-slice/disp32 685 # . . discard args 686 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 687 # check-ints-equal(eax, -3, msg) 688 # . . push args 689 68/push "F - test-parse-hex-int-from-slice-negative"/imm32 690 68/push -3/imm32 691 50/push-eax 692 # . . call 693 e8/call check-ints-equal/disp32 694 # . . discard args 695 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 696 # . epilogue 697 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 698 5d/pop-to-ebp 699 c3/return 700 701 is-hex-digit?: # c: byte -> result/eax: boolean 702 # . prologue 703 55/push-ebp 704 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 705 # . save registers 706 51/push-ecx 707 # ecx = c 708 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx 709 # return false if c < '0' 710 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x30/imm32 # compare ecx 711 7c/jump-if-< $is-hex-digit?:false/disp8 712 # return true if c <= '9' 713 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x39/imm32 # compare ecx 714 7e/jump-if-<= $is-hex-digit?:true/disp8 715 # drop case 716 25/and-eax-with 0x5f/imm32 717 # return false if c > 'f' 718 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x66/imm32 # compare ecx 719 7f/jump-if-> $is-hex-digit?:false/disp8 720 # return true if c >= 'a' 721 81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x61/imm32 # compare ecx 722 7d/jump-if->= $is-hex-digit?:true/disp8 723 # otherwise return false 724 $is-hex-digit?:false: 725 b8/copy-to-eax 0/imm32/false 726 eb/jump $is-hex-digit?:end/disp8 727 $is-hex-digit?:true: 728 b8/copy-to-eax 1/imm32/true 729 $is-hex-digit?:end: 730 # . restore registers 731 59/pop-to-ecx 732 # . epilogue 733 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 734 5d/pop-to-ebp 735 c3/return 736 737 test-hex-below-0: 738 # eax = is-hex-digit?(0x2f) 739 # . . push args 740 68/push 0x2f/imm32 741 # . . call 742 e8/call is-hex-digit?/disp32 743 # . . discard args 744 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 745 # check-ints-equal(eax, 0, msg) 746 # . . push args 747 68/push "F - test-hex-below-0"/imm32 748 68/push 0/imm32/false 749 50/push-eax 750 # . . call 751 e8/call check-ints-equal/disp32 752 # . . discard args 753 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 754 c3/return 755 756 test-hex-0-to-9: 757 # eax = is-hex-digit?(0x30) 758 # . . push args 759 68/push 0x30/imm32 760 # . . call 761 e8/call is-hex-digit?/disp32 762 # . . discard args 763 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 764 # check-ints-equal(eax, 1, msg) 765 # . . push args 766 68/push "F - test-hex-at-0"/imm32 767 68/push 1/imm32/true 768 50/push-eax 769 # . . call 770 e8/call check-ints-equal/disp32 771 # . . discard args 772 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 773 # eax = is-hex-digit?(0x39) 774 # . . push args 775 68/push 0x39/imm32 776 # . . call 777 e8/call is-hex-digit?/disp32 778 # . . discard args 779 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 780 # check-ints-equal(eax, 1, msg) 781 # . . push args 782 68/push "F - test-hex-at-9"/imm32 783 68/push 1/imm32/true 784 50/push-eax 785 # . . call 786 e8/call check-ints-equal/disp32 787 # . . discard args 788 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 789 c3/return 790 791 test-hex-above-9-to-a: 792 # eax = is-hex-digit?(0x3a) 793 # . . push args 794 68/push 0x3a/imm32 795 # . . call 796 e8/call is-hex-digit?/disp32 797 # . . discard args 798 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 799 # check-ints-equal(eax, 0, msg) 800 # . . push args 801 68/push "F - test-hex-above-9-to-a"/imm32 802 68/push 0/imm32/false 803 50/push-eax 804 # . . call 805 e8/call check-ints-equal/disp32 806 # . . discard args 807 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 808 c3/return 809 810 test-hex-a-to-f: 811 # eax = is-hex-digit?(0x61) 812 # . . push args 813 68/push 0x61/imm32 814 # . . call 815 e8/call is-hex-digit?/disp32 816 # . . discard args 817 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 818 # check-ints-equal(eax, 1, msg) 819 # . . push args 820 68/push "F - test-hex-at-a"/imm32 821 68/push 1/imm32/true 822 50/push-eax 823 # . . call 824 e8/call check-ints-equal/disp32 825 # . . discard args 826 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 827 # eax = is-hex-digit?(0x66) 828 # . . push args 829 68/push 0x66/imm32 830 # . . call 831 e8/call is-hex-digit?/disp32 832 # . . discard args 833 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 834 # check-ints-equal(eax, 1, msg) 835 # . . push args 836 68/push "F - test-hex-at-f"/imm32 837 68/push 1/imm32/true 838 50/push-eax 839 # . . call 840 e8/call check-ints-equal/disp32 841 # . . discard args 842 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 843 c3/return 844 845 test-hex-above-f: 846 # eax = is-hex-digit?(0x67) 847 # . . push args 848 68/push 0x67/imm32 849 # . . call 850 e8/call is-hex-digit?/disp32 851 # . . discard args 852 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 853 # check-ints-equal(eax, 0, msg) 854 # . . push args 855 68/push "F - test-hex-above-f"/imm32 856 68/push 0/imm32/false 857 50/push-eax 858 # . . call 859 e8/call check-ints-equal/disp32 860 # . . discard args 861 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 862 c3/return 863 864 from-hex-char: # in/eax: byte -> out/eax: nibble 865 $from-hex-char:check0: 866 # if (eax < '0') goto abort 867 3d/compare-eax-with 0x30/imm32/0 868 7c/jump-if-< $from-hex-char:abort/disp8 869 $from-hex-char:check1: 870 # if (eax > 'f') goto abort 871 3d/compare-eax-with 0x66/imm32/f 872 7f/jump-if-> $from-hex-char:abort/disp8 873 $from-hex-char:check2: 874 # if (eax > '9') goto next check 875 3d/compare-eax-with 0x39/imm32/9 876 7f/jump-if-> $from-hex-char:check3/disp8 877 $from-hex-char:digit: 878 # return eax - '0' 879 2d/subtract-from-eax 0x30/imm32/0 880 c3/return 881 $from-hex-char:check3: 882 # if (eax < 'a') goto abort 883 3d/compare-eax-with 0x61/imm32/a 884 7c/jump-if-< $from-hex-char:abort/disp8 885 $from-hex-char:letter: 886 # return eax - ('a'-10) 887 2d/subtract-from-eax 0x57/imm32/a-10 888 c3/return 889 890 $from-hex-char:abort: 891 # . _write(2/stderr, error) 892 # . . push args 893 68/push "invalid hex char: "/imm32 894 68/push 2/imm32/stderr 895 # . . call 896 e8/call _write/disp32 897 # . . discard args 898 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 899 # . clear-stream($Stderr->buffer) 900 # . . save eax 901 50/push-eax 902 # . . push args 903 68/push $Stderr->buffer/imm32 904 # . . call 905 e8/call clear-stream/disp32 906 # . . discard args 907 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 908 # . . restore eax 909 58/pop-to-eax 910 # . write-int32-hex-buffered(Stderr, eax) 911 # . . push args 912 50/push-eax 913 68/push Stderr/imm32 914 # . . call 915 e8/call write-int32-hex-buffered/disp32 916 # . . discard args 917 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 918 # . flush(Stderr) 919 # . . push args 920 68/push Stderr/imm32 921 # . . call 922 e8/call flush/disp32 923 # . . discard args 924 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 925 # . _write(2/stderr, "\n") 926 # . . push args 927 68/push Newline/imm32 928 68/push 2/imm32/stderr 929 # . . call 930 e8/call _write/disp32 931 # . . discard args 932 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 933 # . syscall(exit, 1) 934 bb/copy-to-ebx 1/imm32 935 e8/call syscall_exit/disp32 936 # never gets here 937 938 # . . vim:nowrap:textwidth=0