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 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>=  $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-!=  $hex-int?:initial-0/disp8
 33     # . ++curr
 34     41/increment-ecx
 35     # skip past leading '0x'
 36 $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-!=  $hex-int?:loop/disp8
 42     # . ++curr
 43     41/increment-ecx
 44 $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>=  $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-!=  $hex-int?:loop/disp8
 53     # . ++curr
 54     41/increment-ecx
 55 $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>=  $hex-int?:true/disp8
 59     # var eax: boolean = 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  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-=  $hex-int?:end/disp8
 70     # ++curr
 71     41/increment-ecx
 72     # loop
 73     eb/jump  $hex-int?:loop/disp8
 74 $hex-int?:true:
 75     # return true
 76     b8/copy-to-eax  1/imm32/true
 77 $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-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 = hex-int?(slice)
101     # . . push args
102     51/push-ecx
103     # . . call
104     e8/call  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-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   .               .                 :number <- copy 1:literal
  6:array:number <- copy 5:address:array:number/deref
]
+mem: storing 3 in location 6
+mem: storing 14 in location 7
+mem: storing 15 in location 8
+mem: storing 16 in location 9

//: disable the size mismatch check since the destination array need not be initialized
:(after "bool size_mismatch(const reagent& x, const vector<double>& data)")
if (x.types.at(0) == Type_number["array"]) return false;
:(after "long long int size_of(const reagent& r)")
  if (r.types.at(0) == Type_number["array"]) {
    assert(SIZE(r.types) > 1);
    // skip the 'array' type to get at the element type
    return 1 + Memory[r.value]*size_of(array_element(r.types));
  }

//:: To access elements of an array, use 'index'

:(scenario index)
recipe main [
  1:number <- copy 3:literal  # length
  2:number <- copy 14:literal
  3:number <- copy 15:literal
  4:number <- copy 16:literal
  5:number <- index 1:array:number/raw, 0:literal  # unsafe
]
+mem: storing 14 in location 5

:(scenario index_direct_offset)
recipe main [
  1:number <- copy 3:literal  # length
  2:number <- copy 14:literal
  3:number <- copy 15:literal
  4:number <- copy 16:literal
  5:number <- copy 0:literal
  6:number <- index 1:array:number/raw, 5:number  # unsafe
]
+mem: storing 14 in location 6

:(before "End Primitive Recipe Declarations")
INDEX,
:(before "End Primitive Recipe Numbers")
Recipe_number["index"] = INDEX;
:(before "End Primitive Recipe Implementations")
case INDEX: {
//?   if (Trace_stream) Trace_stream->dump_layer = "run"; //? 1
  reagent base = canonize(current_instruction().ingredients.at(0));
//?   trace(Primitive_recipe_depth, "run") << "ingredient 0 after canonize: " << base.to_string(); //? 1
  long long int base_address = base.value;
  assert(base.types.at(0) == Type_number["array"]);
  reagent offset = canonize(current_instruction().ingredients.at(1));
//?   trace(Primitive_recipe_depth, "run") << "ingredient 1 after canonize: " << offset.to_string(); //? 1
  vector<double> offset_val(read_memory(offset));
  vector<type_number> element_type = array_element(base.types);
//?   trace(Primitive_recipe_depth, "run") << "offset: " << offset_val.at(0); //? 1
//?   trace(Primitive_recipe_depth, "run") << "size of elements: " << size_of(element_type); //? 1
  long long int src = base_address + 1 + offset_val.at(0)*size_of(element_type);
  trace(Primitive_recipe_depth, "run") << "address to copy is " << src;
  trace(Primitive_recipe_depth, "run") << "its type is " << Type[element_type.at(0)].name;
  reagent tmp;
  tmp.set_value(src);
  copy(element_type.begin(), element_type.end(), inserter(tmp.types, tmp.types.begin()));
  products.push_back(read_memory(tmp));
  break;
}

:(code)
vector<type_number> array_element(const vector<type_number>& types) {
  return vector<type_number>(++types.begin(), types.end());
}

:(scenario index_address)
recipe main [
  1:number <- copy 3:literal  # length
  2:number <- copy 14:literal
  3:number <- copy 15:literal
  4:number <- copy 16:literal
  5:number <- index-address 1:array:number/raw, 0:literal  # unsafe
]
+mem: storing 2 in location 5

//:: To write to elements of containers, you need their address.

:(scenario index_indirect)
recipe main [
  1:number <- copy 3:literal  # length
  2:number <- copy 14:literal
  3:number <- copy 15:literal
  4:number <- copy 16:literal
  5:address:array:number <- copy 1:literal
  6:number <- index 5:address:array:number/deref, 1:literal
]
+mem: storing 15 in location 6

:(before "End Primitive Recipe Declarations")
INDEX_ADDRESS,
:(before "End Primitive Recipe Numbers")
Recipe_number["index-address"] = INDEX_ADDRESS;
:(before "End Primitive Recipe Implementations")
case INDEX_ADDRESS: {
  reagent base = canonize(current_instruction().ingredients.at(0));
  long long int base_address = base.value;
  assert(base.types.at(0) == Type_number["array"]);
  reagent offset = canonize(current_instruction().ingredients.at(1));
  vector<double> offset_val(read_memory(offset));
  vector<type_number> element_type = array_element(base.types);
  long long int result = base_address + 1 + offset_val.at(0)*size_of(element_type);
  products.resize(1);
  products.at(0).push_back(result);
  break;
}

//:: compute the length of an array

:(scenario array_length)
recipe main [
  1:number <- copy 3:literal  # length
  2:number <- copy 14:literal
  3:number <- copy 15:literal
  4:number <- copy 16:literal
  5:number <- length 1:array:number/raw  # unsafe
]
+mem: storing 3 in location 5

:(before "End Primitive Recipe Declarations")
LENGTH,
:(before "End Primitive Recipe Numbers")
Recipe_number["length"] = LENGTH;
:(before "End Primitive Recipe Implementations")
case LENGTH: {
  reagent x = canonize(current_instruction().ingredients.at(0));
  if (x.types.at(0) != Type_number["array"]) {
    raise << "tried to calculate length of non-array " << x.to_string() << '\n';
    break;
  }
  products.resize(1);
  products.at(0).push_back(Memory[x.value]);
  break;
}
. # 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 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-< $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-<= $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-> $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->= $hex-digit?:true/disp8 723 # otherwise return false 724 $hex-digit?:false: 725 b8/copy-to-eax 0/imm32/false 726 eb/jump $hex-digit?:end/disp8 727 $hex-digit?:true: 728 b8/copy-to-eax 1/imm32/true 729 $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 = hex-digit?(0x2f) 739 # . . push args 740 68/push 0x2f/imm32 741 # . . call 742 e8/call 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 = hex-digit?(0x30) 758 # . . push args 759 68/push 0x30/imm32 760 # . . call 761 e8/call 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 = hex-digit?(0x39) 774 # . . push args 775 68/push 0x39/imm32 776 # . . call 777 e8/call 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 = hex-digit?(0x3a) 793 # . . push args 794 68/push 0x3a/imm32 795 # . . call 796 e8/call 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 = hex-digit?(0x61) 812 # . . push args 813 68/push 0x61/imm32 814 # . . call 815 e8/call 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 = hex-digit?(0x66) 828 # . . push args 829 68/push 0x66/imm32 830 # . . call 831 e8/call 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 = hex-digit?(0x67) 847 # . . push args 848 68/push 0x67/imm32 849 # . . call 850 e8/call 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 (abort "invalid hex char") 892 # never gets here 893 894 # . . vim:nowrap:textwidth=0