about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2018-07-20 17:08:55 -0700
committerKartik Agaram <vc@akkartik.com>2018-07-20 17:08:55 -0700
commit0ca791cd8cb84f71da011b3f3827c61126011492 (patch)
tree8f6b27c56284c267dff5e2c2f6c122fa9bf220eb
parent3fa78f1c16714cf39e388757a046ed4e010ef2b9 (diff)
downloadmu-0ca791cd8cb84f71da011b3f3827c61126011492.tar.gz
4376 - subx: online help includes supported opcodes
-rw-r--r--subx/001help.cc8
-rw-r--r--subx/010vm.cc12
-rw-r--r--subx/011parse.cc23
-rw-r--r--subx/012direct_addressing.cc47
-rw-r--r--subx/013indirect_addressing.cc27
-rw-r--r--subx/014immediate_addressing.cc34
-rw-r--r--subx/016jump_relative.cc21
-rw-r--r--subx/017jump_relative.cc3
8 files changed, 167 insertions, 8 deletions
diff --git a/subx/001help.cc b/subx/001help.cc
index e6e0527d..340f7d33 100644
--- a/subx/001help.cc
+++ b/subx/001help.cc
@@ -15,12 +15,14 @@ if (is_equal(argv[1], "help")) {
     help_contents();
     return 0;
   }
-  else if (contains_key(Help, argv[2])) {
-    cerr << get(Help, argv[2]);
+  string key(argv[2]);
+  // End Help Special-cases(key)
+  if (contains_key(Help, key)) {
+    cerr << get(Help, key);
     return 0;
   }
   else {
-    cerr << "No help found for '" << argv[2] << "'\n";
+    cerr << "No help found for '" << key << "'\n";
     help_contents();
     cerr << "Please check your command for typos.\n";
     return 1;
diff --git a/subx/010vm.cc b/subx/010vm.cc
index 67c407a8..9d6d047d 100644
--- a/subx/010vm.cc
+++ b/subx/010vm.cc
@@ -31,12 +31,14 @@ EIP = 1;  // preserve null pointer
 cerr << "  registers\n";
 :(before "End Help Texts")
 put(Help, "registers",
-  "SubX currently supports 8 integer registers, numbered from 0 to 7.\n"
+  "SubX currently supports eight 32-bit integer registers: R0 to R7.\n"
+  "R4 contains the top of the stack.\n"
   "There's also a register for the address of the currently executing instruction. It is modified by jumps.\n"
-  "Various instructions modify one or more of 3 flags, as a side-effect:\n"
-  "- the sign flag: usually set if an arithmetic result is negative, or reset if not.\n"
-  "- the zero flag: usually set if a result is zero, or reset if not.\n"
-  "- the overflow flag: usually set if an arithmetic result overflows.\n"
+  "Various instructions modify one or more of three 1-bit 'flag' registers, as a side-effect:\n"
+  "- the sign flag (SF): usually set if an arithmetic result is negative, or reset if not.\n"
+  "- the zero flag (ZF): usually set if a result is zero, or reset if not.\n"
+  "- the overflow flag (OF): usually set if an arithmetic result overflows.\n"
+  "The flag bits are read by conditional jumps.\n"
   "We don't support non-integer (floating-point) registers yet.\n"
 );
 
diff --git a/subx/011parse.cc b/subx/011parse.cc
index b5914d2c..c6d66ff4 100644
--- a/subx/011parse.cc
+++ b/subx/011parse.cc
@@ -225,3 +225,26 @@ int32_t imm32() {
   result |= (next()<<24);
   return result;
 }
+
+//: start tracking supported opcodes
+:(before "End Globals")
+map</*op*/uint8_t, string> name;
+:(before "End One-time Setup")
+init_op_names();
+:(code)
+void init_op_names() {
+  put(name, 0xf4, "halt");
+  put(name, 0x05, "add imm32 to register R0 (EAX)");
+  // 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';
+  cerr << "Coming soon: `subx help operands` for details on words like 'r32' and 'disp8'.\n";
+  return 0;
+}
+:(before "End Help Contents")
+cerr << "  opcodes\n";
diff --git a/subx/012direct_addressing.cc b/subx/012direct_addressing.cc
index bdad57ab..61eb3d9d 100644
--- a/subx/012direct_addressing.cc
+++ b/subx/012direct_addressing.cc
@@ -1,5 +1,8 @@
 //: operating directly on a register
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x01, "add r32 to rm32");
+
 :(scenario add_r32_to_r32)
 % Reg[EAX].i = 0x10;
 % Reg[EBX].i = 1;
@@ -60,6 +63,9 @@ string rname(uint8_t r) {
 
 //:: subtract
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x29, "subtract r32 from rm32");
+
 :(scenario subtract_r32_from_r32)
 % Reg[EAX].i = 10;
 % Reg[EBX].i = 1;
@@ -83,6 +89,9 @@ case 0x29: {  // subtract r32 from r/m32
 
 //:: and
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x21, "rm32 = bitwise AND of r32 with rm32");
+
 :(scenario and_r32_with_r32)
 % Reg[EAX].i = 0x0a0b0c0d;
 % Reg[EBX].i = 0x000000ff;
@@ -106,6 +115,9 @@ case 0x21: {  // and r32 with r/m32
 
 //:: or
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x09, "rm32 = bitwise OR of r32 with rm32");
+
 :(scenario or_r32_with_r32)
 % Reg[EAX].i = 0x0a0b0c0d;
 % Reg[EBX].i = 0xa0b0c0d0;
@@ -129,6 +141,9 @@ case 0x09: {  // or r32 with r/m32
 
 //:: xor
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x31, "rm32 = bitwise XOR of r32 with rm32");
+
 :(scenario xor_r32_with_r32)
 % Reg[EAX].i = 0x0a0b0c0d;
 % Reg[EBX].i = 0xaabbc0d0;
@@ -152,6 +167,9 @@ case 0x31: {  // xor r32 with r/m32
 
 //:: not
 
+:(before "End Initialize Op Names(name)")
+put(name, 0xf7, "bitwise complement of rm32");
+
 :(scenario not_r32)
 % Reg[EBX].i = 0x0f0f00ff;
 == 0x1
@@ -177,6 +195,9 @@ case 0xf7: {  // xor r32 with r/m32
 
 //:: compare (cmp)
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x39, "set SF if rm32 < r32");
+
 :(scenario compare_r32_with_r32_greater)
 % Reg[EAX].i = 0x0a0b0c0d;
 % Reg[EBX].i = 0x0a0b0c07;
@@ -228,6 +249,9 @@ case 0x39: {  // set SF if r/m32 < r32
 
 //:: copy (mov)
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x89, "copy r32 to rm32");
+
 :(scenario copy_r32_to_r32)
 % Reg[EBX].i = 0xaf;
 == 0x1
@@ -251,6 +275,9 @@ case 0x89: {  // copy r32 to r/m32
 
 //:: xchg
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x01, "swap the contents of r32 and rm32");
+
 :(scenario xchg_r32_with_r32)
 % Reg[EBX].i = 0xaf;
 % Reg[EAX].i = 0x2e;
@@ -279,6 +306,16 @@ 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");
+
 :(scenario push_r32)
 % Reg[ESP].u = 0x64;
 % Reg[EBX].i = 0x0000000a;
@@ -313,6 +350,16 @@ 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)");
+
 :(scenario pop_r32)
 % Reg[ESP].u = 0x60;
 % write_mem_i32(0x60, 0x0000000a);
diff --git a/subx/013indirect_addressing.cc b/subx/013indirect_addressing.cc
index 232b67de..4480e809 100644
--- a/subx/013indirect_addressing.cc
+++ b/subx/013indirect_addressing.cc
@@ -27,6 +27,9 @@ case 0:  // indirect addressing
 
 //:
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x03, "add rm32 to r32");
+
 :(scenario add_mem_at_r32_to_r32)
 % Reg[EAX].i = 0x60;
 % Reg[EBX].i = 0x10;
