about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-07-27 10:15:03 -0700
committerKartik Agaram <vc@akkartik.com>2018-07-27 10:15:03 -0700
commit3f4bbe9e5fd1389720c7adb5577edf2dc01d51e9 (patch)
tree61d80e20e59be711010dd4a1665afedc8d9bb93a
parent667d21177b9d8fe3652710b818d13940b23511d7 (diff)
downloadmu-3f4bbe9e5fd1389720c7adb5577edf2dc01d51e9.tar.gz
4434
Key core data structures by hex bytes in text rather than opcode
numbers. Saves us round trips of having to parse and reparse strings,
and also allows us to more easily ignore unexpected non-hex words in
each transform. We'll use this ability next when we start inserting
labels.
-rw-r--r--subx/010vm.cc26
-rw-r--r--subx/011run.cc2
-rw-r--r--subx/012direct_addressing.cc50
-rw-r--r--subx/013indirect_addressing.cc18
-rw-r--r--subx/014immediate_addressing.cc32
-rw-r--r--subx/016jump_relative.cc14
-rw-r--r--subx/017jump_relative.cc14
-rw-r--r--subx/018functions.cc4
-rw-r--r--subx/019syscalls.cc2
-rw-r--r--subx/022check_instruction.cc242
-rw-r--r--subx/024pack_instructions.cc2
11 files changed, 209 insertions, 197 deletions
diff --git a/subx/010vm.cc b/subx/010vm.cc
index 4d13f27d..5ea40bbc 100644
--- a/subx/010vm.cc
+++ b/subx/010vm.cc
@@ -193,29 +193,29 @@ inline uint8_t next() {
 
 //: start tracking supported opcodes
 :(before "End Globals")
-map</*op*/uint8_t, string> name;
-map</*op*/uint8_t, string> name_0f;
-map</*op*/uint8_t, string> name_f3;
-map</*op*/uint8_t, string> name_f3_0f;
+map</*op*/string, string> name;
+map</*op*/string, string> name_0f;
+map</*op*/string, string> name_f3;
+map</*op*/string, string> name_f3_0f;
 :(before "End One-time Setup")
 init_op_names();
 :(code)
 void init_op_names() {
-  put(name, 0xf4, "halt");
+  put(name, "f4", "halt");
   // End Initialize Op Names(name)
 }
 
 :(before "End Help Special-cases(key)")
 if (key == "opcodes") {
   cerr << "Opcodes currently supported by SubX:\n";
-  for (map<uint8_t, string>::iterator p = name.begin();  p != name.end();  ++p)
-    cerr << "  " << HEXBYTE << NUM(p->first) << ": " << p->second << '\n';
-  for (map<uint8_t, string>::iterator p = name_0f.begin();  p != name_0f.end();  ++p)
-    cerr << "  0f " << HEXBYTE << NUM(p->first) << ": " << p->second << '\n';
-  for (map<uint8_t, string>::iterator p = name_f3.begin();  p != name_f3.end();  ++p)
-    cerr << "  f3 " << HEXBYTE << NUM(p->first) << ": " << p->second << '\n';
-  for (map<uint8_t, string>::iterator p = name_f3_0f.begin();  p != name_f3_0f.end();  ++p)
-    cerr << "  f3 0f " << HEXBYTE << NUM(p->first) << ": " << p->second << '\n';
+  for (map<string, string>::iterator p = name.begin();  p != name.end();  ++p)
+    cerr << "  " << p->first << ": " << p->second << '\n';
+  for (map<string, string>::iterator p = name_0f.begin();  p != name_0f.end();  ++p)
+    cerr << "  0f " << p->first << ": " << p->second << '\n';
+  for (map<string, string>::iterator p = name_f3.begin();  p != name_f3.end();  ++p)
+    cerr << "  f3 " << p->first << ": " << p->second << '\n';
+  for (map<string, string>::iterator p = name_f3_0f.begin();  p != name_f3_0f.end();  ++p)
+    cerr << "  f3 0f " << p->first << ": " << p->second << '\n';
   cerr << "Run `subx help instructions` for details on words like 'r32' and 'disp8'.\n";
   return 0;
 }
diff --git a/subx/011run.cc b/subx/011run.cc
index 906a8305..2de87a72 100644
--- a/subx/011run.cc
+++ b/subx/011run.cc
@@ -231,7 +231,7 @@ uint8_t hex_byte(const string& s) {
 //:: run
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x05, "add imm32 to R0 (EAX)");
+put(name, "05", "add imm32 to R0 (EAX)");
 
 //: our first opcode
 :(before "End Single-Byte Opcodes")
diff --git a/subx/012direct_addressing.cc b/subx/012direct_addressing.cc
index 601f7ab2..66d34195 100644
--- a/subx/012direct_addressing.cc
+++ b/subx/012direct_addressing.cc
@@ -1,7 +1,7 @@
 //: operating directly on a register
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x01, "add r32 to rm32");
+put(name, "01", "add r32 to rm32");
 
 :(scenario add_r32_to_r32)
 % Reg[EAX].i = 0x10;
@@ -64,7 +64,7 @@ string rname(uint8_t r) {
 //:: subtract
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x29, "subtract r32 from rm32");
+put(name, "29", "subtract r32 from rm32");
 
 :(scenario subtract_r32_from_r32)
 % Reg[EAX].i = 10;
@@ -90,7 +90,7 @@ case 0x29: {  // subtract r32 from r/m32
 //:: and
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x21, "rm32 = bitwise AND of r32 with rm32");
+put(name, "21", "rm32 = bitwise AND of r32 with rm32");
 
 :(scenario and_r32_with_r32)
 % Reg[EAX].i = 0x0a0b0c0d;
