about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-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();