@@ -67,6 +70,9 @@ case 0x03: {  // add r/m32 to r32
 
 //:
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x2b, "subtract rm32 from r32");
+
 :(scenario subtract_mem_at_r32_from_r32)
 % Reg[EAX].i = 0x60;
 % Reg[EBX].i = 10;
@@ -107,6 +113,9 @@ case 0x2b: {  // subtract r/m32 from r32
 
 //:
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x23, "r32 = bitwise AND of r32 with rm32");
+
 :(scenario and_mem_at_r32_with_r32)
 % Reg[EAX].i = 0x60;
 % Reg[EBX].i = 0x0a0b0c0d;
@@ -147,6 +156,9 @@ case 0x23: {  // and r/m32 with r32
 
 //:
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x0b, "r32 = bitwise OR of r32 with rm32");
+
 :(scenario or_mem_at_r32_with_r32)
 % Reg[EAX].i = 0x60;
 % Reg[EBX].i = 0xa0b0c0d0;
@@ -187,6 +199,9 @@ case 0x0b: {  // or r/m32 with r32
 
 //:
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x33, "r32 = bitwise XOR of r32 with rm32");
+
 :(scenario xor_mem_at_r32_with_r32)
 % Reg[EAX].i = 0x60;
 % Reg[EBX].i = 0xa0b0c0d0;
@@ -267,6 +282,9 @@ ff 00 0f 0f  # 0x0f0f00ff
 
 //:
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x3b, "set SF if rm32 > r32");
+
 :(scenario compare_r32_with_mem_at_r32_greater)
 % Reg[EAX].i = 0x60;
 % Reg[EBX].i = 0x0a0b0c0d;
@@ -337,6 +355,9 @@ case 0x3b: {  // set SF if r32 < r/m32
 
 //:
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x8b, "copy rm32 to r32");
+
 :(scenario copy_mem_at_r32_to_r32)
 % Reg[EAX].i = 0x60;
 == 0x1  # code segment