@@ -116,7 +116,7 @@ case 0x21: {  // and r32 with r/m32
 //:: or
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x09, "rm32 = bitwise OR of r32 with rm32");
+put(name, "09", "rm32 = bitwise OR of r32 with rm32");
 
 :(scenario or_r32_with_r32)
 % Reg[EAX].i = 0x0a0b0c0d;
@@ -142,7 +142,7 @@ case 0x09: {  // or r32 with r/m32
 //:: xor
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x31, "rm32 = bitwise XOR of r32 with rm32");
+put(name, "31", "rm32 = bitwise XOR of r32 with rm32");
 
 :(scenario xor_r32_with_r32)
 % Reg[EAX].i = 0x0a0b0c0d;
@@ -168,7 +168,7 @@ case 0x31: {  // xor r32 with r/m32
 //:: not
 
 :(before "End Initialize Op Names(name)")
-put(name, 0xf7, "bitwise complement of rm32");
+put(name, "f7", "bitwise complement of rm32");
 
 :(scenario not_r32)
 % Reg[EBX].i = 0x0f0f00ff;
@@ -196,7 +196,7 @@ case 0xf7: {  // xor r32 with r/m32
 //:: compare (cmp)
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x39, "set SF if rm32 < r32");
+put(name, "39", "set SF if rm32 < r32");
 
 :(scenario compare_r32_with_r32_greater)
 % Reg[EAX].i = 0x0a0b0c0d;
@@ -250,7 +250,7 @@ case 0x39: {  // set SF if r/m32 < r32
 //:: copy (mov)
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x89, "copy r32 to rm32");
+put(name, "89", "copy r32 to rm32");
 
 :(scenario copy_r32_to_r32)
 % Reg[EBX].i = 0xaf;
@@ -276,7 +276,7 @@ case 0x89: {  // copy r32 to r/m32
 //:: xchg
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x87, "swap the contents of r32 and rm32");
+put(name, "87", "swap the contents of r32 and rm32");
 
 :(scenario xchg_r32_with_r32)
 % Reg[EBX].i = 0xaf;
@@ -307,14 +307,14 @@ case 0x87: {  // exchange r32 with r/m32
 //:: push
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x50, "push R0 (EAX) to stack");
-put(name, 0x51, "push R1 (ECX) to stack");
-put(name, 0x52, "push R2 (EDX) to stack");
-put(name, 0x53, "push R3 (EBX) to stack");
-put(name, 0x54, "push R4 (ESP) to stack");
-put(name, 0x55, "push R5 (EBP) to stack");
-put(name, 0x56, "push R6 (ESI) to stack");
-put(name, 0x57, "push R7 (EDI) to stack");
+put(name, "50", "push R0 (EAX) to stack");
+put(name, "51", "push R1 (ECX) to stack");
+put(name, "52", "push R2 (EDX) to stack");
+put(name, "53", "push R3 (EBX) to stack");
+put(name, "54", "push R4 (ESP) to stack");
+put(name, "55", "push R5 (EBP) to stack");
+put(name, "56", "push R6 (ESI) to stack");
+put(name, "57", "push R7 (EDI) to stack");
 
 :(scenario push_r32)
 % Reg[ESP].u = 0x64;
@@ -351,14 +351,14 @@ void push(uint32_t val) {
 //:: pop
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x58, "pop top of stack to R0 (EAX)");
-put(name, 0x59, "pop top of stack to R1 (ECX)");
-put(name, 0x5a, "pop top of stack to R2 (EDX)");
-put(name, 0x5b, "pop top of stack to R3 (EBX)");
-put(name, 0x5c, "pop top of stack to R4 (ESP)");
-put(name, 0x5d, "pop top of stack to R5 (EBP)");
-put(name, 0x5e, "pop top of stack to R6 (ESI)");
-put(name, 0x5f, "pop top of stack to R7 (EDI)");
+put(name, "58", "pop top of stack to R0 (EAX)");
+put(name, "59", "pop top of stack to R1 (ECX)");
+put(name, "5a", "pop top of stack to R2 (EDX)");
+put(name, "5b", "pop top of stack to R3 (EBX)");
+put(name, "5c", "pop top of stack to R4 (ESP)");
+put(name, "5d", "pop top of stack to R5 (EBP)");
+put(name, "5e", "pop top of stack to R6 (ESI)");
+put(name, "5f", "pop top of stack to R7 (EDI)");
 
 :(scenario pop_r32)
 % Reg[ESP].u = 0x60;
diff --git a/subx/013indirect_addressing.cc b/subx/013indirect_addressing.cc
index c192b35b..e908e17c 100644
--- a/subx/013indirect_addressing.cc
+++ b/subx/013indirect_addressing.cc
@@ -28,7 +28,7 @@ case 0:  // indirect addressing
 //:
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x03, "add rm32 to r32");
+put(name, "03", "add rm32 to r32");
 
 :(scenario add_mem_at_r32_to_r32)
 % Reg[EAX].i = 0x60;
@@ -71,7 +71,7 @@ case 0x03: {  // add r/m32 to r32
 //:
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x2b, "subtract rm32 from r32");
+put(name, "2b", "subtract rm32 from r32");
 
 :(scenario subtract_mem_at_r32_from_r32)
 % Reg[EAX].i = 0x60;
@@ -114,7 +114,7 @@ case 0x2b: {  // subtract r/m32 from r32
 //:
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x23, "r32 = bitwise AND of r32 with rm32");
+put(name, "23", "r32 = bitwise AND of r32 with rm32");
 
 :(scenario and_mem_at_r32_with_r32)
 % Reg[EAX].i = 0x60;
