From 03dcb7c9400cf6dcf04deb8e1bfa804a9621d0b4 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Fri, 13 Oct 2017 21:53:00 -0700 Subject: 4062 --- html/subx/010core.cc.html | 366 +++++++++++++++--------------- html/subx/011direct_addressing.cc.html | 38 ++-- html/subx/012indirect_addressing.cc.html | 34 +-- html/subx/013immediate_addressing.cc.html | 70 +++--- subx/010core.cc | 4 + 5 files changed, 260 insertions(+), 252 deletions(-) diff --git a/html/subx/010core.cc.html b/html/subx/010core.cc.html index f6772607..6b0d2643 100644 --- a/html/subx/010core.cc.html +++ b/html/subx/010core.cc.html @@ -98,190 +98,194 @@ if ('onhashchange' in window) { 35 //: how the flag registers are updated after each instruction 36 37 :(before "End Includes") - 38 // beware: no side-effects in args - 39 #define BINARY_ARITHMETIC_OP(op, arg1, arg2) { \ - 40 /* arg1 and arg2 must be signed */ \ - 41 int64_t tmp = arg1 op arg2; \ - 42 arg1 = arg1 op arg2; \ - 43 trace(2, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ - 44 SF = (arg1 < 0); \ - 45 ZF = (arg1 == 0); \ - 46 OF = (arg1 != tmp); \ - 47 } - 48 - 49 #define BINARY_BITWISE_OP(op, arg1, arg2) { \ - 50 /* arg1 and arg2 must be unsigned */ \ - 51 arg1 = arg1 op arg2; \ - 52 trace(2, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ - 53 SF = (arg1 >> 31); \ - 54 ZF = (arg1 == 0); \ - 55 OF = false; \ - 56 } - 57 - 58 //:: simulated RAM - 59 - 60 :(before "End Globals") - 61 vector<uint8_t> Mem; - 62 uint32_t End_of_program = 0; - 63 :(before "End Reset") - 64 Mem.clear(); - 65 Mem.resize(1024); - 66 End_of_program = 0; - 67 - 68 //:: core interpreter loop - 69 - 70 :(scenario add_imm32_to_eax) - 71 # In scenarios, programs are a series of hex bytes, each (variable-length) - 72 # instruction on one line. - 73 # - 74 # x86 instructions consist of the following parts (see cheatsheet.pdf): - 75 # opcode ModR/M SIB displacement immediate - 76 # instruction mod, reg, Reg/Mem bits scale, index, base - 77 # 1-3 bytes 0/1 byte 0/1 byte 0/1/2/4 bytes 0/1/2/4 bytes - 78 ¦ 05 0a 0b 0c 0d # add 0x0d0c0b0a to EAX - 79 # All hex bytes must be exactly 2 characters each. No '0x' prefixes. - 80 +load: 1 -> 05 - 81 +load: 2 -> 0a - 82 +load: 3 -> 0b - 83 +load: 4 -> 0c - 84 +load: 5 -> 0d - 85 +run: add imm32 0x0d0c0b0a to reg EAX - 86 +run: storing 0x0d0c0b0a - 87 - 88 :(code) - 89 // helper for tests: load a program into memory from a textual representation - 90 // of its bytes, and run it - 91 void run(const string& text_bytes) { - 92 load_program(text_bytes); - 93 EIP = 1; // preserve null pointer - 94 while (EIP < End_of_program) - 95 ¦ run_one_instruction(); - 96 } - 97 - 98 // skeleton of how x86 instructions are decoded - 99 void run_one_instruction() { -100 uint8_t op=0, op2=0, op3=0; -101 switch (op = next()) { -102 case 0xf4: // hlt -103 ¦ EIP = End_of_program; -104 ¦ break; -105 // our first opcode -106 case 0x05: { // add imm32 to EAX -107 ¦ int32_t arg2 = imm32(); -108 ¦ trace(2, "run") << "add imm32 0x" << HEXWORD << arg2 << " to reg EAX" << end(); -109 ¦ BINARY_ARITHMETIC_OP(+, Reg[EAX].i, arg2); -110 ¦ break; -111 } -112 // End Single-Byte Opcodes -113 case 0x0f: -114 ¦ switch(op2 = next()) { -115 ¦ // End Two-Byte Opcodes Starting With 0f -116 ¦ default: -117 ¦ ¦ cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n'; -118 ¦ ¦ exit(1); -119 ¦ } -120 ¦ break; -121 case 0xf3: -122 ¦ switch(op2 = next()) { -123 ¦ // End Two-Byte Opcodes Starting With f3 -124 ¦ case 0x0f: -125 ¦ ¦ switch(op3 = next()) { -126 ¦ ¦ // End Three-Byte Opcodes Starting With f3 0f -127 ¦ ¦ default: -128 ¦ ¦ ¦ cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n'; -129 ¦ ¦ ¦ exit(1); -130 ¦ ¦ } -131 ¦ ¦ break; -132 ¦ default: -133 ¦ ¦ cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n'; -134 ¦ ¦ exit(1); -135 ¦ } -136 ¦ break; -137 default: -138 ¦ cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n'; -139 ¦ exit(1); -140 } -141 } -142 -143 void load_program(const string& text_bytes) { -144 uint32_t addr = 1; -145 istringstream in(text_bytes); -146 in >> std::noskipws; -147 while (has_data(in)) { -148 ¦ char c1 = next_hex_byte(in); -149 ¦ if (c1 == '\0') break; -150 ¦ if (!has_data(in)) { -151 ¦ ¦ raise << "input program truncated mid-byte\n" << end(); -152 ¦ ¦ return; -153 ¦ } -154 ¦ char c2 = next_hex_byte(in); -155 ¦ if (c2 == '\0') { -156 ¦ ¦ raise << "input program truncated mid-byte\n" << end(); -157 ¦ ¦ return; -158 ¦ } -159 ¦ Mem.at(addr) = to_byte(c1, c2); -160 ¦ trace(99, "load") << addr << " -> " << HEXBYTE << NUM(Mem.at(addr)) << end(); -161 ¦ addr++; -162 } -163 End_of_program = addr; -164 } -165 -166 char next_hex_byte(istream& in) { -167 while (has_data(in)) { -168 ¦ char c = '\0'; -169 ¦ in >> c; -170 ¦ if (c == ' ' || c == '\n') continue; -171 ¦ while (c == '#') { -172 ¦ ¦ while (has_data(in)) { -173 ¦ ¦ ¦ in >> c; -174 ¦ ¦ ¦ if (c == '\n') { -175 ¦ ¦ ¦ ¦ in >> c; -176 ¦ ¦ ¦ ¦ break; -177 ¦ ¦ ¦ } -178 ¦ ¦ } -179 ¦ } -180 ¦ if (c >= '0' && c <= '9') return c; -181 ¦ else if (c >= 'a' && c <= 'f') return c; -182 ¦ else if (c >= 'A' && c <= 'F') return tolower(c); -183 ¦ // disallow any non-hex characters, including a '0x' prefix -184 ¦ if (!isspace(c)) { -185 ¦ ¦ raise << "invalid non-hex character '" << c << "'\n" << end(); -186 ¦ ¦ break; -187 ¦ } -188 } -189 return '\0'; -190 } -191 -192 uint8_t to_byte(char hex_byte1, char hex_byte2) { -193 return to_hex_num(hex_byte1)*16 + to_hex_num(hex_byte2); + 38 // Combine 'arg1' and 'arg2' with arithmetic operation 'op' and store the + 39 // result in 'arg1', then update flags. + 40 // beware: no side-effects in args + 41 #define BINARY_ARITHMETIC_OP(op, arg1, arg2) { \ + 42 /* arg1 and arg2 must be signed */ \ + 43 int64_t tmp = arg1 op arg2; \ + 44 arg1 = arg1 op arg2; \ + 45 trace(2, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ + 46 SF = (arg1 < 0); \ + 47 ZF = (arg1 == 0); \ + 48 OF = (arg1 != tmp); \ + 49 } + 50 + 51 // Combine 'arg1' and 'arg2' with bitwise operation 'op' and store the result + 52 // in 'arg1', then update flags. + 53 #define BINARY_BITWISE_OP(op, arg1, arg2) { \ + 54 /* arg1 and arg2 must be unsigned */ \ + 55 arg1 = arg1 op arg2; \ + 56 trace(2, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ + 57 SF = (arg1 >> 31); \ + 58 ZF = (arg1 == 0); \ + 59 OF = false; \ + 60 } + 61 + 62 //:: simulated RAM + 63 + 64 :(before "End Globals") + 65 vector<uint8_t> Mem; + 66 uint32_t End_of_program = 0; + 67 :(before "End Reset") + 68 Mem.clear(); + 69 Mem.resize(1024); + 70 End_of_program = 0; + 71 + 72 //:: core interpreter loop + 73 + 74 :(scenario add_imm32_to_eax) + 75 # In scenarios, programs are a series of hex bytes, each (variable-length) + 76 # instruction on one line. + 77 # + 78 # x86 instructions consist of the following parts (see cheatsheet.pdf): + 79 # opcode ModR/M SIB displacement immediate + 80 # instruction mod, reg, Reg/Mem bits scale, index, base + 81 # 1-3 bytes 0/1 byte 0/1 byte 0/1/2/4 bytes 0/1/2/4 bytes + 82 ¦ 05 0a 0b 0c 0d # add 0x0d0c0b0a to EAX + 83 # All hex bytes must be exactly 2 characters each. No '0x' prefixes. + 84 +load: 1 -> 05 + 85 +load: 2 -> 0a + 86 +load: 3 -> 0b + 87 +load: 4 -> 0c + 88 +load: 5 -> 0d + 89 +run: add imm32 0x0d0c0b0a to reg EAX + 90 +run: storing 0x0d0c0b0a + 91 + 92 :(code) + 93 // helper for tests: load a program into memory from a textual representation + 94 // of its bytes, and run it + 95 void run(const string& text_bytes) { + 96 load_program(text_bytes); + 97 EIP = 1; // preserve null pointer + 98 while (EIP < End_of_program) + 99 ¦ run_one_instruction(); +100 } +101 +102 // skeleton of how x86 instructions are decoded +103 void run_one_instruction() { +104 uint8_t op=0, op2=0, op3=0; +105 switch (op = next()) { +106 case 0xf4: // hlt +107 ¦ EIP = End_of_program; +108 ¦ break; +109 // our first opcode +110 case 0x05: { // add imm32 to EAX +111 ¦ int32_t arg2 = imm32(); +112 ¦ trace(2, "run") << "add imm32 0x" << HEXWORD << arg2 << " to reg EAX" << end(); +113 ¦ BINARY_ARITHMETIC_OP(+, Reg[EAX].i, arg2); +114 ¦ break; +115 } +116 // End Single-Byte Opcodes +117 case 0x0f: +118 ¦ switch(op2 = next()) { +119 ¦ // End Two-Byte Opcodes Starting With 0f +120 ¦ default: +121 ¦ ¦ cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n'; +122 ¦ ¦ exit(1); +123 ¦ } +124 ¦ break; +125 case 0xf3: +126 ¦ switch(op2 = next()) { +127 ¦ // End Two-Byte Opcodes Starting With f3 +128 ¦ case 0x0f: +129 ¦ ¦ switch(op3 = next()) { +130 ¦ ¦ // End Three-Byte Opcodes Starting With f3 0f +131 ¦ ¦ default: +132 ¦ ¦ ¦ cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n'; +133 ¦ ¦ ¦ exit(1); +134 ¦ ¦ } +135 ¦ ¦ break; +136 ¦ default: +137 ¦ ¦ cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n'; +138 ¦ ¦ exit(1); +139 ¦ } +140 ¦ break; +141 default: +142 ¦ cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n'; +143 ¦ exit(1); +144 } +145 } +146 +147 void load_program(const string& text_bytes) { +148 uint32_t addr = 1; +149 istringstream in(text_bytes); +150 in >> std::noskipws; +151 while (has_data(in)) { +152 ¦ char c1 = next_hex_byte(in); +153 ¦ if (c1 == '\0') break; +154 ¦ if (!has_data(in)) { +155 ¦ ¦ raise << "input program truncated mid-byte\n" << end(); +156 ¦ ¦ return; +157 ¦ } +158 ¦ char c2 = next_hex_byte(in); +159 ¦ if (c2 == '\0') { +160 ¦ ¦ raise << "input program truncated mid-byte\n" << end(); +161 ¦ ¦ return; +162 ¦ } +163 ¦ Mem.at(addr) = to_byte(c1, c2); +164 ¦ trace(99, "load") << addr << " -> " << HEXBYTE << NUM(Mem.at(addr)) << end(); +165 ¦ addr++; +166 } +167 End_of_program = addr; +168 } +169 +170 char next_hex_byte(istream& in) { +171 while (has_data(in)) { +172 ¦ char c = '\0'; +173 ¦ in >> c; +174 ¦ if (c == ' ' || c == '\n') continue; +175 ¦ while (c == '#') { +176 ¦ ¦ while (has_data(in)) { +177 ¦ ¦ ¦ in >> c; +178 ¦ ¦ ¦ if (c == '\n') { +179 ¦ ¦ ¦ ¦ in >> c; +180 ¦ ¦ ¦ ¦ break; +181 ¦ ¦ ¦ } +182 ¦ ¦ } +183 ¦ } +184 ¦ if (c >= '0' && c <= '9') return c; +185 ¦ else if (c >= 'a' && c <= 'f') return c; +186 ¦ else if (c >= 'A' && c <= 'F') return tolower(c); +187 ¦ // disallow any non-hex characters, including a '0x' prefix +188 ¦ if (!isspace(c)) { +189 ¦ ¦ raise << "invalid non-hex character '" << c << "'\n" << end(); +190 ¦ ¦ break; +191 ¦ } +192 } +193 return '\0'; 194 } -195 uint8_t to_hex_num(char c) { -196 if (c >= '0' && c <= '9') return c - '0'; -197 if (c >= 'a' && c <= 'f') return c - 'a' + 10; -198 assert(false); -199 return 0; -200 } -201 -202 inline uint8_t next() { -203 return Mem.at(EIP++); +195 +196 uint8_t to_byte(char hex_byte1, char hex_byte2) { +197 return to_hex_num(hex_byte1)*16 + to_hex_num(hex_byte2); +198 } +199 uint8_t to_hex_num(char c) { +200 if (c >= '0' && c <= '9') return c - '0'; +201 if (c >= 'a' && c <= 'f') return c - 'a' + 10; +202 assert(false); +203 return 0; 204 } 205 -206 // read a 32-bit immediate in little-endian order from the instruction stream -207 int32_t imm32() { -208 int32_t result = next(); -209 result |= (next()<<8); -210 result |= (next()<<16); -211 result |= (next()<<24); -212 return result; -213 } -214 -215 :(before "End Includes") -216 #include <iomanip> -217 #define HEXBYTE std::hex << std::setw(2) << std::setfill('0') -218 #define HEXWORD std::hex << std::setw(8) << std::setfill('0') -219 // ugly that iostream doesn't print uint8_t as an integer -220 #define NUM(X) static_cast<int>(X) -221 #include <stdint.h> +206 inline uint8_t next() { +207 return Mem.at(EIP++); +208 } +209 +210 // read a 32-bit immediate in little-endian order from the instruction stream +211 int32_t imm32() { +212 int32_t result = next(); +213 result |= (next()<<8); +214 result |= (next()<<16); +215 result |= (next()<<24); +216 return result; +217 } +218 +219 :(before "End Includes") +220 #include <iomanip> +221 #define HEXBYTE std::hex << std::setw(2) << std::setfill('0') +222 #define HEXWORD std::hex << std::setw(8) << std::setfill('0') +223 // ugly that iostream doesn't print uint8_t as an integer +224 #define NUM(X) static_cast<int>(X) +225 #include <stdint.h> diff --git a/html/subx/011direct_addressing.cc.html b/html/subx/011direct_addressing.cc.html index 97745fd0..7918cf20 100644 --- a/html/subx/011direct_addressing.cc.html +++ b/html/subx/011direct_addressing.cc.html @@ -76,11 +76,11 @@ if ('onhashchange' in window) { 11 12 :(before "End Single-Byte Opcodes") 13 case 0x01: { // add r32 to r/m32 - 14 uint8_t modrm = next(); + 14 uint8_t modrm = next(); 15 uint8_t arg2 = (modrm>>3)&0x7; - 16 trace(2, "run") << "add reg " << NUM(arg2) << " to effective address" << end(); + 16 trace(2, "run") << "add reg " << NUM(arg2) << " to effective address" << end(); 17 int32_t* arg1 = effective_address(modrm); - 18 BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i); + 18 BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i); 19 break; 20 } 21 @@ -96,12 +96,12 @@ if ('onhashchange' in window) { 31 switch (mod) { 32 case 3: 33 ¦ // mod 3 is just register direct addressing - 34 ¦ trace(2, "run") << "effective address is reg " << NUM(rm) << end(); + 34 ¦ trace(2, "run") << "effective address is reg " << NUM(rm) << end(); 35 ¦ result = &Reg[rm].i; 36 ¦ break; 37 // End Mod Special-cases 38 default: - 39 ¦ cerr << "unrecognized mod bits: " << NUM(mod) << '\n'; + 39 ¦ cerr << "unrecognized mod bits: " << NUM(mod) << '\n'; 40 ¦ exit(1); 41 } 42 return result; @@ -120,11 +120,11 @@ if ('onhashchange' in window) { 55 56 :(before "End Single-Byte Opcodes") 57 case 0x29: { // subtract r32 from r/m32 - 58 uint8_t modrm = next(); + 58 uint8_t modrm = next(); 59 uint8_t arg2 = (modrm>>3)&0x7; - 60 trace(2, "run") << "subtract reg " << NUM(arg2) << " from effective address" << end(); + 60 trace(2, "run") << "subtract reg " << NUM(arg2) << " from effective address" << end(); 61 int32_t* arg1 = effective_address(modrm); - 62 BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i); + 62 BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i); 63 break; 64 } 65 @@ -141,11 +141,11 @@ if ('onhashchange' in window) { 76 77 :(before "End Single-Byte Opcodes") 78 case 0x21: { // and r32 with r/m32 - 79 uint8_t modrm = next(); + 79 uint8_t modrm = next(); 80 uint8_t arg2 = (modrm>>3)&0x7; - 81 trace(2, "run") << "and reg " << NUM(arg2) << " with effective address" << end(); + 81 trace(2, "run") << "and reg " << NUM(arg2) << " with effective address" << end(); 82 int32_t* arg1 = effective_address(modrm); - 83 BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u); + 83 BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u); 84 break; 85 } 86 @@ -162,11 +162,11 @@ if ('onhashchange' in window) { 97 98 :(before "End Single-Byte Opcodes") 99 case 0x09: { // or r32 with r/m32 -100 uint8_t modrm = next(); +100 uint8_t modrm = next(); 101 uint8_t arg2 = (modrm>>3)&0x7; -102 trace(2, "run") << "or reg " << NUM(arg2) << " with effective address" << end(); +102 trace(2, "run") << "or reg " << NUM(arg2) << " with effective address" << end(); 103 int32_t* arg1 = effective_address(modrm); -104 BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u); +104 BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u); 105 break; 106 } 107 @@ -183,11 +183,11 @@ if ('onhashchange' in window) { 118 119 :(before "End Single-Byte Opcodes") 120 case 0x31: { // xor r32 with r/m32 -121 uint8_t modrm = next(); +121 uint8_t modrm = next(); 122 uint8_t arg2 = (modrm>>3)&0x7; -123 trace(2, "run") << "xor reg " << NUM(arg2) << " with effective address" << end(); +123 trace(2, "run") << "xor reg " << NUM(arg2) << " with effective address" << end(); 124 int32_t* arg1 = effective_address(modrm); -125 BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u); +125 BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u); 126 break; 127 } 128 @@ -203,11 +203,11 @@ if ('onhashchange' in window) { 138 139 :(before "End Single-Byte Opcodes") 140 case 0xf7: { // xor r32 with r/m32 -141 uint8_t modrm = next(); +141 uint8_t modrm = next(); 142 trace(2, "run") << "'not' of effective address" << end(); 143 int32_t* arg1 = effective_address(modrm); 144 *arg1 = ~(*arg1); -145 trace(2, "run") << "storing 0x" << HEXWORD << *arg1 << end(); +145 trace(2, "run") << "storing 0x" << HEXWORD << *arg1 << end(); 146 SF = (*arg1 >> 31); 147 ZF = (*arg1 == 0); 148 OF = false; diff --git a/html/subx/012indirect_addressing.cc.html b/html/subx/012indirect_addressing.cc.html index 8f0f7adf..863b554d 100644 --- a/html/subx/012indirect_addressing.cc.html +++ b/html/subx/012indirect_addressing.cc.html @@ -80,8 +80,8 @@ if ('onhashchange' in window) { 16 // mod 0 is usually indirect addressing 17 switch (rm) { 18 default: - 19 ¦ trace(2, "run") << "effective address is mem at address 0x" << std::hex << Reg[rm].u << " (reg " << NUM(rm) << ")" << end(); - 20 ¦ assert(Reg[rm].u + sizeof(int32_t) <= Mem.size()); + 19 ¦ trace(2, "run") << "effective address is mem at address 0x" << std::hex << Reg[rm].u << " (reg " << NUM(rm) << ")" << end(); + 20 ¦ assert(Reg[rm].u + sizeof(int32_t) <= Mem.size()); 21 ¦ result = reinterpret_cast<int32_t*>(&Mem.at(Reg[rm].u)); // rely on the host itself being in little-endian order 22 ¦ break; 23 // End Mod 0 Special-cases @@ -102,11 +102,11 @@ if ('onhashchange' in window) { 38 39 :(before "End Single-Byte Opcodes") 40 case 0x03: { // add r/m32 to r32 - 41 uint8_t modrm = next(); + 41 uint8_t modrm = next(); 42 uint8_t arg1 = (modrm>>3)&0x7; - 43 trace(2, "run") << "add effective address to reg " << NUM(arg1) << end(); + 43 trace(2, "run") << "add effective address to reg " << NUM(arg1) << end(); 44 const int32_t* arg2 = effective_address(modrm); - 45 BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2); + 45 BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2); 46 break; 47 } 48 @@ -136,11 +136,11 @@ if ('onhashchange' in window) { 72 73 :(before "End Single-Byte Opcodes") 74 case 0x2b: { // subtract r/m32 from r32 - 75 uint8_t modrm = next(); + 75 uint8_t modrm = next(); 76 uint8_t arg1 = (modrm>>3)&0x7; - 77 trace(2, "run") << "subtract effective address from reg " << NUM(arg1) << end(); + 77 trace(2, "run") << "subtract effective address from reg " << NUM(arg1) << end(); 78 const int32_t* arg2 = effective_address(modrm); - 79 BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2); + 79 BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2); 80 break; 81 } 82 @@ -173,11 +173,11 @@ if ('onhashchange' in window) { 109 110 :(before "End Single-Byte Opcodes") 111 case 0x23: { // and r/m32 with r32 -112 uint8_t modrm = next(); +112 uint8_t modrm = next(); 113 uint8_t arg1 = (modrm>>3)&0x7; -114 trace(2, "run") << "and effective address with reg " << NUM(arg1) << end(); +114 trace(2, "run") << "and effective address with reg " << NUM(arg1) << end(); 115 const int32_t* arg2 = effective_address(modrm); -116 BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2); +116 BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2); 117 break; 118 } 119 @@ -213,11 +213,11 @@ if ('onhashchange' in window) { 149 150 :(before "End Single-Byte Opcodes") 151 case 0x0b: { // or r/m32 with r32 -152 uint8_t modrm = next(); +152 uint8_t modrm = next(); 153 uint8_t arg1 = (modrm>>3)&0x7; -154 trace(2, "run") << "or effective address with reg " << NUM(arg1) << end(); +154 trace(2, "run") << "or effective address with reg " << NUM(arg1) << end(); 155 const int32_t* arg2 = effective_address(modrm); -156 BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2); +156 BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2); 157 break; 158 } 159 @@ -253,11 +253,11 @@ if ('onhashchange' in window) { 189 190 :(before "End Single-Byte Opcodes") 191 case 0x33: { // xor r/m32 with r32 -192 uint8_t modrm = next(); +192 uint8_t modrm = next(); 193 uint8_t arg1 = (modrm>>3)&0x7; -194 trace(2, "run") << "xor effective address with reg " << NUM(arg1) << end(); +194 trace(2, "run") << "xor effective address with reg " << NUM(arg1) << end(); 195 const int32_t* arg2 = effective_address(modrm); -196 BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2); +196 BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2); 197 break; 198 } 199 diff --git a/html/subx/013immediate_addressing.cc.html b/html/subx/013immediate_addressing.cc.html index b8ee4442..02a931db 100644 --- a/html/subx/013immediate_addressing.cc.html +++ b/html/subx/013immediate_addressing.cc.html @@ -68,26 +68,26 @@ if ('onhashchange' in window) { 4 % Reg[3].i = 1; 5 # op ModRM SIB displacement immediate 6 81 c3 0a 0b 0c 0d # add 0x0d0c0b0a to EBX (reg 3) - 7 +run: combine imm32 0x0d0c0b0a with effective address + 7 +run: combine imm32 0x0d0c0b0a with effective address 8 +run: effective address is reg 3 9 +run: subop add 10 +run: storing 0x0d0c0b0b 11 12 :(before "End Single-Byte Opcodes") 13 case 0x81: { // combine imm32 with r/m32 - 14 uint8_t modrm = next(); - 15 int32_t arg2 = imm32(); - 16 trace(2, "run") << "combine imm32 0x" << HEXWORD << arg2 << " with effective address" << end(); + 14 uint8_t modrm = next(); + 15 int32_t arg2 = imm32(); + 16 trace(2, "run") << "combine imm32 0x" << HEXWORD << arg2 << " with effective address" << end(); 17 int32_t* arg1 = effective_address(modrm); 18 uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits 19 switch (subop) { 20 case 0: 21 ¦ trace(2, "run") << "subop add" << end(); - 22 ¦ BINARY_ARITHMETIC_OP(+, *arg1, arg2); + 22 ¦ BINARY_ARITHMETIC_OP(+, *arg1, arg2); 23 ¦ break; 24 // End Op 81 Subops 25 default: - 26 ¦ cerr << "unrecognized sub-opcode after 81: " << NUM(subop) << '\n'; + 26 ¦ cerr << "unrecognized sub-opcode after 81: " << NUM(subop) << '\n'; 27 ¦ exit(1); 28 } 29 break; @@ -100,7 +100,7 @@ if ('onhashchange' in window) { 36 % Mem.at(0x60) = 1; 37 # op ModR/M SIB displacement immediate 38 81 03 0a 0b 0c 0d # add 0x0d0c0b0a to *EBX (reg 3) - 39 +run: combine imm32 0x0d0c0b0a with effective address + 39 +run: combine imm32 0x0d0c0b0a with effective address 40 +run: effective address is mem at address 0x60 (reg 3) 41 +run: subop add 42 +run: storing 0x0d0c0b0b @@ -111,14 +111,14 @@ if ('onhashchange' in window) { 47 % Reg[EAX].i = 0x0d0c0baa; 48 # op ModR/M SIB displacement immediate 49 2d 0a 0b 0c 0d # subtract 0x0d0c0b0a from EAX (reg 0) - 50 +run: subtract imm32 0x0d0c0b0a from reg EAX + 50 +run: subtract imm32 0x0d0c0b0a from reg EAX 51 +run: storing 0x000000a0 52 53 :(before "End Single-Byte Opcodes") 54 case 0x2d: { // subtract imm32 from EAX - 55 int32_t arg2 = imm32(); - 56 trace(2, "run") << "subtract imm32 0x" << HEXWORD << arg2 << " from reg EAX" << end(); - 57 BINARY_ARITHMETIC_OP(-, Reg[EAX].i, arg2); + 55 int32_t arg2 = imm32(); + 56 trace(2, "run") << "subtract imm32 0x" << HEXWORD << arg2 << " from reg EAX" << end(); + 57 BINARY_ARITHMETIC_OP(-, Reg[EAX].i, arg2); 58 break; 59 } 60 @@ -129,7 +129,7 @@ if ('onhashchange' in window) { 65 % Mem.at(0x60) = 10; 66 # op ModRM SIB displacement immediate 67 81 2b 01 00 00 00 # subtract 1 from *EBX (reg 3) - 68 +run: combine imm32 0x00000001 with effective address + 68 +run: combine imm32 0x00000001 with effective address 69 +run: effective address is mem at address 0x60 (reg 3) 70 +run: subop subtract 71 +run: storing 0x00000009 @@ -140,7 +140,7 @@ if ('onhashchange' in window) { 76 % Reg[3].i = 10; 77 # op ModRM SIB displacement immediate 78 81 eb 01 00 00 00 # subtract 1 from EBX (reg 3) - 79 +run: combine imm32 0x00000001 with effective address + 79 +run: combine imm32 0x00000001 with effective address 80 +run: effective address is reg 3 81 +run: subop subtract 82 +run: storing 0x00000009 @@ -148,7 +148,7 @@ if ('onhashchange' in window) { 84 :(before "End Op 81 Subops") 85 case 5: { 86 trace(2, "run") << "subop subtract" << end(); - 87 BINARY_ARITHMETIC_OP(-, *arg1, arg2); + 87 BINARY_ARITHMETIC_OP(-, *arg1, arg2); 88 break; 89 } 90 @@ -158,14 +158,14 @@ if ('onhashchange' in window) { 94 % Reg[EAX].i = 0xff; 95 # op ModR/M SIB displacement immediate 96 25 0a 0b 0c 0d # and 0x0d0c0b0a with EAX (reg 0) - 97 +run: and imm32 0x0d0c0b0a with reg EAX + 97 +run: and imm32 0x0d0c0b0a with reg EAX 98 +run: storing 0x0000000a 99 100 :(before "End Single-Byte Opcodes") 101 case 0x25: { // and imm32 with EAX -102 int32_t arg2 = imm32(); -103 trace(2, "run") << "and imm32 0x" << HEXWORD << arg2 << " with reg EAX" << end(); -104 BINARY_BITWISE_OP(&, Reg[EAX].i, arg2); +102 int32_t arg2 = imm32(); +103 trace(2, "run") << "and imm32 0x" << HEXWORD << arg2 << " with reg EAX" << end(); +104 BINARY_BITWISE_OP(&, Reg[EAX].i, arg2); 105 break; 106 } 107 @@ -176,7 +176,7 @@ if ('onhashchange' in window) { 112 % Mem.at(0x60) = 0xff; 113 # op ModRM SIB displacement immediate 114 81 23 0a 0b 0c 0d # and 0x0d0c0b0a with *EBX (reg 3) -115 +run: combine imm32 0x0d0c0b0a with effective address +115 +run: combine imm32 0x0d0c0b0a with effective address 116 +run: effective address is mem at address 0x60 (reg 3) 117 +run: subop and 118 +run: storing 0x0000000a @@ -187,7 +187,7 @@ if ('onhashchange' in window) { 123 % Reg[3].i = 0xff; 124 # op ModRM SIB displacement immediate 125 81 e3 0a 0b 0c 0d # and 0x0d0c0b0a with EBX (reg 3) -126 +run: combine imm32 0x0d0c0b0a with effective address +126 +run: combine imm32 0x0d0c0b0a with effective address 127 +run: effective address is reg 3 128 +run: subop and 129 +run: storing 0x0000000a @@ -195,7 +195,7 @@ if ('onhashchange' in window) { 131 :(before "End Op 81 Subops") 132 case 4: { 133 trace(2, "run") << "subop and" << end(); -134 BINARY_BITWISE_OP(&, *arg1, arg2); +134 BINARY_BITWISE_OP(&, *arg1, arg2); 135 break; 136 } 137 @@ -205,14 +205,14 @@ if ('onhashchange' in window) { 141 % Reg[EAX].i = 0xd0c0b0a0; 142 # op ModR/M SIB displacement immediate 143 0d 0a 0b 0c 0d # or 0x0d0c0b0a with EAX (reg 0) -144 +run: or imm32 0x0d0c0b0a with reg EAX +144 +run: or imm32 0x0d0c0b0a with reg EAX 145 +run: storing 0xddccbbaa 146 147 :(before "End Single-Byte Opcodes") 148 case 0x0d: { // or imm32 with EAX -149 int32_t arg2 = imm32(); -150 trace(2, "run") << "or imm32 0x" << HEXWORD << arg2 << " with reg EAX" << end(); -151 BINARY_BITWISE_OP(|, Reg[EAX].i, arg2); +149 int32_t arg2 = imm32(); +150 trace(2, "run") << "or imm32 0x" << HEXWORD << arg2 << " with reg EAX" << end(); +151 BINARY_BITWISE_OP(|, Reg[EAX].i, arg2); 152 break; 153 } 154 @@ -226,7 +226,7 @@ if ('onhashchange' in window) { 162 % Mem.at(0x63) = 0xd0; 163 # op ModRM SIB displacement immediate 164 81 0b 0a 0b 0c 0d # or 0x0d0c0b0a with *EBX (reg 3) -165 +run: combine imm32 0x0d0c0b0a with effective address +165 +run: combine imm32 0x0d0c0b0a with effective address 166 +run: effective address is mem at address 0x60 (reg 3) 167 +run: subop or 168 +run: storing 0xddccbbaa @@ -237,7 +237,7 @@ if ('onhashchange' in window) { 173 % Reg[3].i = 0xd0c0b0a0; 174 # op ModRM SIB displacement immediate 175 81 cb 0a 0b 0c 0d # or 0x0d0c0b0a with EBX (reg 3) -176 +run: combine imm32 0x0d0c0b0a with effective address +176 +run: combine imm32 0x0d0c0b0a with effective address 177 +run: effective address is reg 3 178 +run: subop or 179 +run: storing 0xddccbbaa @@ -245,7 +245,7 @@ if ('onhashchange' in window) { 181 :(before "End Op 81 Subops") 182 case 1: { 183 trace(2, "run") << "subop or" << end(); -184 BINARY_BITWISE_OP(|, *arg1, arg2); +184 BINARY_BITWISE_OP(|, *arg1, arg2); 185 break; 186 } 187 @@ -255,14 +255,14 @@ if ('onhashchange' in window) { 191 % Reg[EAX].i = 0xddccb0a0; 192 # op ModR/M SIB displacement immediate 193 35 0a 0b 0c 0d # xor 0x0d0c0b0a with EAX (reg 0) -194 +run: xor imm32 0x0d0c0b0a with reg EAX +194 +run: xor imm32 0x0d0c0b0a with reg EAX 195 +run: storing 0xd0c0bbaa 196 197 :(before "End Single-Byte Opcodes") 198 case 0x35: { // xor imm32 with EAX -199 int32_t arg2 = imm32(); -200 trace(2, "run") << "xor imm32 0x" << HEXWORD << arg2 << " with reg EAX" << end(); -201 BINARY_BITWISE_OP(^, Reg[EAX].i, arg2); +199 int32_t arg2 = imm32(); +200 trace(2, "run") << "xor imm32 0x" << HEXWORD << arg2 << " with reg EAX" << end(); +201 BINARY_BITWISE_OP(^, Reg[EAX].i, arg2); 202 break; 203 } 204 @@ -276,7 +276,7 @@ if ('onhashchange' in window) { 212 % Mem.at(0x63) = 0xd0; 213 # op ModRM SIB displacement immediate 214 81 33 0a 0b 0c 0d # xor 0x0d0c0b0a with *EBX (reg 3) -215 +run: combine imm32 0x0d0c0b0a with effective address +215 +run: combine imm32 0x0d0c0b0a with effective address 216 +run: effective address is mem at address 0x60 (reg 3) 217 +run: subop xor 218 +run: storing 0xddccbbaa @@ -287,7 +287,7 @@ if ('onhashchange' in window) { 223 % Reg[3].i = 0xd0c0b0a0; 224 # op ModRM SIB displacement immediate 225 81 f3 0a 0b 0c 0d # xor 0x0d0c0b0a with EBX (reg 3) -226 +run: combine imm32 0x0d0c0b0a with effective address +226 +run: combine imm32 0x0d0c0b0a with effective address 227 +run: effective address is reg 3 228 +run: subop xor 229 +run: storing 0xddccbbaa @@ -295,7 +295,7 @@ if ('onhashchange' in window) { 231 :(before "End Op 81 Subops") 232 case 6: { 233 trace(2, "run") << "subop xor" << end(); -234 BINARY_BITWISE_OP(^, *arg1, arg2); +234 BINARY_BITWISE_OP(^, *arg1, arg2); 235 break; 236 } diff --git a/subx/010core.cc b/subx/010core.cc index 058cc065..9229143b 100644 --- a/subx/010core.cc +++ b/subx/010core.cc @@ -35,6 +35,8 @@ SF = ZF = OF = false; //: how the flag registers are updated after each instruction :(before "End Includes") +// Combine 'arg1' and 'arg2' with arithmetic operation 'op' and store the +// result in 'arg1', then update flags. // beware: no side-effects in args #define BINARY_ARITHMETIC_OP(op, arg1, arg2) { \ /* arg1 and arg2 must be signed */ \ @@ -46,6 +48,8 @@ SF = ZF = OF = false; OF = (arg1 != tmp); \ } +// Combine 'arg1' and 'arg2' with bitwise operation 'op' and store the result +// in 'arg1', then update flags. #define BINARY_BITWISE_OP(op, arg1, arg2) { \ /* arg1 and arg2 must be unsigned */ \ arg1 = arg1 op arg2; \ -- cgit 1.4.1-2-gfad0