4376 - subx: online help includes supported opcodes

This commit is contained in:
Kartik Agaram 2018-07-20 17:08:55 -07:00
parent 3fa78f1c16
commit 0ca791cd8c
8 changed files with 167 additions and 8 deletions

View File

@ -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;

View File

@ -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"
);

View File

@ -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";

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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