@@ -157,7 +157,7 @@ case 0x23: {  // and r/m32 with r32
 //:
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x0b, "r32 = bitwise OR of r32 with rm32");
+put(name, "0b", "r32 = bitwise OR of r32 with rm32");
 
 :(scenario or_mem_at_r32_with_r32)
 % Reg[EAX].i = 0x60;
@@ -200,7 +200,7 @@ case 0x0b: {  // or r/m32 with r32
 //:
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x33, "r32 = bitwise XOR of r32 with rm32");
+put(name, "33", "r32 = bitwise XOR of r32 with rm32");
 
 :(scenario xor_mem_at_r32_with_r32)
 % Reg[EAX].i = 0x60;
@@ -283,7 +283,7 @@ ff 00 0f 0f  # 0x0f0f00ff
 //:
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x3b, "set SF if rm32 > r32");
+put(name, "3b", "set SF if rm32 > r32");
 
 :(scenario compare_r32_with_mem_at_r32_greater)
 % Reg[EAX].i = 0x60;
@@ -356,7 +356,7 @@ case 0x3b: {  // set SF if r32 < r/m32
 //:
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x8b, "copy rm32 to r32");
+put(name, "8b", "copy rm32 to r32");
 
 :(scenario copy_mem_at_r32_to_r32)
 % Reg[EAX].i = 0x60;
@@ -384,7 +384,7 @@ case 0x8b: {  // copy r32 to r/m32
 //:: jump
 
 :(before "End Initialize Op Names(name)")
-put(name, 0xff, "jump/push/call rm32 based on subop");
+put(name, "ff", "jump/push/call rm32 based on subop");
 
 :(scenario jump_mem_at_r32)
 % Reg[EAX].i = 0x60;
@@ -447,7 +447,7 @@ case 6: {  // push r/m32 to stack
 //:: pop
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x8f, "pop top of stack to rm32");
+put(name, "8f", "pop top of stack to rm32");
 
 :(scenario pop_mem_at_r32)
 % Reg[EAX].i = 0x60;
diff --git a/subx/014immediate_addressing.cc b/subx/014immediate_addressing.cc
index 9a04215f..8c308a80 100644
--- a/subx/014immediate_addressing.cc
+++ b/subx/014immediate_addressing.cc
@@ -1,7 +1,7 @@
 //: instructions that (immediately) contain an argument to act with
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x81, "combine rm32 with imm32 based on subop");
+put(name, "81", "combine rm32 with imm32 based on subop");
 
 :(scenario add_imm32_to_r32)
 % Reg[EBX].i = 1;
@@ -52,7 +52,7 @@ case 0x81: {  // combine imm32 with r/m32
 //:: subtract
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x2d, "subtract imm32 from R0 (EAX)");
+put(name, "2d", "subtract imm32 from R0 (EAX)");
 
 :(scenario subtract_imm32_from_eax)
 % Reg[EAX].i = 0x0d0c0baa;
@@ -108,7 +108,7 @@ case 5: {
 //:: and
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x25, "R0 = bitwise AND of imm32 with R0 (EAX)");
+put(name, "25", "R0 = bitwise AND of imm32 with R0 (EAX)");
 
 :(scenario and_imm32_with_eax)
 % Reg[EAX].i = 0xff;
@@ -164,7 +164,7 @@ case 4: {
 //:: or
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x0d, "R0 = bitwise OR of imm32 with R0 (EAX)");
+put(name, "0d", "R0 = bitwise OR of imm32 with R0 (EAX)");
 
 :(scenario or_imm32_with_eax)
 % Reg[EAX].i = 0xd0c0b0a0;
@@ -218,7 +218,7 @@ case 1: {
 //:: xor
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x35, "R0 = bitwise XOR of imm32 with R0 (EAX)");
+put(name, "35", "R0 = bitwise XOR of imm32 with R0 (EAX)");
 
 :(scenario xor_imm32_with_eax)
 % Reg[EAX].i = 0xddccb0a0;
@@ -272,7 +272,7 @@ case 6: {
 //:: compare (cmp)
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x3d, "subtract imm32 from R0 (EAX)");
+put(name, "3d", "subtract imm32 from R0 (EAX)");
 
 :(scenario compare_imm32_with_eax_greater)
 % Reg[EAX].i = 0x0d0c0b0a;