@@ -362,6 +383,9 @@ case 0x8b: {  // copy r32 to r/m32
 
 //:: jump
 
+:(before "End Initialize Op Names(name)")
+put(name, 0xff, "jump/push/call rm32 depending on subop");
+
 :(scenario jump_mem_at_r32)
 % Reg[EAX].i = 0x60;
 == 0x1  # code segment
@@ -422,6 +446,9 @@ case 6: {  // push r/m32 to stack
 
 //:: pop
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x8f, "pop top of stack to rm32");
+
 :(scenario pop_mem_at_r32)
 % Reg[EAX].i = 0x60;
 % Reg[ESP].u = 0x10;
diff --git a/subx/014immediate_addressing.cc b/subx/014immediate_addressing.cc
index 11e825b8..9a04215f 100644
--- a/subx/014immediate_addressing.cc
+++ b/subx/014immediate_addressing.cc
@@ -1,5 +1,8 @@
 //: 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");
+
 :(scenario add_imm32_to_r32)
 % Reg[EBX].i = 1;
 == 0x1
@@ -48,6 +51,9 @@ case 0x81: {  // combine imm32 with r/m32
 
 //:: subtract
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x2d, "subtract imm32 from R0 (EAX)");
+
 :(scenario subtract_imm32_from_eax)
 % Reg[EAX].i = 0x0d0c0baa;
 == 0x1
@@ -101,6 +107,9 @@ case 5: {
 
 //:: and
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x25, "R0 = bitwise AND of imm32 with R0 (EAX)");
+
 :(scenario and_imm32_with_eax)
 % Reg[EAX].i = 0xff;
 == 0x1
@@ -154,6 +163,9 @@ case 4: {
 
 //:: or
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x0d, "R0 = bitwise OR of imm32 with R0 (EAX)");
+
 :(scenario or_imm32_with_eax)
 % Reg[EAX].i = 0xd0c0b0a0;
 == 0x1
@@ -205,6 +217,9 @@ case 1: {
 
 //:: xor
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x35, "R0 = bitwise XOR of imm32 with R0 (EAX)");
+
 :(scenario xor_imm32_with_eax)
 % Reg[EAX].i = 0xddccb0a0;
 == 0x1
@@ -256,6 +271,9 @@ case 6: {
 
 //:: compare (cmp)
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x3d, "subtract imm32 from R0 (EAX)");
+
 :(scenario compare_imm32_with_eax_greater)
 % Reg[EAX].i = 0x0d0c0b0a;
 == 0x1
@@ -377,6 +395,16 @@ 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)");
+
 :(scenario copy_imm32_to_r32)
 == 0x1
 # op  ModR/M  SIB   displacement  immediate
@@ -401,6 +429,9 @@ case 0xbf: {  // copy imm32 to r32
 
 //:
 
+:(before "End Initialize Op Names(name)")
+put(name, 0xc7, "copy imm32 to rm32");
+
 :(scenario copy_imm32_to_mem_at_r32)
 % Reg[EBX].i = 0x60;
 == 0x1
@@ -422,6 +453,9 @@ case 0xc7: {  // copy imm32 to r32
 
 //:: push
 
+:(before "End Initialize Op Names(name)")
+put(name, 0x68, "push imm32 to stack");
+
 :(scenario push_imm32)
 % Reg[ESP].u = 0x14;
 == 0x1
diff --git a/subx/016jump_relative.cc b/subx/016jump_relative.cc
index 061a947f..9b411f07 100644
--- a/subx/016jump_relative.cc
+++ b/subx/016jump_relative.cc
@@ -2,6 +2,9 @@
 
 //:: jump
 
+:(before "End Initialize Op Names(name)")
+put(name, 0xeb, "jump disp8 bytes away");
+
 :(scenario jump_rel8)
 == 0x1
 # op  ModR/M  SIB   displacement  immediate
@@ -23,6 +26,9 @@ 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");
+
 :(scenario je_rel8_success)
 % ZF = true;
 == 0x1
@@ -59,6 +65,9 @@ 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");
+
 :(scenario jne_rel8_success)
 % ZF = false;
 == 0x1
@@ -95,6 +104,9 @@ 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)");
+
 :(scenario jg_rel8_success)
 % ZF = false;
 % SF = false;
@@ -135,6 +147,9 @@ 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)");
+
 :(scenario jge_rel8_success)
 % SF = false;
 % OF = false;
@@ -173,6 +188,9 @@ 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)");
+
 :(scenario jl_rel8_success)
 % ZF = false;
 % SF = true;
@@ -213,6 +231,9 @@ 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)");
+
 :(scenario jle_rel8_equal)
 % ZF = true;
 % SF = false;
diff --git a/subx/017jump_relative.cc b/subx/017jump_relative.cc
index 711a5b6a..d7553df7 100644
--- a/subx/017jump_relative.cc
+++ b/subx/017jump_relative.cc
@@ -2,6 +2,9 @@
 
 //:: jump
 
+:(before "End Initialize Op Names(name)")
+put(name, 0xe9, "jump disp16 bytes away");
+
 :(scenario jump_rel16)
 == 0x1
 # op  ModR/M  SIB   displacement  immediate