@@ -396,14 +396,14 @@ case 7: {
 //:: copy (mov)
 
 :(before "End Initialize Op Names(name)")
-put(name, 0xb8, "copy imm32 to R0 (EAX)");
-put(name, 0xb9, "copy imm32 to R1 (ECX)");
-put(name, 0xba, "copy imm32 to R2 (EDX)");
-put(name, 0xbb, "copy imm32 to R3 (EBX)");
-put(name, 0xbc, "copy imm32 to R4 (ESP)");
-put(name, 0xbd, "copy imm32 to R5 (EBP)");
-put(name, 0xbe, "copy imm32 to R6 (ESI)");
-put(name, 0xbf, "copy imm32 to R7 (EDI)");
+put(name, "b8", "copy imm32 to R0 (EAX)");
+put(name, "b9", "copy imm32 to R1 (ECX)");
+put(name, "ba", "copy imm32 to R2 (EDX)");
+put(name, "bb", "copy imm32 to R3 (EBX)");
+put(name, "bc", "copy imm32 to R4 (ESP)");
+put(name, "bd", "copy imm32 to R5 (EBP)");
+put(name, "be", "copy imm32 to R6 (ESI)");
+put(name, "bf", "copy imm32 to R7 (EDI)");
 
 :(scenario copy_imm32_to_r32)
 == 0x1
@@ -430,7 +430,7 @@ case 0xbf: {  // copy imm32 to r32
 //:
 
 :(before "End Initialize Op Names(name)")
-put(name, 0xc7, "copy imm32 to rm32");
+put(name, "c7", "copy imm32 to rm32");
 
 :(scenario copy_imm32_to_mem_at_r32)
 % Reg[EBX].i = 0x60;
@@ -454,7 +454,7 @@ case 0xc7: {  // copy imm32 to r32
 //:: push
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x68, "push imm32 to stack");
+put(name, "68", "push imm32 to stack");
 
 :(scenario push_imm32)
 % Reg[ESP].u = 0x14;
diff --git a/subx/016jump_relative.cc b/subx/016jump_relative.cc
index 9b411f07..48105414 100644
--- a/subx/016jump_relative.cc
+++ b/subx/016jump_relative.cc
@@ -3,7 +3,7 @@
 //:: jump
 
 :(before "End Initialize Op Names(name)")
-put(name, 0xeb, "jump disp8 bytes away");
+put(name, "eb", "jump disp8 bytes away");
 
 :(scenario jump_rel8)
 == 0x1
@@ -27,7 +27,7 @@ case 0xeb: {  // jump rel8
 //:: jump if equal/zero
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x74, "jump disp8 bytes away if ZF is set");
+put(name, "74", "jump disp8 bytes away if ZF is set");
 
 :(scenario je_rel8_success)
 % ZF = true;
@@ -66,7 +66,7 @@ case 0x74: {  // jump rel8 if ZF
 //:: jump if not equal/not zero
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x75, "jump disp8 bytes away if ZF is not set");
+put(name, "75", "jump disp8 bytes away if ZF is not set");
 
 :(scenario jne_rel8_success)
 % ZF = false;
@@ -105,7 +105,7 @@ case 0x75: {  // jump rel8 unless ZF
 //:: jump if greater
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x7f, "jump disp8 bytes away if greater (ZF is unset, SF == OF)");
+put(name, "7f", "jump disp8 bytes away if greater (ZF is unset, SF == OF)");
 
 :(scenario jg_rel8_success)
 % ZF = false;
@@ -148,7 +148,7 @@ case 0x7f: {  // jump rel8 if !SF and !ZF
 //:: jump if greater or equal
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x7d, "jump disp8 bytes away if greater or equal (SF == OF)");
+put(name, "7d", "jump disp8 bytes away if greater or equal (SF == OF)");
 
 :(scenario jge_rel8_success)
 % SF = false;
@@ -189,7 +189,7 @@ case 0x7d: {  // jump rel8 if !SF
 //:: jump if lesser
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x7c, "jump disp8 bytes away if lesser (SF != OF)");
+put(name, "7c", "jump disp8 bytes away if lesser (SF != OF)");
 
 :(scenario jl_rel8_success)
 % ZF = false;
@@ -232,7 +232,7 @@ case 0x7c: {  // jump rel8 if SF and !ZF
 //:: jump if lesser or equal
 
 :(before "End Initialize Op Names(name)")
-put(name, 0x7e, "jump disp8 bytes away if lesser or equal (ZF is set or SF != OF)");
+put(name, "7e", "jump disp8 bytes away if lesser or equal (ZF is set or SF != OF)");
 
 :(scenario jle_rel8_equal)
 % ZF = true;
diff --git a/subx/017jump_relative.cc b/subx/017jump_relative.cc
index 5164b044..e13c1ac7 100644
--- a/subx/017jump_relative.cc
+++ b/subx/017jump_relative.cc
@@ -3,7 +3,7 @@
 //:: jump
 
 :(before "End Initialize Op Names(name)")
-put(name, 0xe9, "jump disp16 bytes away");
+put(name, "e9", "jump disp16 bytes away");
 
 :(scenario jump_rel16)
 == 0x1
@@ -33,7 +33,7 @@ int16_t imm16() {
 //:: jump if equal/zero
 
 :(before "End Initialize Op Names(name)")
-put(name_0f, 0x84, "jump disp16 bytes away if ZF is set");
+put(name_0f, "84", "jump disp16 bytes away if ZF is set");
 
 :(scenario je_rel16_success)
 % ZF = true;
@@ -72,7 +72,7 @@ case 0x84: {  // jump rel16 if ZF
 //:: jump if not equal/not zero
 
 :(before "End Initialize Op Names(name)")
-put(name_0f, 0x85, "jump disp16 bytes away if ZF is not set");
+put(name_0f, "85", "jump disp16 bytes away if ZF is not set");
 
 :(scenario jne_rel16_success)
 % ZF = false;
@@ -111,7 +111,7 @@ case 0x85: {  // jump rel16 unless ZF
 //:: jump if greater
 
 :(before "End Initialize Op Names(name)")
-put(name_0f, 0x8f, "jump disp16 bytes away if greater (ZF is unset, SF == OF)");
+put(name_0f, "8f", "jump disp16 bytes away if greater (ZF is unset, SF == OF)");
 
 :(scenario jg_rel16_success)
 % ZF = false;
@@ -154,7 +154,7 @@ case 0x8f: {  // jump rel16 if !SF and !ZF
 //:: jump if greater or equal
 
 :(before "End Initialize Op Names(name)")
-put(name_0f, 0x8d, "jump disp16 bytes away if greater or equal (SF == OF)");
+put(name_0f, "8d", "jump disp16 bytes away if greater or equal (SF == OF)");
 
 :(scenario jge_rel16_success)
 % SF = false;
@@ -195,7 +195,7 @@ case 0x8d: {  // jump rel16 if !SF
 //:: jump if lesser
 
 :(before "End Initialize Op Names(name)")
-put(name_0f, 0x8c, "jump disp16 bytes away if lesser (SF != OF)");
+put(name_0f, "8c", "jump disp16 bytes away if lesser (SF != OF)");
 
 :(scenario jl_rel16_success)
 % ZF = false;
@@ -238,7 +238,7 @@ case 0x8c: {  // jump rel16 if SF and !ZF
 //:: jump if lesser or equal
 
 :(before "End Initialize Op Names(name)")
-put(name_0f, 0x8e, "jump disp16 bytes away if lesser or equal (ZF is set or SF != OF)");
+put(name_0f, "8e", "jump disp16 bytes away if lesser or equal (ZF is set or SF != OF)");
 
 :(scenario jle_rel16_equal)
 % ZF = true;
diff --git a/subx/018functions.cc b/subx/018functions.cc
index ac6df740..d7401716 100644
--- a/subx/018functions.cc
+++ b/subx/018functions.cc
@@ -1,7 +1,7 @@
 //:: call
 
 :(before "End Initialize Op Names(name)")
-put(name, 0xe8, "call disp32");
+put(name, "e8", "call disp32");
 
 :(scenario call_disp32)
 % Reg[ESP].u = 0x64;
@@ -67,7 +67,7 @@ a0 00 00 00  # 0xa0
 //:: ret
 
 :(before "End Initialize Op Names(name)")
-put(name, 0xc3, "return from most recent unfinished call");
+put(name, "c3", "return from most recent unfinished call");
 
 :(scenario ret)
 % Reg[ESP].u = 0x60;
diff --git a/subx/019syscalls.cc b/subx/019syscalls.cc
index 77a8d698..33972417 100644
--- a/subx/019syscalls.cc
+++ b/subx/019syscalls.cc
@@ -1,5 +1,5 @@
 :(before "End Initialize Op Names(name)")
-put(name, 0xcd, "software interrupt");
+put(name, "cd", "software interrupt");
 
 :(before "End Single-Byte Opcodes")
 case 0xcd: {  // int imm8 (software interrupt)
diff --git a/subx/022check_instruction.cc b/subx/022check_instruction.cc
index 93087b9a..c4b169ff 100644
--- a/subx/022check_instruction.cc
+++ b/subx/022check_instruction.cc
@@ -75,21 +75,23 @@ void check_operands(/*const*/ program& p) {
 }
 
 void check_operands(const line& inst) {
-  uint8_t op = hex_byte(inst.words.at(0).data);
-  if (trace_contains_errors()) return;
-  if (op == 0x0f) {
+  word op = preprocess_op(inst.words.at(0));
+  if (op.data == "0f") {
     check_operands_0f(inst);
     return;
   }
-  if (op == 0xf3) {
+  if (op.data == "f3") {
     check_operands_f3(inst);
     return;
   }
-  if (!contains_key(name, op)) {
-    raise << "unknown opcode '" << HEXBYTE << NUM(op) << "'\n" << end();
-    return;
-  }
-  check_operands(op, inst);
+  check_operands(inst, op);
+}
+
+word preprocess_op(word/*copy*/ op) {
+  op.data = tolower(op.data.c_str());
+  if (starts_with(op.data, "0x"))
+    op.data = op.data.substr(2);
+  return op;
 }
 
 //: To check the operands for an opcode, we'll track the permitted operands
@@ -129,130 +131,130 @@ void init_op_types() {
 }
 
 :(before "End Globals")
-map</*op*/uint8_t, /*bitvector*/uint8_t> Permitted_operands;
-const uint8_t INVALID_OPERANDS = 0xff;  // no instruction uses all the operands
+map</*op*/string, /*bitvector*/uint8_t> Permitted_operands;
+const uint8_t INVALID_OPERANDS = 0xff;  // no instruction uses all the operand types
 :(before "End One-time Setup")
 init_permitted_operands();
 :(code)
 void init_permitted_operands() {
   //// Class A: just op, no operands
   // halt
-  put(Permitted_operands, 0xf4, 0x00);
+  put(Permitted_operands, "f4", 0x00);
   // push
-  put(Permitted_operands, 0x50, 0x00);
-  put(Permitted_operands, 0x51, 0x00);
-  put(Permitted_operands, 0x52, 0x00);
-  put(Permitted_operands, 0x53, 0x00);
-  put(Permitted_operands, 0x54, 0x00);
-  put(Permitted_operands, 0x55, 0x00);
-  put(Permitted_operands, 0x56, 0x00);
-  put(Permitted_operands, 0x57, 0x00);
+  put(Permitted_operands, "50", 0x00);
+  put(Permitted_operands, "51", 0x00);
+  put(Permitted_operands, "52", 0x00);
+  put(Permitted_operands, "53", 0x00);
+  put(Permitted_operands, "54", 0x00);
+  put(Permitted_operands, "55", 0x00);
+  put(Permitted_operands, "56", 0x00);
+  put(Permitted_operands, "57", 0x00);
   // pop
-  put(Permitted_operands, 0x58, 0x00);
-  put(Permitted_operands, 0x59, 0x00);
-  put(Permitted_operands, 0x5a, 0x00);
-  put(Permitted_operands, 0x5b, 0x00);
-  put(Permitted_operands, 0x5c, 0x00);
-  put(Permitted_operands, 0x5d, 0x00);
-  put(Permitted_operands, 0x5e, 0x00);
-  put(Permitted_operands, 0x5f, 0x00);
+  put(Permitted_operands, "58", 0x00);
+  put(Permitted_operands, "59", 0x00);
+  put(Permitted_operands, "5a", 0x00);
+  put(Permitted_operands, "5b", 0x00);
+  put(Permitted_operands, "5c", 0x00);
+  put(Permitted_operands, "5d", 0x00);
+  put(Permitted_operands, "5e", 0x00);
+  put(Permitted_operands, "5f", 0x00);
   // return
-  put(Permitted_operands, 0xc3, 0x00);
+  put(Permitted_operands, "c3", 0x00);
 
   //// Class B: just op and disp8
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  0     0     0      |0       1     0     0
 
   // jump
-  put(Permitted_operands, 0xeb, 0x04);
-  put(Permitted_operands, 0x74, 0x04);
-  put(Permitted_operands, 0x75, 0x04);
-  put(Permitted_operands, 0x7c, 0x04);
-  put(Permitted_operands, 0x7d, 0x04);
-  put(Permitted_operands, 0x7e, 0x04);
-  put(Permitted_operands, 0x7f, 0x04);
+  put(Permitted_operands, "eb", 0x04);
+  put(Permitted_operands, "74", 0x04);
+  put(Permitted_operands, "75", 0x04);
+  put(Permitted_operands, "7c", 0x04);
+  put(Permitted_operands, "7d", 0x04);
+  put(Permitted_operands, "7e", 0x04);
+  put(Permitted_operands, "7f", 0x04);
 
   //// Class C: just op and disp16
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  0     0     0      |1       0     0     0
-  put(Permitted_operands, 0xe8, 0x08);  // jump
+  put(Permitted_operands, "e8", 0x08);  // jump
 
   //// Class D: just op and disp32
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  0     0     1      |0       0     0     0
-  put(Permitted_operands, 0xe9, 0x10);  // call
+  put(Permitted_operands, "e9", 0x10);  // call
 
   //// Class E: just op and imm8
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  0     1     0      |0       0     0     0
-  put(Permitted_operands, 0xcd, 0x20);  // software interrupt
+  put(Permitted_operands, "cd", 0x20);  // software interrupt
 
   //// Class F: just op and imm32
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  1     0     0      |0       0     0     0
-  put(Permitted_operands, 0x05, 0x40);  // add
-  put(Permitted_operands, 0x2d, 0x40);  // subtract
-  put(Permitted_operands, 0x25, 0x40);  // and
-  put(Permitted_operands, 0x0d, 0x40);  // or
-  put(Permitted_operands, 0x35, 0x40);  // xor
-  put(Permitted_operands, 0x3d, 0x40);  // compare
-  put(Permitted_operands, 0x68, 0x40);  // push
+  put(Permitted_operands, "05", 0x40);  // add
+  put(Permitted_operands, "2d", 0x40);  // subtract
+  put(Permitted_operands, "25", 0x40);  // and
+  put(Permitted_operands, "0d", 0x40);  // or
+  put(Permitted_operands, "35", 0x40);  // xor
+  put(Permitted_operands, "3d", 0x40);  // compare
+  put(Permitted_operands, "68", 0x40);  // push
   // copy
-  put(Permitted_operands, 0xb8, 0x40);
-  put(Permitted_operands, 0xb9, 0x40);
-  put(Permitted_operands, 0xba, 0x40);
-  put(Permitted_operands, 0xbb, 0x40);
-  put(Permitted_operands, 0xbc, 0x40);
-  put(Permitted_operands, 0xbd, 0x40);
-  put(Permitted_operands, 0xbe, 0x40);
-  put(Permitted_operands, 0xbf, 0x40);
+  put(Permitted_operands, "b8", 0x40);
+  put(Permitted_operands, "b9", 0x40);
+  put(Permitted_operands, "ba", 0x40);
+  put(Permitted_operands, "bb", 0x40);
+  put(Permitted_operands, "bc", 0x40);
+  put(Permitted_operands, "bd", 0x40);
+  put(Permitted_operands, "be", 0x40);
+  put(Permitted_operands, "bf", 0x40);
 
   //// Class M: using ModR/M byte
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  0     0     0      |0       0     0     1
 
   // add
-  put(Permitted_operands, 0x01, 0x01);
-  put(Permitted_operands, 0x03, 0x01);
+  put(Permitted_operands, "01", 0x01);
+  put(Permitted_operands, "03", 0x01);
   // subtract
-  put(Permitted_operands, 0x29, 0x01);
-  put(Permitted_operands, 0x2b, 0x01);
+  put(Permitted_operands, "29", 0x01);
+  put(Permitted_operands, "2b", 0x01);
   // and
-  put(Permitted_operands, 0x21, 0x01);
-  put(Permitted_operands, 0x23, 0x01);
+  put(Permitted_operands, "21", 0x01);
+  put(Permitted_operands, "23", 0x01);
   // or
-  put(Permitted_operands, 0x09, 0x01);
-  put(Permitted_operands, 0x0b, 0x01);
+  put(Permitted_operands, "09", 0x01);
+  put(Permitted_operands, "0b", 0x01);
   // complement
-  put(Permitted_operands, 0xf7, 0x01);
+  put(Permitted_operands, "f7", 0x01);
   // xor
-  put(Permitted_operands, 0x31, 0x01);
-  put(Permitted_operands, 0x33, 0x01);
+  put(Permitted_operands, "31", 0x01);
+  put(Permitted_operands, "33", 0x01);
   // compare
-  put(Permitted_operands, 0x39, 0x01);
-  put(Permitted_operands, 0x3b, 0x01);
+  put(Permitted_operands, "39", 0x01);
+  put(Permitted_operands, "3b", 0x01);
   // copy
-  put(Permitted_operands, 0x89, 0x01);
-  put(Permitted_operands, 0x8b, 0x01);
+  put(Permitted_operands, "89", 0x01);
+  put(Permitted_operands, "8b", 0x01);
   // swap
-  put(Permitted_operands, 0x87, 0x01);
+  put(Permitted_operands, "87", 0x01);
   // pop
-  put(Permitted_operands, 0x8f, 0x01);
+  put(Permitted_operands, "8f", 0x01);
 
   //// Class O: op, ModR/M and subop (not r32)
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  0     0     0      |0       0     1     1
-  put(Permitted_operands, 0xff, 0x03);  // jump/push/call
+  put(Permitted_operands, "ff", 0x03);  // jump/push/call
 
   //// Class N: op, ModR/M and imm32
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  1     0     0      |0       0     0     1
-  put(Permitted_operands, 0xc7, 0x41);  // copy
+  put(Permitted_operands, "c7", 0x41);  // copy
 
   //// Class P: op, ModR/M, subop (not r32) and imm32
   //  imm32 imm8  disp32 |disp16  disp8 subop modrm
   //  1     0     0      |0       0     1     1
-  put(Permitted_operands, 0x81, 0x43);  // combine
+  put(Permitted_operands, "81", 0x43);  // combine
 
   // End Init Permitted Operands
 }
@@ -263,17 +265,17 @@ void init_permitted_operands() {
 #define CLEAR(bitvector, bit)  ((bitvector) & (~(1 << (bit))))
 
 :(code)
-void check_operands(uint8_t op, const line& inst) {
-  uint8_t expected_bitvector = get(Permitted_operands, op);
+void check_operands(const line& inst, const word& op) {
+  uint8_t expected_bitvector = get(Permitted_operands, op.data);
   if (HAS(expected_bitvector, MODRM))
     check_operands_modrm(inst, op);
-  compare_bitvector(op, inst, CLEAR(expected_bitvector, MODRM));
+  compare_bitvector(inst, CLEAR(expected_bitvector, MODRM), op);
 }
 
 //: Many instructions can be checked just by comparing bitvectors.
 
-void compare_bitvector(uint8_t op, const line& inst, uint8_t expected) {
-  if (all_raw_hex_bytes(inst) && has_operands(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
+void compare_bitvector(const line& inst, uint8_t expected, const word& op) {
+  if (all_hex_bytes(inst) && has_operands(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
   uint8_t bitvector = compute_operand_bitvector(inst);
   if (trace_contains_errors()) return;  // duplicate operand type
   if (bitvector == expected) return;  // all good with this instruction
@@ -282,14 +284,30 @@ void compare_bitvector(uint8_t op, const line& inst, uint8_t expected) {
     if ((bitvector & 0x1) == (expected & 0x1)) continue;  // all good with this operand
     const string& optype = Operand_type_name.at(i);
     if ((bitvector & 0x1) > (expected & 0x1))
-      raise << "'" << to_string(inst) << "' (" << get(name, op) << "): unexpected " << optype << " operand\n" << end();
+      raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end();
     else
-      raise << "'" << to_string(inst) << "' (" << get(name, op) << "): missing " << optype << " operand\n" << end();
+      raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end();
     // continue giving all errors for a single instruction
   }
   // ignore settings in any unused bits
 }
 
+string maybe_name(const word& op) {
+  if (!is_hex_byte(op)) return "";
+  if (!contains_key(name, op.data)) return "";
+  return " ("+get(name, op.data)+')';
+}
+
+bool is_hex_byte(const word& curr) {
+  if (contains_any_operand_metadata(curr))
+    return false;
+  if (SIZE(curr.data) != 2)
+    return false;
+  if (curr.data.find_first_not_of("0123456789abcdefABCDEF") != string::npos)
+    return false;
+  return true;
+}
+
 uint32_t compute_operand_bitvector(const line& inst) {
   uint32_t bitvector = 0;
   for (int i = /*skip op*/1;  i < SIZE(inst.words);  ++i) {
@@ -314,16 +332,10 @@ int first_operand(const line& inst) {
   return 1;
 }
 
-bool all_raw_hex_bytes(const line& inst) {
-  for (int i = 0;  i < SIZE(inst.words);  ++i) {
-    const word& curr = inst.words.at(i);
-    if (contains_any_operand_metadata(curr))
-      return false;
-    if (SIZE(curr.data) != 2)
-      return false;
-    if (curr.data.find_first_not_of("0123456789abcdefABCDEF") != string::npos)
+bool all_hex_bytes(const line& inst) {
+  for (int i = 0;  i < SIZE(inst.words);  ++i)
+    if (!is_hex_byte(inst.words.at(i)))
       return false;
-  }
   return true;
 }
 
@@ -384,12 +396,12 @@ Instruction_operands.insert("imm8");
 Instruction_operands.insert("imm32");
 
 :(code)
-void check_operands_modrm(const line& inst, uint8_t op) {
-  if (all_raw_hex_bytes(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
+void check_operands_modrm(const line& inst, const word& op) {
+  if (all_hex_bytes(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
   check_metadata_present(inst, "mod", op);
   check_metadata_present(inst, "rm32", op);
   // no check for r32; some instructions don't use it; just assume it's 0 if missing
-  if (op == 0x81 || op == 0x8f || op == 0xff) {  // keep sync'd with 'help subop'
+  if (op.data == "81" || op.data == "8f" || op.data == "ff") {  // keep sync'd with 'help subop'
     check_metadata_present(inst, "subop", op);
     check_metadata_absent(inst, "r32", op, "should be replaced by subop");
   }
@@ -408,14 +420,14 @@ void check_operands_modrm(const line& inst, uint8_t op) {
   // no check for scale; 0 (2**0 = 1) by default
 }
 
-void check_metadata_present(const line& inst, const string& type, uint8_t op) {
+void check_metadata_present(const line& inst, const string& type, const word& op) {
   if (!has_metadata(inst, type))
-    raise << "'" << to_string(inst) << "' (" << get(name, op) << "): missing " << type << " operand\n" << end();
+    raise << "'" << to_string(inst) << "' (" << get(name, op.data) << "): missing " << type << " operand\n" << end();
 }
 
-void check_metadata_absent(const line& inst, const string& type, uint8_t op, const string& msg) {
+void check_metadata_absent(const line& inst, const string& type, const word& op, const string& msg) {
   if (has_metadata(inst, type))
-    raise << "'" << to_string(inst) << "' (" << get(name, op) << "): unexpected " << type << " operand (" << msg << ")\n" << end();
+    raise << "'" << to_string(inst) << "' (" << get(name, op.data) << "): unexpected " << type << " operand (" << msg << ")\n" << end();
 }
 
 bool has_metadata(const line& inst, const string& m) {
@@ -510,12 +522,12 @@ void check_operands_0f(const line& inst) {
     raise << "opcode '0f' requires a second opcode\n" << end();
     return;
   }
-  uint8_t op = hex_byte(inst.words.at(1).data);
-  if (!contains_key(name_0f, op)) {
-    raise << "unknown 2-byte opcode '0f " << HEXBYTE << NUM(op) << "'\n" << end();
+  word op = preprocess_op(inst.words.at(1));
+  if (!contains_key(name_0f, op.data)) {
+    raise << "unknown 2-byte opcode '0f " << op.data << "'\n" << end();
     return;
   }
-  check_operands_0f(op, inst);
+  check_operands_0f(inst, op);
 }
 
 void check_operands_f3(const line& /*unused*/) {
@@ -532,28 +544,28 @@ void check_operands_f3(const line& /*unused*/) {
 +error: '0f 84' (jump disp16 bytes away if ZF is set): missing disp16 operand
 
 :(before "End Globals")
-map</*op*/uint8_t, /*bitvector*/uint8_t> Permitted_operands_0f;
+map</*op*/string, /*bitvector*/uint8_t> Permitted_operands_0f;
 :(before "End Init Permitted Operands")
 //// Class C: just op and disp16
 //  imm32 imm8  disp32 |disp16  disp8 subop modrm
 //  0     0     0      |1       0     0     0
-put(Permitted_operands_0f, 0x84, 0x08);
-put(Permitted_operands_0f, 0x85, 0x08);
-put(Permitted_operands_0f, 0x8c, 0x08);
-put(Permitted_operands_0f, 0x8d, 0x08);
-put(Permitted_operands_0f, 0x8e, 0x08);
-put(Permitted_operands_0f, 0x8f, 0x08);
+put(Permitted_operands_0f, "84", 0x08);
+put(Permitted_operands_0f, "85", 0x08);
+put(Permitted_operands_0f, "8c", 0x08);
+put(Permitted_operands_0f, "8d", 0x08);
+put(Permitted_operands_0f, "8e", 0x08);
+put(Permitted_operands_0f, "8f", 0x08);
 
 :(code)
-void check_operands_0f(uint8_t op, const line& inst) {
-  uint8_t expected_bitvector = get(Permitted_operands_0f, op);
+void check_operands_0f(const line& inst, const word& op) {
+  uint8_t expected_bitvector = get(Permitted_operands_0f, op.data);
   if (HAS(expected_bitvector, MODRM))
     check_operands_modrm(inst, op);
-  compare_bitvector_0f(op, inst, CLEAR(expected_bitvector, MODRM));
+  compare_bitvector_0f(inst, CLEAR(expected_bitvector, MODRM), op);
 }
 
-void compare_bitvector_0f(uint8_t op, const line& inst, uint8_t expected) {
-  if (all_raw_hex_bytes(inst) && has_operands(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
+void compare_bitvector_0f(const line& inst, uint8_t expected, const word& op) {
+  if (all_hex_bytes(inst) && has_operands(inst)) return;  // deliberately programming in raw hex; we'll raise a warning elsewhere
   uint8_t bitvector = compute_operand_bitvector(inst);
   if (trace_contains_errors()) return;  // duplicate operand type
   if (bitvector == expected) return;  // all good with this instruction
@@ -562,9 +574,9 @@ void compare_bitvector_0f(uint8_t op, const line& inst, uint8_t expected) {
     if ((bitvector & 0x1) == (expected & 0x1)) continue;  // all good with this operand
     const string& optype = Operand_type_name.at(i);
     if ((bitvector & 0x1) > (expected & 0x1))
-      raise << "'" << to_string(inst) << "' (" << get(name_0f, op) << "): unexpected " << optype << " operand\n" << end();
+      raise << "'" << to_string(inst) << "' (" << get(name_0f, op.data) << "): unexpected " << optype << " operand\n" << end();
     else
-      raise << "'" << to_string(inst) << "' (" << get(name_0f, op) << "): missing " << optype << " operand\n" << end();
+      raise << "'" << to_string(inst) << "' (" << get(name_0f, op.data) << "): missing " << optype << " operand\n" << end();
     // continue giving all errors for a single instruction
   }
   // ignore settings in any unused bits
diff --git a/subx/024pack_instructions.cc b/subx/024pack_instructions.cc
index a897f1da..bbaa7369 100644
--- a/subx/024pack_instructions.cc
+++ b/subx/024pack_instructions.cc
@@ -50,7 +50,7 @@ void pack_instructions(program& p) {
   segment& code = p.segments.at(0);
   for (int i = 0;  i < SIZE(code.lines);  ++i) {
     line& inst = code.lines.at(i);
-    if (all_raw_hex_bytes(inst)) continue;
+    if (all_hex_bytes(inst)) continue;
     trace(99, "translate") << "packing instruction '" << to_string(/*with metadata*/inst) << "'" << end();
     pack_instruction(inst);
     trace(99, "translate") << "instruction after packing: '" << to_string(/*without metadata*/inst.words) << "'" << end();