From db8a9ab553535551ce80dcd5ce07cffb85f5cfb7 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 11 Jan 2019 18:08:21 -0800 Subject: 4921 --- html/subx/010---vm.cc.html | 16 +- html/subx/013direct_addressing.cc.html | 1477 +++++++++++++++-------------- html/subx/014indirect_addressing.cc.html | 26 +- html/subx/015immediate_addressing.cc.html | 2 +- html/subx/016index_addressing.cc.html | 4 +- html/subx/019functions.cc.html | 2 +- html/subx/021byte_addressing.cc.html | 41 +- html/subx/030---operands.cc.html | 2 +- html/subx/031check_operands.cc.html | 712 +++++++------- html/subx/032check_operand_bounds.cc.html | 2 +- html/subx/036global_variables.cc.html | 4 +- html/subx/039debug.cc.html | 31 + 12 files changed, 1201 insertions(+), 1118 deletions(-) (limited to 'html/subx') diff --git a/html/subx/010---vm.cc.html b/html/subx/010---vm.cc.html index 0be40175..f7a9812d 100644 --- a/html/subx/010---vm.cc.html +++ b/html/subx/010---vm.cc.html @@ -357,14 +357,14 @@ if ('onhashchange' in window) { 296 void run_one_instruction() { 297 uint8_t op=0, op2=0, op3=0; 298 // Run One Instruction -299 trace(90, "run") << "inst: 0x" << HEXWORD << EIP << end(); -300 op = next(); -301 if (Dump_trace) { -302 cerr << "opcode: " << HEXBYTE << NUM(op) << '\n'; -303 cerr << "registers at start: "; -304 dump_registers(); -305 //? dump_stack(); // for debugging; not defined until later layer -306 } +299 if (Dump_trace) { +300 cerr << "registers: "; +301 dump_registers(); +302 //? dump_stack(); // for debugging; not defined until later layer +303 } +304 trace(90, "run") << "inst: 0x" << HEXWORD << EIP << end(); +305 op = next(); +306 if (Dump_trace) cerr << "opcode: " << HEXBYTE << NUM(op) << '\n'; 307 switch (op) { 308 case 0xf4: // hlt 309 EIP = End_of_program; diff --git a/html/subx/013direct_addressing.cc.html b/html/subx/013direct_addressing.cc.html index cf04767e..190c4920 100644 --- a/html/subx/013direct_addressing.cc.html +++ b/html/subx/013direct_addressing.cc.html @@ -82,7 +82,7 @@ if ('onhashchange' in window) { 18 case 0x01: { // add r32 to r/m32 19 uint8_t modrm = next(); 20 uint8_t arg2 = (modrm>>3)&0x7; - 21 trace(90, "run") << "add " << rname(arg2) << " to r/m32" << end(); + 21 trace(90, "run") << "add " << rname(arg2) << " to r/m32" << end(); 22 int32_t* arg1 = effective_address(modrm); 23 BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i); 24 break; @@ -98,7 +98,7 @@ if ('onhashchange' in window) { 34 const uint8_t rm = modrm & 0x7; 35 if (mod == 3) { 36 // mod 3 is just register direct addressing - 37 trace(90, "run") << "r/m32 is " << rname(rm) << end(); + 37 trace(90, "run") << "r/m32 is " << rname(rm) << end(); 38 return &Reg[rm].i; 39 } 40 return mem_addr_i32(effective_address_number(modrm)); @@ -120,742 +120,743 @@ if ('onhashchange' in window) { 56 exit(1); 57 } 58 //: other mods are indirect, and they'll set addr appropriately - 59 return addr; - 60 } - 61 - 62 string rname(uint8_t r) { - 63 switch (r) { - 64 case 0: return "EAX"; - 65 case 1: return "ECX"; - 66 case 2: return "EDX"; - 67 case 3: return "EBX"; - 68 case 4: return "ESP"; - 69 case 5: return "EBP"; - 70 case 6: return "ESI"; - 71 case 7: return "EDI"; - 72 default: raise << "invalid register " << r << '\n' << end(); return ""; - 73 } - 74 } - 75 - 76 //:: subtract - 77 - 78 :(before "End Initialize Op Names") - 79 put_new(Name, "29", "subtract r32 from rm32 (sub)"); - 80 - 81 :(scenario subtract_r32_from_r32) - 82 % Reg[EAX].i = 10; - 83 % Reg[EBX].i = 1; - 84 == 0x1 - 85 # op ModR/M SIB displacement immediate - 86 29 d8 # subtract EBX from EAX - 87 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) - 88 +run: subtract EBX from r/m32 - 89 +run: r/m32 is EAX - 90 +run: storing 0x00000009 - 91 - 92 :(before "End Single-Byte Opcodes") - 93 case 0x29: { // subtract r32 from r/m32 - 94 const uint8_t modrm = next(); - 95 const uint8_t arg2 = (modrm>>3)&0x7; - 96 trace(90, "run") << "subtract " << rname(arg2) << " from r/m32" << end(); - 97 int32_t* arg1 = effective_address(modrm); - 98 BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i); - 99 break; -100 } -101 -102 //:: multiply -103 -104 :(before "End Initialize Op Names") -105 put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul)"); -106 -107 :(scenario multiply_eax_by_r32) -108 % Reg[EAX].i = 4; -109 % Reg[ECX].i = 3; -110 == 0x1 -111 # op ModR/M SIB displacement immediate -112 f7 e1 # multiply EAX by ECX -113 # ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX) -114 +run: operate on r/m32 -115 +run: r/m32 is ECX -116 +run: subop: multiply EAX by r/m32 -117 +run: storing 0x0000000c -118 -119 :(before "End Single-Byte Opcodes") -120 case 0xf7: { -121 const uint8_t modrm = next(); -122 trace(90, "run") << "operate on r/m32" << end(); -123 int32_t* arg1 = effective_address(modrm); -124 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits -125 switch (subop) { -126 case 4: { // mul unsigned EAX by r/m32 -127 trace(90, "run") << "subop: multiply EAX by r/m32" << end(); -128 const uint64_t result = Reg[EAX].u * static_cast<uint32_t>(*arg1); -129 Reg[EAX].u = result & 0xffffffff; -130 Reg[EDX].u = result >> 32; -131 OF = (Reg[EDX].u != 0); -132 trace(90, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end(); -133 break; -134 } -135 // End Op f7 Subops -136 default: -137 cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n'; -138 exit(1); -139 } -140 break; -141 } -142 -143 //: -144 -145 :(before "End Initialize Op Names") -146 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)"); -147 -148 :(scenario multiply_r32_into_r32) -149 % Reg[EAX].i = 4; -150 % Reg[EBX].i = 2; -151 == 0x1 -152 # op ModR/M SIB displacement immediate -153 0f af d8 # subtract EBX into EAX -154 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -155 +run: multiply r/m32 into EBX -156 +run: r/m32 is EAX -157 +run: storing 0x00000008 -158 -159 :(before "End Two-Byte Opcodes Starting With 0f") -160 case 0xaf: { // multiply r32 into r/m32 -161 const uint8_t modrm = next(); -162 const uint8_t arg2 = (modrm>>3)&0x7; -163 trace(90, "run") << "multiply r/m32 into " << rname(arg2) << end(); -164 const int32_t* arg1 = effective_address(modrm); -165 BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1); -166 break; -167 } -168 -169 //:: negate -170 -171 :(scenario negate_r32) -172 % Reg[EBX].i = 1; -173 == 0x1 -174 # op ModR/M SIB displacement immediate -175 f7 db # negate EBX -176 # ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) -177 +run: operate on r/m32 -178 +run: r/m32 is EBX -179 +run: subop: negate -180 +run: storing 0xffffffff -181 -182 :(before "End Op f7 Subops") -183 case 3: { // negate r/m32 -184 trace(90, "run") << "subop: negate" << end(); -185 // one case that can overflow -186 if (static_cast<uint32_t>(*arg1) == 0x80000000) { -187 trace(90, "run") << "overflow" << end(); -188 SF = true; -189 ZF = false; -190 OF = true; -191 break; -192 } -193 *arg1 = -(*arg1); -194 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); -195 SF = (*arg1 >> 31); -196 ZF = (*arg1 == 0); -197 OF = false; -198 break; -199 } -200 -201 :(scenario negate_can_overflow) // in exactly one situation -202 % Reg[EBX].i = 0x80000000; // INT_MIN -203 == 0x1 -204 # op ModR/M SIB displacement immediate -205 f7 db # negate EBX -206 # ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) -207 +run: operate on r/m32 -208 +run: r/m32 is EBX -209 +run: subop: negate -210 +run: overflow -211 -212 //:: shift left -213 -214 :(before "End Initialize Op Names") -215 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); -216 -217 :(scenario shift_left_r32_with_cl) -218 % Reg[EBX].i = 13; -219 % Reg[ECX].i = 1; -220 == 0x1 -221 # op ModR/M SIB displacement immediate -222 d3 e3 # negate EBX -223 # ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) -224 +run: operate on r/m32 -225 +run: r/m32 is EBX -226 +run: subop: shift left by CL bits -227 +run: storing 0x0000001a -228 -229 :(before "End Single-Byte Opcodes") -230 case 0xd3: { -231 const uint8_t modrm = next(); -232 trace(90, "run") << "operate on r/m32" << end(); -233 int32_t* arg1 = effective_address(modrm); -234 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits -235 switch (subop) { -236 case 4: { // shift left r/m32 by CL -237 trace(90, "run") << "subop: shift left by CL bits" << end(); -238 uint8_t count = Reg[ECX].u & 0x1f; -239 // OF is only defined if count is 1 -240 if (count == 1) { -241 bool msb = (*arg1 & 0x80000000) >> 1; -242 bool pnsb = (*arg1 & 0x40000000); -243 OF = (msb != pnsb); -244 } -245 *arg1 = (*arg1 << count); -246 ZF = (*arg1 == 0); -247 SF = (*arg1 < 0); -248 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); -249 break; -250 } -251 // End Op d3 Subops -252 default: -253 cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n'; -254 exit(1); -255 } -256 break; -257 } -258 -259 //:: shift right arithmetic -260 -261 :(scenario shift_right_arithmetic_r32_with_cl) -262 % Reg[EBX].i = 26; -263 % Reg[ECX].i = 1; -264 == 0x1 -265 # op ModR/M SIB displacement immediate -266 d3 fb # negate EBX -267 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -268 +run: operate on r/m32 -269 +run: r/m32 is EBX -270 +run: subop: shift right by CL bits, while preserving sign -271 +run: storing 0x0000000d -272 -273 :(before "End Op d3 Subops") -274 case 7: { // shift right r/m32 by CL, preserving sign -275 trace(90, "run") << "subop: shift right by CL bits, while preserving sign" << end(); -276 uint8_t count = Reg[ECX].u & 0x1f; -277 *arg1 = (*arg1 >> count); -278 ZF = (*arg1 == 0); -279 SF = (*arg1 < 0); -280 // OF is only defined if count is 1 -281 if (count == 1) OF = false; -282 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); -283 break; -284 } -285 -286 :(scenario shift_right_arithmetic_odd_r32_with_cl) -287 % Reg[EBX].i = 27; -288 % Reg[ECX].i = 1; -289 == 0x1 -290 # op ModR/M SIB displacement immediate -291 d3 fb # negate EBX -292 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -293 +run: operate on r/m32 -294 +run: r/m32 is EBX -295 +run: subop: shift right by CL bits, while preserving sign -296 # result: 13 -297 +run: storing 0x0000000d -298 -299 :(scenario shift_right_arithmetic_negative_r32_with_cl) -300 % Reg[EBX].i = 0xfffffffd; // -3 -301 % Reg[ECX].i = 1; -302 == 0x1 -303 # op ModR/M SIB displacement immediate -304 d3 fb # negate EBX -305 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) -306 +run: operate on r/m32 -307 +run: r/m32 is EBX -308 +run: subop: shift right by CL bits, while preserving sign -309 # result: -2 -310 +run: storing 0xfffffffe -311 -312 //:: shift right logical -313 -314 :(scenario shift_right_logical_r32_with_cl) -315 % Reg[EBX].i = 26; -316 % Reg[ECX].i = 1; -317 == 0x1 -318 # op ModR/M SIB displacement immediate -319 d3 eb # negate EBX -320 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -321 +run: operate on r/m32 -322 +run: r/m32 is EBX -323 +run: subop: shift right by CL bits, while padding zeroes -324 +run: storing 0x0000000d -325 -326 :(before "End Op d3 Subops") -327 case 5: { // shift right r/m32 by CL, preserving sign -328 trace(90, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); -329 uint8_t count = Reg[ECX].u & 0x1f; -330 // OF is only defined if count is 1 -331 if (count == 1) { -332 bool msb = (*arg1 & 0x80000000) >> 1; -333 bool pnsb = (*arg1 & 0x40000000); -334 OF = (msb != pnsb); -335 } -336 uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1); -337 *uarg1 = (*uarg1 >> count); -338 ZF = (*uarg1 == 0); -339 // result is always positive by definition -340 SF = false; -341 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); -342 break; -343 } -344 -345 :(scenario shift_right_logical_odd_r32_with_cl) -346 % Reg[EBX].i = 27; -347 % Reg[ECX].i = 1; -348 == 0x1 -349 # op ModR/M SIB displacement immediate -350 d3 eb # negate EBX -351 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -352 +run: operate on r/m32 -353 +run: r/m32 is EBX -354 +run: subop: shift right by CL bits, while padding zeroes -355 # result: 13 -356 +run: storing 0x0000000d -357 -358 :(scenario shift_right_logical_negative_r32_with_cl) -359 % Reg[EBX].i = 0xfffffffd; -360 % Reg[ECX].i = 1; -361 == 0x1 -362 # op ModR/M SIB displacement immediate -363 d3 eb # negate EBX -364 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) -365 +run: operate on r/m32 -366 +run: r/m32 is EBX -367 +run: subop: shift right by CL bits, while padding zeroes -368 +run: storing 0x7ffffffe -369 -370 //:: and -371 -372 :(before "End Initialize Op Names") -373 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)"); -374 -375 :(scenario and_r32_with_r32) -376 % Reg[EAX].i = 0x0a0b0c0d; -377 % Reg[EBX].i = 0x000000ff; -378 == 0x1 -379 # op ModR/M SIB displacement immediate -380 21 d8 # and EBX with destination EAX -381 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -382 +run: and EBX with r/m32 -383 +run: r/m32 is EAX -384 +run: storing 0x0000000d -385 -386 :(before "End Single-Byte Opcodes") -387 case 0x21: { // and r32 with r/m32 -388 const uint8_t modrm = next(); -389 const uint8_t arg2 = (modrm>>3)&0x7; -390 trace(90, "run") << "and " << rname(arg2) << " with r/m32" << end(); -391 int32_t* arg1 = effective_address(modrm); -392 BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u); -393 break; -394 } -395 -396 //:: or -397 -398 :(before "End Initialize Op Names") -399 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)"); -400 -401 :(scenario or_r32_with_r32) -402 % Reg[EAX].i = 0x0a0b0c0d; -403 % Reg[EBX].i = 0xa0b0c0d0; -404 == 0x1 -405 # op ModR/M SIB displacement immediate -406 09 d8 # or EBX with destination EAX -407 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -408 +run: or EBX with r/m32 -409 +run: r/m32 is EAX -410 +run: storing 0xaabbccdd -411 -412 :(before "End Single-Byte Opcodes") -413 case 0x09: { // or r32 with r/m32 -414 const uint8_t modrm = next(); -415 const uint8_t arg2 = (modrm>>3)&0x7; -416 trace(90, "run") << "or " << rname(arg2) << " with r/m32" << end(); -417 int32_t* arg1 = effective_address(modrm); -418 BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u); -419 break; -420 } -421 -422 //:: xor -423 -424 :(before "End Initialize Op Names") -425 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)"); -426 -427 :(scenario xor_r32_with_r32) -428 % Reg[EAX].i = 0x0a0b0c0d; -429 % Reg[EBX].i = 0xaabbc0d0; -430 == 0x1 -431 # op ModR/M SIB displacement immediate -432 31 d8 # xor EBX with destination EAX -433 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -434 +run: xor EBX with r/m32 -435 +run: r/m32 is EAX -436 +run: storing 0xa0b0ccdd -437 -438 :(before "End Single-Byte Opcodes") -439 case 0x31: { // xor r32 with r/m32 -440 const uint8_t modrm = next(); -441 const uint8_t arg2 = (modrm>>3)&0x7; -442 trace(90, "run") << "xor " << rname(arg2) << " with r/m32" << end(); -443 int32_t* arg1 = effective_address(modrm); -444 BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u); -445 break; -446 } -447 -448 //:: not -449 -450 :(scenario not_r32) -451 % Reg[EBX].i = 0x0f0f00ff; -452 == 0x1 -453 # op ModR/M SIB displacement immediate -454 f7 d3 # not EBX -455 # ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX) -456 +run: operate on r/m32 -457 +run: r/m32 is EBX -458 +run: subop: not -459 +run: storing 0xf0f0ff00 -460 -461 :(before "End Op f7 Subops") -462 case 2: { // not r/m32 -463 trace(90, "run") << "subop: not" << end(); -464 *arg1 = ~(*arg1); -465 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); -466 SF = (*arg1 >> 31); -467 ZF = (*arg1 == 0); -468 OF = false; -469 break; -470 } -471 -472 //:: compare (cmp) -473 -474 :(before "End Initialize Op Names") -475 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)"); -476 -477 :(scenario compare_r32_with_r32_greater) -478 % Reg[EAX].i = 0x0a0b0c0d; -479 % Reg[EBX].i = 0x0a0b0c07; -480 == 0x1 -481 # op ModR/M SIB displacement immediate -482 39 d8 # compare EBX with EAX -483 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -484 +run: compare EBX with r/m32 -485 +run: r/m32 is EAX -486 +run: SF=0; ZF=0; OF=0 -487 -488 :(before "End Single-Byte Opcodes") -489 case 0x39: { // set SF if r/m32 < r32 -490 const uint8_t modrm = next(); -491 const uint8_t reg2 = (modrm>>3)&0x7; -492 trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end(); -493 const int32_t* arg1 = effective_address(modrm); -494 const int32_t arg2 = Reg[reg2].i; -495 const int32_t tmp1 = *arg1 - arg2; -496 SF = (tmp1 < 0); -497 ZF = (tmp1 == 0); -498 const int64_t tmp2 = *arg1 - arg2; -499 OF = (tmp1 != tmp2); -500 trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); -501 break; -502 } -503 -504 :(scenario compare_r32_with_r32_lesser) -505 % Reg[EAX].i = 0x0a0b0c07; -506 % Reg[EBX].i = 0x0a0b0c0d; -507 == 0x1 -508 # op ModR/M SIB displacement immediate -509 39 d8 # compare EBX with EAX -510 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -511 +run: compare EBX with r/m32 -512 +run: r/m32 is EAX -513 +run: SF=1; ZF=0; OF=0 -514 -515 :(scenario compare_r32_with_r32_equal) -516 % Reg[EAX].i = 0x0a0b0c0d; -517 % Reg[EBX].i = 0x0a0b0c0d; -518 == 0x1 -519 # op ModR/M SIB displacement immediate -520 39 d8 # compare EBX with EAX -521 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -522 +run: compare EBX with r/m32 -523 +run: r/m32 is EAX -524 +run: SF=0; ZF=1; OF=0 -525 -526 //:: copy (mov) -527 -528 :(before "End Initialize Op Names") -529 put_new(Name, "89", "copy r32 to rm32 (mov)"); -530 -531 :(scenario copy_r32_to_r32) -532 % Reg[EBX].i = 0xaf; -533 == 0x1 -534 # op ModR/M SIB displacement immediate -535 89 d8 # copy EBX to EAX -536 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -537 +run: copy EBX to r/m32 -538 +run: r/m32 is EAX -539 +run: storing 0x000000af -540 -541 :(before "End Single-Byte Opcodes") -542 case 0x89: { // copy r32 to r/m32 -543 const uint8_t modrm = next(); -544 const uint8_t rsrc = (modrm>>3)&0x7; -545 trace(90, "run") << "copy " << rname(rsrc) << " to r/m32" << end(); -546 int32_t* dest = effective_address(modrm); -547 *dest = Reg[rsrc].i; -548 trace(90, "run") << "storing 0x" << HEXWORD << *dest << end(); -549 break; -550 } -551 -552 //:: xchg -553 -554 :(before "End Initialize Op Names") -555 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)"); -556 -557 :(scenario xchg_r32_with_r32) -558 % Reg[EBX].i = 0xaf; -559 % Reg[EAX].i = 0x2e; -560 == 0x1 -561 # op ModR/M SIB displacement immediate -562 87 d8 # exchange EBX with EAX -563 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) -564 +run: exchange EBX with r/m32 -565 +run: r/m32 is EAX -566 +run: storing 0x000000af in r/m32 -567 +run: storing 0x0000002e in EBX -568 -569 :(before "End Single-Byte Opcodes") -570 case 0x87: { // exchange r32 with r/m32 -571 const uint8_t modrm = next(); -572 const uint8_t reg2 = (modrm>>3)&0x7; -573 trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end(); -574 int32_t* arg1 = effective_address(modrm); -575 const int32_t tmp = *arg1; -576 *arg1 = Reg[reg2].i; -577 Reg[reg2].i = tmp; -578 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end(); -579 trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end(); -580 break; -581 } -582 -583 //:: increment -584 -585 :(before "End Initialize Op Names") -586 put_new(Name, "40", "increment EAX (inc)"); -587 put_new(Name, "41", "increment ECX (inc)"); -588 put_new(Name, "42", "increment EDX (inc)"); -589 put_new(Name, "43", "increment EBX (inc)"); -590 put_new(Name, "44", "increment ESP (inc)"); -591 put_new(Name, "45", "increment EBP (inc)"); -592 put_new(Name, "46", "increment ESI (inc)"); -593 put_new(Name, "47", "increment EDI (inc)"); -594 -595 :(scenario increment_r32) -596 % Reg[ECX].u = 0x1f; -597 == 0x1 # code segment -598 # op ModR/M SIB displacement immediate -599 41 # increment ECX -600 +run: increment ECX -601 +run: storing value 0x00000020 -602 -603 :(before "End Single-Byte Opcodes") -604 case 0x40: -605 case 0x41: -606 case 0x42: -607 case 0x43: -608 case 0x44: -609 case 0x45: -610 case 0x46: -611 case 0x47: { // increment r32 -612 const uint8_t reg = op & 0x7; -613 trace(90, "run") << "increment " << rname(reg) << end(); -614 ++Reg[reg].u; -615 trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); -616 break; -617 } -618 -619 :(before "End Initialize Op Names") -620 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)"); -621 -622 :(scenario increment_rm32) -623 % Reg[EAX].u = 0x20; -624 == 0x1 # code segment -625 # op ModR/M SIB displacement immediate -626 ff c0 # increment EAX -627 # ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX) -628 +run: increment r/m32 -629 +run: r/m32 is EAX -630 +run: storing value 0x00000021 -631 -632 :(before "End Single-Byte Opcodes") -633 case 0xff: { -634 const uint8_t modrm = next(); -635 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits -636 switch (subop) { -637 case 0: { // increment r/m32 -638 trace(90, "run") << "increment r/m32" << end(); -639 int32_t* arg = effective_address(modrm); -640 ++*arg; -641 trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end(); -642 break; -643 } -644 default: -645 cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n'; -646 DUMP(""); -647 exit(1); -648 // End Op ff Subops -649 } -650 break; -651 } -652 -653 //:: decrement -654 -655 :(before "End Initialize Op Names") -656 put_new(Name, "48", "decrement EAX (dec)"); -657 put_new(Name, "49", "decrement ECX (dec)"); -658 put_new(Name, "4a", "decrement EDX (dec)"); -659 put_new(Name, "4b", "decrement EBX (dec)"); -660 put_new(Name, "4c", "decrement ESP (dec)"); -661 put_new(Name, "4d", "decrement EBP (dec)"); -662 put_new(Name, "4e", "decrement ESI (dec)"); -663 put_new(Name, "4f", "decrement EDI (dec)"); -664 -665 :(scenario decrement_r32) -666 % Reg[ECX].u = 0x1f; -667 == 0x1 # code segment -668 # op ModR/M SIB displacement immediate -669 49 # decrement ECX -670 +run: decrement ECX -671 +run: storing value 0x0000001e -672 -673 :(before "End Single-Byte Opcodes") -674 case 0x48: -675 case 0x49: -676 case 0x4a: -677 case 0x4b: -678 case 0x4c: -679 case 0x4d: -680 case 0x4e: -681 case 0x4f: { // decrement r32 -682 const uint8_t reg = op & 0x7; -683 trace(90, "run") << "decrement " << rname(reg) << end(); -684 --Reg[reg].u; -685 trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); -686 break; -687 } -688 -689 :(scenario decrement_rm32) -690 % Reg[EAX].u = 0x20; -691 == 0x1 # code segment -692 # op ModR/M SIB displacement immediate -693 ff c8 # decrement EAX -694 # ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX) -695 +run: decrement r/m32 -696 +run: r/m32 is EAX -697 +run: storing value 0x0000001f -698 -699 :(before "End Op ff Subops") -700 case 1: { // decrement r/m32 -701 trace(90, "run") << "decrement r/m32" << end(); -702 int32_t* arg = effective_address(modrm); -703 --*arg; -704 trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end(); -705 break; -706 } -707 -708 //:: push -709 -710 :(before "End Initialize Op Names") -711 put_new(Name, "50", "push EAX to stack (push)"); -712 put_new(Name, "51", "push ECX to stack (push)"); -713 put_new(Name, "52", "push EDX to stack (push)"); -714 put_new(Name, "53", "push EBX to stack (push)"); -715 put_new(Name, "54", "push ESP to stack (push)"); -716 put_new(Name, "55", "push EBP to stack (push)"); -717 put_new(Name, "56", "push ESI to stack (push)"); -718 put_new(Name, "57", "push EDI to stack (push)"); -719 -720 :(scenario push_r32) -721 % Reg[ESP].u = 0x64; -722 % Reg[EBX].i = 0x0000000a; -723 == 0x1 -724 # op ModR/M SIB displacement immediate -725 53 # push EBX to stack -726 +run: push EBX -727 +run: decrementing ESP to 0x00000060 -728 +run: pushing value 0x0000000a -729 -730 :(before "End Single-Byte Opcodes") -731 case 0x50: -732 case 0x51: -733 case 0x52: -734 case 0x53: -735 case 0x54: -736 case 0x55: -737 case 0x56: -738 case 0x57: { // push r32 to stack -739 uint8_t reg = op & 0x7; -740 trace(90, "run") << "push " << rname(reg) << end(); -741 //? cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n'; -742 push(Reg[reg].u); -743 break; -744 } -745 -746 //:: pop -747 -748 :(before "End Initialize Op Names") -749 put_new(Name, "58", "pop top of stack to EAX (pop)"); -750 put_new(Name, "59", "pop top of stack to ECX (pop)"); -751 put_new(Name, "5a", "pop top of stack to EDX (pop)"); -752 put_new(Name, "5b", "pop top of stack to EBX (pop)"); -753 put_new(Name, "5c", "pop top of stack to ESP (pop)"); -754 put_new(Name, "5d", "pop top of stack to EBP (pop)"); -755 put_new(Name, "5e", "pop top of stack to ESI (pop)"); -756 put_new(Name, "5f", "pop top of stack to EDI (pop)"); -757 -758 :(scenario pop_r32) -759 % Reg[ESP].u = 0x02000000; -760 % Mem.push_back(vma(0x02000000)); // manually allocate memory -761 % write_mem_i32(0x02000000, 0x0000000a); // ..before this write -762 == 0x1 # code segment -763 # op ModR/M SIB displacement immediate -764 5b # pop stack to EBX -765 == 0x2000 # data segment -766 0a 00 00 00 # 0x0a -767 +run: pop into EBX -768 +run: popping value 0x0000000a -769 +run: incrementing ESP to 0x02000004 -770 -771 :(before "End Single-Byte Opcodes") -772 case 0x58: -773 case 0x59: -774 case 0x5a: -775 case 0x5b: -776 case 0x5c: -777 case 0x5d: -778 case 0x5e: -779 case 0x5f: { // pop stack into r32 -780 const uint8_t reg = op & 0x7; -781 trace(90, "run") << "pop into " << rname(reg) << end(); -782 //? cerr << "pop from " << Reg[ESP].u << '\n'; -783 Reg[reg].u = pop(); -784 //? cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n'; -785 break; -786 } -787 :(code) -788 uint32_t pop() { -789 const uint32_t result = read_mem_u32(Reg[ESP].u); -790 trace(90, "run") << "popping value 0x" << HEXWORD << result << end(); -791 Reg[ESP].u += 4; -792 trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); -793 return result; -794 } + 59 // Found effective_address(addr) + 60 return addr; + 61 } + 62 + 63 string rname(uint8_t r) { + 64 switch (r) { + 65 case 0: return "EAX"; + 66 case 1: return "ECX"; + 67 case 2: return "EDX"; + 68 case 3: return "EBX"; + 69 case 4: return "ESP"; + 70 case 5: return "EBP"; + 71 case 6: return "ESI"; + 72 case 7: return "EDI"; + 73 default: raise << "invalid register " << r << '\n' << end(); return ""; + 74 } + 75 } + 76 + 77 //:: subtract + 78 + 79 :(before "End Initialize Op Names") + 80 put_new(Name, "29", "subtract r32 from rm32 (sub)"); + 81 + 82 :(scenario subtract_r32_from_r32) + 83 % Reg[EAX].i = 10; + 84 % Reg[EBX].i = 1; + 85 == 0x1 + 86 # op ModR/M SIB displacement immediate + 87 29 d8 # subtract EBX from EAX + 88 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + 89 +run: subtract EBX from r/m32 + 90 +run: r/m32 is EAX + 91 +run: storing 0x00000009 + 92 + 93 :(before "End Single-Byte Opcodes") + 94 case 0x29: { // subtract r32 from r/m32 + 95 const uint8_t modrm = next(); + 96 const uint8_t arg2 = (modrm>>3)&0x7; + 97 trace(90, "run") << "subtract " << rname(arg2) << " from r/m32" << end(); + 98 int32_t* arg1 = effective_address(modrm); + 99 BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i); +100 break; +101 } +102 +103 //:: multiply +104 +105 :(before "End Initialize Op Names") +106 put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul)"); +107 +108 :(scenario multiply_eax_by_r32) +109 % Reg[EAX].i = 4; +110 % Reg[ECX].i = 3; +111 == 0x1 +112 # op ModR/M SIB displacement immediate +113 f7 e1 # multiply EAX by ECX +114 # ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX) +115 +run: operate on r/m32 +116 +run: r/m32 is ECX +117 +run: subop: multiply EAX by r/m32 +118 +run: storing 0x0000000c +119 +120 :(before "End Single-Byte Opcodes") +121 case 0xf7: { +122 const uint8_t modrm = next(); +123 trace(90, "run") << "operate on r/m32" << end(); +124 int32_t* arg1 = effective_address(modrm); +125 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits +126 switch (subop) { +127 case 4: { // mul unsigned EAX by r/m32 +128 trace(90, "run") << "subop: multiply EAX by r/m32" << end(); +129 const uint64_t result = Reg[EAX].u * static_cast<uint32_t>(*arg1); +130 Reg[EAX].u = result & 0xffffffff; +131 Reg[EDX].u = result >> 32; +132 OF = (Reg[EDX].u != 0); +133 trace(90, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end(); +134 break; +135 } +136 // End Op f7 Subops +137 default: +138 cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n'; +139 exit(1); +140 } +141 break; +142 } +143 +144 //: +145 +146 :(before "End Initialize Op Names") +147 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)"); +148 +149 :(scenario multiply_r32_into_r32) +150 % Reg[EAX].i = 4; +151 % Reg[EBX].i = 2; +152 == 0x1 +153 # op ModR/M SIB displacement immediate +154 0f af d8 # subtract EBX into EAX +155 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +156 +run: multiply r/m32 into EBX +157 +run: r/m32 is EAX +158 +run: storing 0x00000008 +159 +160 :(before "End Two-Byte Opcodes Starting With 0f") +161 case 0xaf: { // multiply r32 into r/m32 +162 const uint8_t modrm = next(); +163 const uint8_t arg2 = (modrm>>3)&0x7; +164 trace(90, "run") << "multiply r/m32 into " << rname(arg2) << end(); +165 const int32_t* arg1 = effective_address(modrm); +166 BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1); +167 break; +168 } +169 +170 //:: negate +171 +172 :(scenario negate_r32) +173 % Reg[EBX].i = 1; +174 == 0x1 +175 # op ModR/M SIB displacement immediate +176 f7 db # negate EBX +177 # ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) +178 +run: operate on r/m32 +179 +run: r/m32 is EBX +180 +run: subop: negate +181 +run: storing 0xffffffff +182 +183 :(before "End Op f7 Subops") +184 case 3: { // negate r/m32 +185 trace(90, "run") << "subop: negate" << end(); +186 // one case that can overflow +187 if (static_cast<uint32_t>(*arg1) == 0x80000000) { +188 trace(90, "run") << "overflow" << end(); +189 SF = true; +190 ZF = false; +191 OF = true; +192 break; +193 } +194 *arg1 = -(*arg1); +195 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); +196 SF = (*arg1 >> 31); +197 ZF = (*arg1 == 0); +198 OF = false; +199 break; +200 } +201 +202 :(scenario negate_can_overflow) // in exactly one situation +203 % Reg[EBX].i = 0x80000000; // INT_MIN +204 == 0x1 +205 # op ModR/M SIB displacement immediate +206 f7 db # negate EBX +207 # ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX) +208 +run: operate on r/m32 +209 +run: r/m32 is EBX +210 +run: subop: negate +211 +run: overflow +212 +213 //:: shift left +214 +215 :(before "End Initialize Op Names") +216 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); +217 +218 :(scenario shift_left_r32_with_cl) +219 % Reg[EBX].i = 13; +220 % Reg[ECX].i = 1; +221 == 0x1 +222 # op ModR/M SIB displacement immediate +223 d3 e3 # negate EBX +224 # ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) +225 +run: operate on r/m32 +226 +run: r/m32 is EBX +227 +run: subop: shift left by CL bits +228 +run: storing 0x0000001a +229 +230 :(before "End Single-Byte Opcodes") +231 case 0xd3: { +232 const uint8_t modrm = next(); +233 trace(90, "run") << "operate on r/m32" << end(); +234 int32_t* arg1 = effective_address(modrm); +235 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits +236 switch (subop) { +237 case 4: { // shift left r/m32 by CL +238 trace(90, "run") << "subop: shift left by CL bits" << end(); +239 uint8_t count = Reg[ECX].u & 0x1f; +240 // OF is only defined if count is 1 +241 if (count == 1) { +242 bool msb = (*arg1 & 0x80000000) >> 1; +243 bool pnsb = (*arg1 & 0x40000000); +244 OF = (msb != pnsb); +245 } +246 *arg1 = (*arg1 << count); +247 ZF = (*arg1 == 0); +248 SF = (*arg1 < 0); +249 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); +250 break; +251 } +252 // End Op d3 Subops +253 default: +254 cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n'; +255 exit(1); +256 } +257 break; +258 } +259 +260 //:: shift right arithmetic +261 +262 :(scenario shift_right_arithmetic_r32_with_cl) +263 % Reg[EBX].i = 26; +264 % Reg[ECX].i = 1; +265 == 0x1 +266 # op ModR/M SIB displacement immediate +267 d3 fb # negate EBX +268 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) +269 +run: operate on r/m32 +270 +run: r/m32 is EBX +271 +run: subop: shift right by CL bits, while preserving sign +272 +run: storing 0x0000000d +273 +274 :(before "End Op d3 Subops") +275 case 7: { // shift right r/m32 by CL, preserving sign +276 trace(90, "run") << "subop: shift right by CL bits, while preserving sign" << end(); +277 uint8_t count = Reg[ECX].u & 0x1f; +278 *arg1 = (*arg1 >> count); +279 ZF = (*arg1 == 0); +280 SF = (*arg1 < 0); +281 // OF is only defined if count is 1 +282 if (count == 1) OF = false; +283 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); +284 break; +285 } +286 +287 :(scenario shift_right_arithmetic_odd_r32_with_cl) +288 % Reg[EBX].i = 27; +289 % Reg[ECX].i = 1; +290 == 0x1 +291 # op ModR/M SIB displacement immediate +292 d3 fb # negate EBX +293 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) +294 +run: operate on r/m32 +295 +run: r/m32 is EBX +296 +run: subop: shift right by CL bits, while preserving sign +297 # result: 13 +298 +run: storing 0x0000000d +299 +300 :(scenario shift_right_arithmetic_negative_r32_with_cl) +301 % Reg[EBX].i = 0xfffffffd; // -3 +302 % Reg[ECX].i = 1; +303 == 0x1 +304 # op ModR/M SIB displacement immediate +305 d3 fb # negate EBX +306 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) +307 +run: operate on r/m32 +308 +run: r/m32 is EBX +309 +run: subop: shift right by CL bits, while preserving sign +310 # result: -2 +311 +run: storing 0xfffffffe +312 +313 //:: shift right logical +314 +315 :(scenario shift_right_logical_r32_with_cl) +316 % Reg[EBX].i = 26; +317 % Reg[ECX].i = 1; +318 == 0x1 +319 # op ModR/M SIB displacement immediate +320 d3 eb # negate EBX +321 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) +322 +run: operate on r/m32 +323 +run: r/m32 is EBX +324 +run: subop: shift right by CL bits, while padding zeroes +325 +run: storing 0x0000000d +326 +327 :(before "End Op d3 Subops") +328 case 5: { // shift right r/m32 by CL, preserving sign +329 trace(90, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); +330 uint8_t count = Reg[ECX].u & 0x1f; +331 // OF is only defined if count is 1 +332 if (count == 1) { +333 bool msb = (*arg1 & 0x80000000) >> 1; +334 bool pnsb = (*arg1 & 0x40000000); +335 OF = (msb != pnsb); +336 } +337 uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1); +338 *uarg1 = (*uarg1 >> count); +339 ZF = (*uarg1 == 0); +340 // result is always positive by definition +341 SF = false; +342 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); +343 break; +344 } +345 +346 :(scenario shift_right_logical_odd_r32_with_cl) +347 % Reg[EBX].i = 27; +348 % Reg[ECX].i = 1; +349 == 0x1 +350 # op ModR/M SIB displacement immediate +351 d3 eb # negate EBX +352 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) +353 +run: operate on r/m32 +354 +run: r/m32 is EBX +355 +run: subop: shift right by CL bits, while padding zeroes +356 # result: 13 +357 +run: storing 0x0000000d +358 +359 :(scenario shift_right_logical_negative_r32_with_cl) +360 % Reg[EBX].i = 0xfffffffd; +361 % Reg[ECX].i = 1; +362 == 0x1 +363 # op ModR/M SIB displacement immediate +364 d3 eb # negate EBX +365 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) +366 +run: operate on r/m32 +367 +run: r/m32 is EBX +368 +run: subop: shift right by CL bits, while padding zeroes +369 +run: storing 0x7ffffffe +370 +371 //:: and +372 +373 :(before "End Initialize Op Names") +374 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)"); +375 +376 :(scenario and_r32_with_r32) +377 % Reg[EAX].i = 0x0a0b0c0d; +378 % Reg[EBX].i = 0x000000ff; +379 == 0x1 +380 # op ModR/M SIB displacement immediate +381 21 d8 # and EBX with destination EAX +382 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +383 +run: and EBX with r/m32 +384 +run: r/m32 is EAX +385 +run: storing 0x0000000d +386 +387 :(before "End Single-Byte Opcodes") +388 case 0x21: { // and r32 with r/m32 +389 const uint8_t modrm = next(); +390 const uint8_t arg2 = (modrm>>3)&0x7; +391 trace(90, "run") << "and " << rname(arg2) << " with r/m32" << end(); +392 int32_t* arg1 = effective_address(modrm); +393 BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u); +394 break; +395 } +396 +397 //:: or +398 +399 :(before "End Initialize Op Names") +400 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)"); +401 +402 :(scenario or_r32_with_r32) +403 % Reg[EAX].i = 0x0a0b0c0d; +404 % Reg[EBX].i = 0xa0b0c0d0; +405 == 0x1 +406 # op ModR/M SIB displacement immediate +407 09 d8 # or EBX with destination EAX +408 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +409 +run: or EBX with r/m32 +410 +run: r/m32 is EAX +411 +run: storing 0xaabbccdd +412 +413 :(before "End Single-Byte Opcodes") +414 case 0x09: { // or r32 with r/m32 +415 const uint8_t modrm = next(); +416 const uint8_t arg2 = (modrm>>3)&0x7; +417 trace(90, "run") << "or " << rname(arg2) << " with r/m32" << end(); +418 int32_t* arg1 = effective_address(modrm); +419 BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u); +420 break; +421 } +422 +423 //:: xor +424 +425 :(before "End Initialize Op Names") +426 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)"); +427 +428 :(scenario xor_r32_with_r32) +429 % Reg[EAX].i = 0x0a0b0c0d; +430 % Reg[EBX].i = 0xaabbc0d0; +431 == 0x1 +432 # op ModR/M SIB displacement immediate +433 31 d8 # xor EBX with destination EAX +434 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +435 +run: xor EBX with r/m32 +436 +run: r/m32 is EAX +437 +run: storing 0xa0b0ccdd +438 +439 :(before "End Single-Byte Opcodes") +440 case 0x31: { // xor r32 with r/m32 +441 const uint8_t modrm = next(); +442 const uint8_t arg2 = (modrm>>3)&0x7; +443 trace(90, "run") << "xor " << rname(arg2) << " with r/m32" << end(); +444 int32_t* arg1 = effective_address(modrm); +445 BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u); +446 break; +447 } +448 +449 //:: not +450 +451 :(scenario not_r32) +452 % Reg[EBX].i = 0x0f0f00ff; +453 == 0x1 +454 # op ModR/M SIB displacement immediate +455 f7 d3 # not EBX +456 # ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX) +457 +run: operate on r/m32 +458 +run: r/m32 is EBX +459 +run: subop: not +460 +run: storing 0xf0f0ff00 +461 +462 :(before "End Op f7 Subops") +463 case 2: { // not r/m32 +464 trace(90, "run") << "subop: not" << end(); +465 *arg1 = ~(*arg1); +466 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); +467 SF = (*arg1 >> 31); +468 ZF = (*arg1 == 0); +469 OF = false; +470 break; +471 } +472 +473 //:: compare (cmp) +474 +475 :(before "End Initialize Op Names") +476 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)"); +477 +478 :(scenario compare_r32_with_r32_greater) +479 % Reg[EAX].i = 0x0a0b0c0d; +480 % Reg[EBX].i = 0x0a0b0c07; +481 == 0x1 +482 # op ModR/M SIB displacement immediate +483 39 d8 # compare EBX with EAX +484 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +485 +run: compare EBX with r/m32 +486 +run: r/m32 is EAX +487 +run: SF=0; ZF=0; OF=0 +488 +489 :(before "End Single-Byte Opcodes") +490 case 0x39: { // set SF if r/m32 < r32 +491 const uint8_t modrm = next(); +492 const uint8_t reg2 = (modrm>>3)&0x7; +493 trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end(); +494 const int32_t* arg1 = effective_address(modrm); +495 const int32_t arg2 = Reg[reg2].i; +496 const int32_t tmp1 = *arg1 - arg2; +497 SF = (tmp1 < 0); +498 ZF = (tmp1 == 0); +499 const int64_t tmp2 = *arg1 - arg2; +500 OF = (tmp1 != tmp2); +501 trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); +502 break; +503 } +504 +505 :(scenario compare_r32_with_r32_lesser) +506 % Reg[EAX].i = 0x0a0b0c07; +507 % Reg[EBX].i = 0x0a0b0c0d; +508 == 0x1 +509 # op ModR/M SIB displacement immediate +510 39 d8 # compare EBX with EAX +511 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +512 +run: compare EBX with r/m32 +513 +run: r/m32 is EAX +514 +run: SF=1; ZF=0; OF=0 +515 +516 :(scenario compare_r32_with_r32_equal) +517 % Reg[EAX].i = 0x0a0b0c0d; +518 % Reg[EBX].i = 0x0a0b0c0d; +519 == 0x1 +520 # op ModR/M SIB displacement immediate +521 39 d8 # compare EBX with EAX +522 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +523 +run: compare EBX with r/m32 +524 +run: r/m32 is EAX +525 +run: SF=0; ZF=1; OF=0 +526 +527 //:: copy (mov) +528 +529 :(before "End Initialize Op Names") +530 put_new(Name, "89", "copy r32 to rm32 (mov)"); +531 +532 :(scenario copy_r32_to_r32) +533 % Reg[EBX].i = 0xaf; +534 == 0x1 +535 # op ModR/M SIB displacement immediate +536 89 d8 # copy EBX to EAX +537 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +538 +run: copy EBX to r/m32 +539 +run: r/m32 is EAX +540 +run: storing 0x000000af +541 +542 :(before "End Single-Byte Opcodes") +543 case 0x89: { // copy r32 to r/m32 +544 const uint8_t modrm = next(); +545 const uint8_t rsrc = (modrm>>3)&0x7; +546 trace(90, "run") << "copy " << rname(rsrc) << " to r/m32" << end(); +547 int32_t* dest = effective_address(modrm); +548 *dest = Reg[rsrc].i; +549 trace(90, "run") << "storing 0x" << HEXWORD << *dest << end(); +550 break; +551 } +552 +553 //:: xchg +554 +555 :(before "End Initialize Op Names") +556 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)"); +557 +558 :(scenario xchg_r32_with_r32) +559 % Reg[EBX].i = 0xaf; +560 % Reg[EAX].i = 0x2e; +561 == 0x1 +562 # op ModR/M SIB displacement immediate +563 87 d8 # exchange EBX with EAX +564 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) +565 +run: exchange EBX with r/m32 +566 +run: r/m32 is EAX +567 +run: storing 0x000000af in r/m32 +568 +run: storing 0x0000002e in EBX +569 +570 :(before "End Single-Byte Opcodes") +571 case 0x87: { // exchange r32 with r/m32 +572 const uint8_t modrm = next(); +573 const uint8_t reg2 = (modrm>>3)&0x7; +574 trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end(); +575 int32_t* arg1 = effective_address(modrm); +576 const int32_t tmp = *arg1; +577 *arg1 = Reg[reg2].i; +578 Reg[reg2].i = tmp; +579 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end(); +580 trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end(); +581 break; +582 } +583 +584 //:: increment +585 +586 :(before "End Initialize Op Names") +587 put_new(Name, "40", "increment EAX (inc)"); +588 put_new(Name, "41", "increment ECX (inc)"); +589 put_new(Name, "42", "increment EDX (inc)"); +590 put_new(Name, "43", "increment EBX (inc)"); +591 put_new(Name, "44", "increment ESP (inc)"); +592 put_new(Name, "45", "increment EBP (inc)"); +593 put_new(Name, "46", "increment ESI (inc)"); +594 put_new(Name, "47", "increment EDI (inc)"); +595 +596 :(scenario increment_r32) +597 % Reg[ECX].u = 0x1f; +598 == 0x1 # code segment +599 # op ModR/M SIB displacement immediate +600 41 # increment ECX +601 +run: increment ECX +602 +run: storing value 0x00000020 +603 +604 :(before "End Single-Byte Opcodes") +605 case 0x40: +606 case 0x41: +607 case 0x42: +608 case 0x43: +609 case 0x44: +610 case 0x45: +611 case 0x46: +612 case 0x47: { // increment r32 +613 const uint8_t reg = op & 0x7; +614 trace(90, "run") << "increment " << rname(reg) << end(); +615 ++Reg[reg].u; +616 trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); +617 break; +618 } +619 +620 :(before "End Initialize Op Names") +621 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)"); +622 +623 :(scenario increment_rm32) +624 % Reg[EAX].u = 0x20; +625 == 0x1 # code segment +626 # op ModR/M SIB displacement immediate +627 ff c0 # increment EAX +628 # ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX) +629 +run: increment r/m32 +630 +run: r/m32 is EAX +631 +run: storing value 0x00000021 +632 +633 :(before "End Single-Byte Opcodes") +634 case 0xff: { +635 const uint8_t modrm = next(); +636 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits +637 switch (subop) { +638 case 0: { // increment r/m32 +639 trace(90, "run") << "increment r/m32" << end(); +640 int32_t* arg = effective_address(modrm); +641 ++*arg; +642 trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end(); +643 break; +644 } +645 default: +646 cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n'; +647 DUMP(""); +648 exit(1); +649 // End Op ff Subops +650 } +651 break; +652 } +653 +654 //:: decrement +655 +656 :(before "End Initialize Op Names") +657 put_new(Name, "48", "decrement EAX (dec)"); +658 put_new(Name, "49", "decrement ECX (dec)"); +659 put_new(Name, "4a", "decrement EDX (dec)"); +660 put_new(Name, "4b", "decrement EBX (dec)"); +661 put_new(Name, "4c", "decrement ESP (dec)"); +662 put_new(Name, "4d", "decrement EBP (dec)"); +663 put_new(Name, "4e", "decrement ESI (dec)"); +664 put_new(Name, "4f", "decrement EDI (dec)"); +665 +666 :(scenario decrement_r32) +667 % Reg[ECX].u = 0x1f; +668 == 0x1 # code segment +669 # op ModR/M SIB displacement immediate +670 49 # decrement ECX +671 +run: decrement ECX +672 +run: storing value 0x0000001e +673 +674 :(before "End Single-Byte Opcodes") +675 case 0x48: +676 case 0x49: +677 case 0x4a: +678 case 0x4b: +679 case 0x4c: +680 case 0x4d: +681 case 0x4e: +682 case 0x4f: { // decrement r32 +683 const uint8_t reg = op & 0x7; +684 trace(90, "run") << "decrement " << rname(reg) << end(); +685 --Reg[reg].u; +686 trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end(); +687 break; +688 } +689 +690 :(scenario decrement_rm32) +691 % Reg[EAX].u = 0x20; +692 == 0x1 # code segment +693 # op ModR/M SIB displacement immediate +694 ff c8 # decrement EAX +695 # ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX) +696 +run: decrement r/m32 +697 +run: r/m32 is EAX +698 +run: storing value 0x0000001f +699 +700 :(before "End Op ff Subops") +701 case 1: { // decrement r/m32 +702 trace(90, "run") << "decrement r/m32" << end(); +703 int32_t* arg = effective_address(modrm); +704 --*arg; +705 trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end(); +706 break; +707 } +708 +709 //:: push +710 +711 :(before "End Initialize Op Names") +712 put_new(Name, "50", "push EAX to stack (push)"); +713 put_new(Name, "51", "push ECX to stack (push)"); +714 put_new(Name, "52", "push EDX to stack (push)"); +715 put_new(Name, "53", "push EBX to stack (push)"); +716 put_new(Name, "54", "push ESP to stack (push)"); +717 put_new(Name, "55", "push EBP to stack (push)"); +718 put_new(Name, "56", "push ESI to stack (push)"); +719 put_new(Name, "57", "push EDI to stack (push)"); +720 +721 :(scenario push_r32) +722 % Reg[ESP].u = 0x64; +723 % Reg[EBX].i = 0x0000000a; +724 == 0x1 +725 # op ModR/M SIB displacement immediate +726 53 # push EBX to stack +727 +run: push EBX +728 +run: decrementing ESP to 0x00000060 +729 +run: pushing value 0x0000000a +730 +731 :(before "End Single-Byte Opcodes") +732 case 0x50: +733 case 0x51: +734 case 0x52: +735 case 0x53: +736 case 0x54: +737 case 0x55: +738 case 0x56: +739 case 0x57: { // push r32 to stack +740 uint8_t reg = op & 0x7; +741 trace(90, "run") << "push " << rname(reg) << end(); +742 //? cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n'; +743 push(Reg[reg].u); +744 break; +745 } +746 +747 //:: pop +748 +749 :(before "End Initialize Op Names") +750 put_new(Name, "58", "pop top of stack to EAX (pop)"); +751 put_new(Name, "59", "pop top of stack to ECX (pop)"); +752 put_new(Name, "5a", "pop top of stack to EDX (pop)"); +753 put_new(Name, "5b", "pop top of stack to EBX (pop)"); +754 put_new(Name, "5c", "pop top of stack to ESP (pop)"); +755 put_new(Name, "5d", "pop top of stack to EBP (pop)"); +756 put_new(Name, "5e", "pop top of stack to ESI (pop)"); +757 put_new(Name, "5f", "pop top of stack to EDI (pop)"); +758 +759 :(scenario pop_r32) +760 % Reg[ESP].u = 0x02000000; +761 % Mem.push_back(vma(0x02000000)); // manually allocate memory +762 % write_mem_i32(0x02000000, 0x0000000a); // ..before this write +763 == 0x1 # code segment +764 # op ModR/M SIB displacement immediate +765 5b # pop stack to EBX +766 == 0x2000 # data segment +767 0a 00 00 00 # 0x0a +768 +run: pop into EBX +769 +run: popping value 0x0000000a +770 +run: incrementing ESP to 0x02000004 +771 +772 :(before "End Single-Byte Opcodes") +773 case 0x58: +774 case 0x59: +775 case 0x5a: +776 case 0x5b: +777 case 0x5c: +778 case 0x5d: +779 case 0x5e: +780 case 0x5f: { // pop stack into r32 +781 const uint8_t reg = op & 0x7; +782 trace(90, "run") << "pop into " << rname(reg) << end(); +783 //? cerr << "pop from " << Reg[ESP].u << '\n'; +784 Reg[reg].u = pop(); +785 //? cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n'; +786 break; +787 } +788 :(code) +789 uint32_t pop() { +790 const uint32_t result = read_mem_u32(Reg[ESP].u); +791 trace(90, "run") << "popping value 0x" << HEXWORD << result << end(); +792 Reg[ESP].u += 4; +793 trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); +794 return result; +795 } diff --git a/html/subx/014indirect_addressing.cc.html b/html/subx/014indirect_addressing.cc.html index b719b747..27499260 100644 --- a/html/subx/014indirect_addressing.cc.html +++ b/html/subx/014indirect_addressing.cc.html @@ -81,7 +81,7 @@ if ('onhashchange' in window) { 18 case 0: // indirect addressing 19 switch (rm) { 20 default: // address in register - 21 trace(90, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end(); + 21 trace(90, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end(); 22 addr = Reg[rm].u; 23 break; 24 // End Mod 0 Special-cases(addr) @@ -110,7 +110,7 @@ if ('onhashchange' in window) { 47 case 0x03: { // add r/m32 to r32 48 const uint8_t modrm = next(); 49 const uint8_t arg1 = (modrm>>3)&0x7; - 50 trace(90, "run") << "add r/m32 to " << rname(arg1) << end(); + 50 trace(90, "run") << "add r/m32 to " << rname(arg1) << end(); 51 const int32_t* arg2 = effective_address(modrm); 52 BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2); 53 break; @@ -153,7 +153,7 @@ if ('onhashchange' in window) { 90 case 0x2b: { // subtract r/m32 from r32 91 const uint8_t modrm = next(); 92 const uint8_t arg1 = (modrm>>3)&0x7; - 93 trace(90, "run") << "subtract r/m32 from " << rname(arg1) << end(); + 93 trace(90, "run") << "subtract r/m32 from " << rname(arg1) << end(); 94 const int32_t* arg2 = effective_address(modrm); 95 BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2); 96 break; @@ -196,7 +196,7 @@ if ('onhashchange' in window) { 133 case 0x23: { // and r/m32 with r32 134 const uint8_t modrm = next(); 135 const uint8_t arg1 = (modrm>>3)&0x7; -136 trace(90, "run") << "and r/m32 with " << rname(arg1) << end(); +136 trace(90, "run") << "and r/m32 with " << rname(arg1) << end(); 137 const int32_t* arg2 = effective_address(modrm); 138 BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2); 139 break; @@ -239,7 +239,7 @@ if ('onhashchange' in window) { 176 case 0x0b: { // or r/m32 with r32 177 const uint8_t modrm = next(); 178 const uint8_t arg1 = (modrm>>3)&0x7; -179 trace(90, "run") << "or r/m32 with " << rname(arg1) << end(); +179 trace(90, "run") << "or r/m32 with " << rname(arg1) << end(); 180 const int32_t* arg2 = effective_address(modrm); 181 BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2); 182 break; @@ -282,7 +282,7 @@ if ('onhashchange' in window) { 219 case 0x33: { // xor r/m32 with r32 220 const uint8_t modrm = next(); 221 const uint8_t arg1 = (modrm>>3)&0x7; -222 trace(90, "run") << "xor r/m32 with " << rname(arg1) << end(); +222 trace(90, "run") << "xor r/m32 with " << rname(arg1) << end(); 223 const int32_t* arg2 = effective_address(modrm); 224 BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2); 225 break; @@ -366,7 +366,7 @@ if ('onhashchange' in window) { 303 case 0x3b: { // set SF if r32 < r/m32 304 const uint8_t modrm = next(); 305 const uint8_t reg1 = (modrm>>3)&0x7; -306 trace(90, "run") << "compare r/m32 with " << rname(reg1) << end(); +306 trace(90, "run") << "compare r/m32 with " << rname(reg1) << end(); 307 const int32_t arg1 = Reg[reg1].i; 308 const int32_t* arg2 = effective_address(modrm); 309 const int32_t tmp1 = arg1 - *arg2; @@ -438,7 +438,7 @@ if ('onhashchange' in window) { 375 case 0x8b: { // copy r32 to r/m32 376 const uint8_t modrm = next(); 377 const uint8_t rdest = (modrm>>3)&0x7; -378 trace(90, "run") << "copy r/m32 to " << rname(rdest) << end(); +378 trace(90, "run") << "copy r/m32 to " << rname(rdest) << end(); 379 const int32_t* src = effective_address(modrm); 380 Reg[rdest].i = *src; 381 trace(90, "run") << "storing 0x" << HEXWORD << *src << end(); @@ -511,7 +511,7 @@ if ('onhashchange' in window) { 448 # ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX) 449 == 0x2000 # data segment 450 30 00 00 00 # 0x30 -451 +run: pop into r/m32 +451 +run: pop into r/m32 452 +run: effective address is 0x00000060 (EAX) 453 +run: popping value 0x00000030 454 +run: incrementing ESP to 0x00002004 @@ -524,7 +524,7 @@ if ('onhashchange' in window) { 461 case 0: { 462 trace(90, "run") << "pop into r/m32" << end(); 463 int32_t* dest = effective_address(modrm); -464 *dest = pop(); +464 *dest = pop(); 465 break; 466 } 467 } @@ -572,7 +572,7 @@ if ('onhashchange' in window) { 509 switch (rm) { 510 default: 511 addr = Reg[rm].u; -512 trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); +512 trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); 513 break; 514 // End Mod 1 Special-cases(addr) 515 } @@ -617,7 +617,7 @@ if ('onhashchange' in window) { 554 switch (rm) { 555 default: 556 addr = Reg[rm].u; -557 trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); +557 trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end(); 558 break; 559 // End Mod 2 Special-cases(addr) 560 } @@ -659,7 +659,7 @@ if ('onhashchange' in window) { 596 case 0x8d: { // copy address of m32 to r32 597 const uint8_t modrm = next(); 598 const uint8_t arg1 = (modrm>>3)&0x7; -599 trace(90, "run") << "copy address into " << rname(arg1) << end(); +599 trace(90, "run") << "copy address into " << rname(arg1) << end(); 600 Reg[arg1].u = effective_address_number(modrm); 601 break; 602 } diff --git a/html/subx/015immediate_addressing.cc.html b/html/subx/015immediate_addressing.cc.html index 59813443..b0c76d04 100644 --- a/html/subx/015immediate_addressing.cc.html +++ b/html/subx/015immediate_addressing.cc.html @@ -653,7 +653,7 @@ if ('onhashchange' in window) { 590 case 0xbf: { // copy imm32 to r32 591 const uint8_t rdest = op & 0x7; 592 const int32_t src = next32(); -593 trace(90, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end(); +593 trace(90, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end(); 594 Reg[rdest].i = src; 595 break; 596 } diff --git a/html/subx/016index_addressing.cc.html b/html/subx/016index_addressing.cc.html index 2e775053..64df7366 100644 --- a/html/subx/016index_addressing.cc.html +++ b/html/subx/016index_addressing.cc.html @@ -87,7 +87,7 @@ if ('onhashchange' in window) { 26 uint32_t addr = 0; 27 if (base != EBP || mod != 0) { 28 addr = Reg[base].u; - 29 trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end(); + 29 trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end(); 30 } 31 else { 32 // base == EBP && mod == 0 @@ -102,7 +102,7 @@ if ('onhashchange' in window) { 41 else { 42 const uint8_t scale = (1 << (sib>>6)); 43 addr += Reg[index].i*scale; // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative. - 44 trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end(); + 44 trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end(); 45 } 46 return addr; 47 } diff --git a/html/subx/019functions.cc.html b/html/subx/019functions.cc.html index bbd254bd..b8d3113f 100644 --- a/html/subx/019functions.cc.html +++ b/html/subx/019functions.cc.html @@ -146,7 +146,7 @@ if ('onhashchange' in window) { 84 :(before "End Single-Byte Opcodes") 85 case 0xc3: { // return from a call 86 trace(90, "run") << "return" << end(); -87 EIP = pop(); +87 EIP = pop(); 88 trace(90, "run") << "jumping to 0x" << HEXWORD << EIP << end(); 89 break; 90 } diff --git a/html/subx/021byte_addressing.cc.html b/html/subx/021byte_addressing.cc.html index 71b63a6a..0ddbdc29 100644 --- a/html/subx/021byte_addressing.cc.html +++ b/html/subx/021byte_addressing.cc.html @@ -15,6 +15,7 @@ body { font-size:12pt; font-family: monospace; color: #000000; background-color: a { color:inherit; } * { font-size:12pt; font-size: 1em; } .traceContains { color: #005f00; } +.PreProc { color: #c000c0; } .LineNr { } .Constant { color: #008787; } .Delimiter { color: #c000c0; } @@ -108,7 +109,7 @@ if ('onhashchange' in window) { 48 88 18 # copy BL to the byte at *EAX 49 # ModR/M in binary: 00 (indirect mode) 011 (src BL) 000 (dest EAX) 50 == 0x2000 - 51 f0 cc bb aa # 0xf0 with more data in following bytes + 51 f0 cc bb aa 52 +run: copy BL to r8/m8-at-r32 53 +run: effective address is 0x00002000 (EAX) 54 +run: storing 0xab @@ -158,7 +159,7 @@ if ('onhashchange' in window) { 98 trace(90, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end(); 99 *dest = *src; 100 const uint8_t rdest_32bit = rdest & 0x3; -101 trace(90, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end(); +101 trace(90, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end(); 102 break; 103 } 104 @@ -174,6 +175,42 @@ if ('onhashchange' in window) { 114 +run: storing 0x44 115 # ensure ESI is unchanged 116 % CHECK_EQ(Reg[ESI].u, 0xaabbccdd); +117 +118 //: +119 +120 :(before "End Initialize Op Names") +121 put_new(Name, "c6", "copy imm8 to r8/m8-at-r32 (mov)"); +122 +123 :(scenario copy_imm8_to_mem_at_r32) +124 % Reg[EAX].i = 0x2000; +125 == 0x1 +126 # op ModR/M SIB displacement immediate +127 c6 00 dd # copy to the byte at *EAX +128 # ModR/M in binary: 00 (indirect mode) 000 (unused) 000 (dest EAX) +129 == 0x2000 +130 f0 cc bb aa +131 +run: copy imm8 to r8/m8-at-r32 +132 +run: effective address is 0x00002000 (EAX) +133 +run: storing 0xdd +134 % CHECK_EQ(0xaabbccdd, read_mem_u32(0x2000)); +135 +136 :(before "End Single-Byte Opcodes") +137 case 0xc6: { // copy imm8 to r/m8 +138 const uint8_t modrm = next(); +139 const uint8_t src = next(); +140 trace(90, "run") << "copy imm8 to r8/m8-at-r32" << end(); +141 trace(90, "run") << "imm8 is 0x" << HEXWORD << src << end(); +142 const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits +143 if (subop != 0) { +144 cerr << "unrecognized subop for opcode c6: " << NUM(subop) << " (only 0/copy currently implemented)\n"; +145 exit(1); +146 } +147 // use unsigned to zero-extend 8-bit value to 32 bits +148 uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm)); +149 *dest = src; +150 trace(90, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end(); +151 break; +152 } diff --git a/html/subx/030---operands.cc.html b/html/subx/030---operands.cc.html index cfff1fbe..2d7d4916 100644 --- a/html/subx/030---operands.cc.html +++ b/html/subx/030---operands.cc.html @@ -79,7 +79,7 @@ if ('onhashchange' in window) { 19 "Each operand has a type. An instruction won't have more than one operand of\n" 20 "any type.\n" 21 "Each instruction has some set of allowed operand types. It'll reject others.\n" - 22 "The complete list of operand types: mod, subop, r32 (register), rm32\n" + 22 "The complete list of operand types: mod, subop, r32 (register), rm32\n" 23 "(register or memory), scale, index, base, disp8, disp16, disp32, imm8,\n" 24 "imm32.\n" 25 "Each of these has its own help page. Try reading 'subx help mod' next.\n" diff --git a/html/subx/031check_operands.cc.html b/html/subx/031check_operands.cc.html index 7894e88a..a8ac3502 100644 --- a/html/subx/031check_operands.cc.html +++ b/html/subx/031check_operands.cc.html @@ -89,14 +89,14 @@ if ('onhashchange' in window) { 27 return; 28 } 29 if (op.data == "f3") { - 30 check_operands_f3(inst); + 30 check_operands_f3(inst); 31 return; 32 } 33 check_operands(inst, op); 34 } 35 36 word preprocess_op(word/*copy*/ op) { - 37 op.data = tolower(op.data.c_str()); + 37 op.data = tolower(op.data.c_str()); 38 // opcodes can't be negative 39 if (starts_with(op.data, "0x")) 40 op.data = op.data.substr(2); @@ -290,353 +290,367 @@ if ('onhashchange' in window) { 228 // imm32 imm8 disp32 |disp16 disp8 subop modrm 229 // 0 1 0 |0 0 1 1 230 put(Permitted_operands, "c1", 0x23); // combine -231 -232 //// Class P: op, ModR/M, subop (not r32) and imm32 -233 // imm32 imm8 disp32 |disp16 disp8 subop modrm -234 // 1 0 0 |0 0 1 1 -235 put(Permitted_operands, "81", 0x43); // combine -236 put(Permitted_operands, "c7", 0x43); // copy -237 -238 // End Init Permitted Operands -239 } -240 -241 :(code) -242 #define HAS(bitvector, bit) ((bitvector) & (1 << (bit))) -243 #define SET(bitvector, bit) ((bitvector) | (1 << (bit))) -244 #define CLEAR(bitvector, bit) ((bitvector) & (~(1 << (bit)))) -245 -246 void check_operands(const line& inst, const word& op) { -247 if (!is_hex_byte(op)) return; -248 uint8_t expected_bitvector = get(Permitted_operands, op.data); -249 if (HAS(expected_bitvector, MODRM)) { -250 check_operands_modrm(inst, op); -251 compare_bitvector_modrm(inst, expected_bitvector, op); -252 } -253 else { -254 compare_bitvector(inst, expected_bitvector, op); -255 } -256 } -257 -258 //: Many instructions can be checked just by comparing bitvectors. -259 -260 void compare_bitvector(const line& inst, uint8_t expected, const word& op) { -261 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere -262 uint8_t bitvector = computed_expected_operand_bitvector(inst); -263 if (trace_contains_errors()) return; // duplicate operand type -264 if (bitvector == expected) return; // all good with this instruction -265 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -266 //? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; -267 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand -268 const string& optype = Operand_type_name.at(i); -269 if ((bitvector & 0x1) > (expected & 0x1)) -270 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); -271 else -272 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); -273 // continue giving all errors for a single instruction -274 } -275 // ignore settings in any unused bits -276 } -277 -278 string maybe_name(const word& op) { -279 if (!is_hex_byte(op)) return ""; -280 if (!contains_key(Name, op.data)) return ""; -281 // strip stuff in parens from the name -282 const string& s = get(Name, op.data); -283 return " ("+s.substr(0, s.find(" ("))+')'; -284 } -285 -286 uint32_t computed_expected_operand_bitvector(const line& inst) { -287 uint32_t bitvector = 0; -288 for (int i = /*skip op*/1; i < SIZE(inst.words); ++i) { -289 bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i)); -290 if (trace_contains_errors()) return INVALID_OPERANDS; // duplicate operand type -291 } -292 return bitvector; -293 } -294 -295 bool has_operands(const line& inst) { -296 return SIZE(inst.words) > first_operand(inst); -297 } -298 -299 int first_operand(const line& inst) { -300 if (inst.words.at(0).data == "0f") return 2; -301 if (inst.words.at(0).data == "f2" || inst.words.at(0).data == "f3") { -302 if (inst.words.at(1).data == "0f") -303 return 3; -304 else -305 return 2; -306 } -307 return 1; -308 } -309 -310 // Scan the metadata of 'w' and return the expected bit corresponding to any operand type. -311 // Also raise an error if metadata contains multiple operand types. -312 uint32_t expected_bit_for_received_operand(const word& w) { -313 uint32_t bv = 0; -314 bool found = false; -315 for (int i = 0; i < SIZE(w.metadata); ++i) { -316 string/*copy*/ curr = w.metadata.at(i); -317 if (curr == "mod" || curr == "rm32" || curr == "r32" || curr == "scale" || curr == "index" || curr == "base") -318 curr = "modrm"; -319 else if (!contains_key(Operand_type, curr)) continue; // ignore unrecognized metadata -320 if (found) { -321 raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end(); -322 return INVALID_OPERANDS; -323 } -324 bv = (1 << get(Operand_type, curr)); -325 found = true; -326 } -327 return bv; -328 } -329 -330 :(scenario conflicting_operand_type) -331 % Hide_errors = true; -332 == 0x1 -333 cd/software-interrupt 80/imm8/imm32 -334 +error: '80/imm8/imm32' has conflicting operand types; it should have only one -335 -336 //: Instructions computing effective addresses have more complex rules, so -337 //: we'll hard-code a common set of instruction-decoding rules. -338 -339 :(scenario check_missing_mod_operand) -340 % Hide_errors = true; -341 == 0x1 -342 81 0/add/subop 3/rm32/ebx 1/imm32 -343 +error: '81 0/add/subop 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing mod operand -344 -345 :(code) -346 void check_operands_modrm(const line& inst, const word& op) { -347 if (all_hex_bytes(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere -348 check_operand_metadata_present(inst, "mod", op); -349 check_operand_metadata_present(inst, "rm32", op); -350 // no check for r32; some instructions don't use it; just assume it's 0 if missing -351 if (op.data == "81" || op.data == "8f" || op.data == "ff") { // keep sync'd with 'help subop' -352 check_operand_metadata_present(inst, "subop", op); -353 check_operand_metadata_absent(inst, "r32", op, "should be replaced by subop"); -354 } -355 if (trace_contains_errors()) return; -356 if (metadata(inst, "rm32").data != "4") return; -357 // SIB byte checks -358 uint8_t mod = hex_byte(metadata(inst, "mod").data); -359 if (mod != /*direct*/3) { -360 check_operand_metadata_present(inst, "base", op); -361 check_operand_metadata_present(inst, "index", op); // otherwise why go to SIB? -362 } -363 else { -364 check_operand_metadata_absent(inst, "base", op, "direct mode"); -365 check_operand_metadata_absent(inst, "index", op, "direct mode"); -366 } -367 // no check for scale; 0 (2**0 = 1) by default -368 } -369 -370 // same as compare_bitvector, with a couple of exceptions for modrm-based instructions -371 // exception: modrm instructions can use a displacement on occasion -372 void compare_bitvector_modrm(const line& inst, uint8_t expected, const word& op) { -373 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere -374 uint8_t bitvector = computed_expected_operand_bitvector(inst); -375 if (trace_contains_errors()) return; // duplicate operand type -376 if (bitvector == expected) return; // all good with this instruction -377 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -378 //? cerr << "comparing for modrm " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; -379 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand -380 const string& optype = Operand_type_name.at(i); -381 if (i == DISP8) { -382 int32_t mod = parse_int(metadata(inst, "mod").data); -383 if (mod != 1) -384 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); -385 continue; // exception 2 -386 } -387 if (i == DISP32) { -388 int32_t mod = parse_int(metadata(inst, "mod").data); -389 int32_t rm32 = parse_int(metadata(inst, "rm32").data); -390 if (mod == 0 && rm32 == 5) -391 ; // ok: special-case for loading address from disp32 -392 else if (mod != 2) -393 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); -394 continue; // exception 2 -395 } -396 if ((bitvector & 0x1) > (expected & 0x1)) -397 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); -398 else -399 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); -400 // continue giving all errors for a single instruction -401 } -402 // ignore settings in any unused bits -403 } -404 -405 void check_operand_metadata_present(const line& inst, const string& type, const word& op) { -406 if (!has_operand_metadata(inst, type)) -407 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << type << " operand\n" << end(); -408 } -409 -410 void check_operand_metadata_absent(const line& inst, const string& type, const word& op, const string& msg) { -411 if (has_operand_metadata(inst, type)) -412 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << type << " operand (" << msg << ")\n" << end(); -413 } -414 -415 :(scenarios transform) -416 :(scenario modrm_with_displacement) -417 % Reg[EAX].u = 0x1; -418 == 0x1 -419 # just avoid null pointer -420 8b/copy 1/mod/lookup+disp8 0/rm32/EAX 2/r32/EDX 4/disp8 # copy *(EAX+4) to EDX -421 $error: 0 -422 :(scenarios run) -423 -424 :(scenario conflicting_operands_in_modrm_instruction) -425 % Hide_errors = true; -426 == 0x1 -427 01/add 0/mod 3/mod -428 +error: '01/add 0/mod 3/mod' has conflicting mod operands -429 -430 :(scenario conflicting_operand_type_modrm) -431 % Hide_errors = true; -432 == 0x1 -433 01/add 0/mod 3/rm32/r32 -434 +error: '3/rm32/r32' has conflicting operand types; it should have only one -435 -436 :(scenario check_missing_rm32_operand) -437 % Hide_errors = true; -438 == 0x1 -439 81 0/add/subop 0/mod 1/imm32 -440 +error: '81 0/add/subop 0/mod 1/imm32' (combine rm32 with imm32 based on subop): missing rm32 operand -441 -442 :(scenario check_missing_subop_operand) -443 % Hide_errors = true; -444 == 0x1 -445 81 0/mod 3/rm32/ebx 1/imm32 -446 +error: '81 0/mod 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing subop operand -447 -448 :(scenario check_missing_base_operand) -449 % Hide_errors = true; -450 == 0x1 -451 81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32 -452 +error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32' (combine rm32 with imm32 based on subop): missing base operand -453 -454 :(scenario check_missing_index_operand) -455 % Hide_errors = true; -456 == 0x1 -457 81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32 -458 +error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32' (combine rm32 with imm32 based on subop): missing index operand -459 -460 :(scenario check_missing_base_operand_2) -461 % Hide_errors = true; -462 == 0x1 -463 81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32 -464 +error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32' (combine rm32 with imm32 based on subop): missing base operand -465 -466 :(scenario check_extra_displacement) -467 % Hide_errors = true; -468 == 0x1 -469 89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8 -470 +error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 operand -471 -472 :(scenario check_base_operand_not_needed_in_direct_mode) -473 == 0x1 -474 81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32 -475 $error: 0 -476 -477 :(scenario extra_modrm) -478 % Hide_errors = true; -479 == 0x1 -480 59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP -481 +error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand -482 -483 //:: similarly handle multi-byte opcodes -484 -485 :(code) -486 void check_operands_0f(const line& inst) { -487 assert(inst.words.at(0).data == "0f"); -488 if (SIZE(inst.words) == 1) { -489 raise << "opcode '0f' requires a second opcode\n" << end(); -490 return; -491 } -492 word op = preprocess_op(inst.words.at(1)); -493 if (!contains_key(Name_0f, op.data)) { -494 raise << "unknown 2-byte opcode '0f " << op.data << "'\n" << end(); -495 return; -496 } -497 check_operands_0f(inst, op); -498 } -499 -500 void check_operands_f3(const line& /*unused*/) { -501 raise << "no supported opcodes starting with f3\n" << end(); -502 } -503 -504 :(scenario check_missing_disp32_operand) -505 % Hide_errors = true; -506 == 0x1 -507 # instruction effective address operand displacement immediate -508 # op subop mod rm32 base index scale r32 -509 # 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes -510 0f 84 # jmp if ZF to ?? -511 +error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand -512 -513 :(before "End Globals") -514 map</*op*/string, /*bitvector*/uint8_t> Permitted_operands_0f; -515 :(before "End Init Permitted Operands") -516 //// Class D: just op and disp32 -517 // imm32 imm8 disp32 |disp16 disp8 subop modrm -518 // 0 0 1 |0 0 0 0 -519 put_new(Permitted_operands_0f, "84", 0x10); -520 put_new(Permitted_operands_0f, "85", 0x10); -521 put_new(Permitted_operands_0f, "8c", 0x10); -522 put_new(Permitted_operands_0f, "8d", 0x10); -523 put_new(Permitted_operands_0f, "8e", 0x10); -524 put_new(Permitted_operands_0f, "8f", 0x10); -525 -526 //// Class M: using ModR/M byte -527 // imm32 imm8 disp32 |disp16 disp8 subop modrm -528 // 0 0 0 |0 0 0 1 -529 put_new(Permitted_operands_0f, "af", 0x01); -530 -531 :(code) -532 void check_operands_0f(const line& inst, const word& op) { -533 uint8_t expected_bitvector = get(Permitted_operands_0f, op.data); -534 if (HAS(expected_bitvector, MODRM)) -535 check_operands_modrm(inst, op); -536 compare_bitvector_0f(inst, CLEAR(expected_bitvector, MODRM), op); -537 } -538 -539 void compare_bitvector_0f(const line& inst, uint8_t expected, const word& op) { -540 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere -541 uint8_t bitvector = computed_expected_operand_bitvector(inst); -542 if (trace_contains_errors()) return; // duplicate operand type -543 if (bitvector == expected) return; // all good with this instruction -544 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { -545 //? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; -546 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand -547 const string& optype = Operand_type_name.at(i); -548 if ((bitvector & 0x1) > (expected & 0x1)) -549 raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": unexpected " << optype << " operand\n" << end(); -550 else -551 raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": missing " << optype << " operand\n" << end(); -552 // continue giving all errors for a single instruction -553 } -554 // ignore settings in any unused bits -555 } -556 -557 string maybe_name_0f(const word& op) { -558 if (!is_hex_byte(op)) return ""; -559 if (!contains_key(Name_0f, op.data)) return ""; -560 // strip stuff in parens from the name -561 const string& s = get(Name_0f, op.data); -562 return " ("+s.substr(0, s.find(" ("))+')'; -563 } -564 -565 string tolower(const char* s) { -566 ostringstream out; -567 for (/*nada*/; *s; ++s) -568 out << static_cast<char>(tolower(*s)); -569 return out.str(); -570 } -571 -572 #undef HAS -573 #undef SET -574 #undef CLEAR -575 -576 :(before "End Includes") -577 #include<cctype> +231 put(Permitted_operands, "c6", 0x23); // copy +232 +233 //// Class P: op, ModR/M, subop (not r32) and imm32 +234 // imm32 imm8 disp32 |disp16 disp8 subop modrm +235 // 1 0 0 |0 0 1 1 +236 put(Permitted_operands, "81", 0x43); // combine +237 put(Permitted_operands, "c7", 0x43); // copy +238 +239 // End Init Permitted Operands +240 } +241 +242 :(code) +243 #define HAS(bitvector, bit) ((bitvector) & (1 << (bit))) +244 #define SET(bitvector, bit) ((bitvector) | (1 << (bit))) +245 #define CLEAR(bitvector, bit) ((bitvector) & (~(1 << (bit)))) +246 +247 void check_operands(const line& inst, const word& op) { +248 if (!is_hex_byte(op)) return; +249 uint8_t expected_bitvector = get(Permitted_operands, op.data); +250 if (HAS(expected_bitvector, MODRM)) { +251 check_operands_modrm(inst, op); +252 compare_bitvector_modrm(inst, expected_bitvector, op); +253 } +254 else { +255 compare_bitvector(inst, expected_bitvector, op); +256 } +257 } +258 +259 //: Many instructions can be checked just by comparing bitvectors. +260 +261 void compare_bitvector(const line& inst, uint8_t expected, const word& op) { +262 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +263 uint8_t bitvector = computed_expected_operand_bitvector(inst); +264 if (trace_contains_errors()) return; // duplicate operand type +265 if (bitvector == expected) return; // all good with this instruction +266 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { +267 //? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; +268 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand +269 const string& optype = Operand_type_name.at(i); +270 if ((bitvector & 0x1) > (expected & 0x1)) +271 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); +272 else +273 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); +274 // continue giving all errors for a single instruction +275 } +276 // ignore settings in any unused bits +277 } +278 +279 string maybe_name(const word& op) { +280 if (!is_hex_byte(op)) return ""; +281 if (!contains_key(Name, op.data)) return ""; +282 // strip stuff in parens from the name +283 const string& s = get(Name, op.data); +284 return " ("+s.substr(0, s.find(" ("))+')'; +285 } +286 +287 uint32_t computed_expected_operand_bitvector(const line& inst) { +288 uint32_t bitvector = 0; +289 for (int i = /*skip op*/1; i < SIZE(inst.words); ++i) { +290 bitvector = bitvector | expected_bit_for_received_operand(inst.words.at(i)); +291 if (trace_contains_errors()) return INVALID_OPERANDS; // duplicate operand type +292 } +293 return bitvector; +294 } +295 +296 bool has_operands(const line& inst) { +297 return SIZE(inst.words) > first_operand(inst); +298 } +299 +300 int first_operand(const line& inst) { +301 if (inst.words.at(0).data == "0f") return 2; +302 if (inst.words.at(0).data == "f2" || inst.words.at(0).data == "f3") { +303 if (inst.words.at(1).data == "0f") +304 return 3; +305 else +306 return 2; +307 } +308 return 1; +309 } +310 +311 // Scan the metadata of 'w' and return the expected bit corresponding to any operand type. +312 // Also raise an error if metadata contains multiple operand types. +313 uint32_t expected_bit_for_received_operand(const word& w) { +314 uint32_t bv = 0; +315 bool found = false; +316 for (int i = 0; i < SIZE(w.metadata); ++i) { +317 string/*copy*/ curr = w.metadata.at(i); +318 if (curr == "mod" || curr == "rm32" || curr == "r32" || curr == "scale" || curr == "index" || curr == "base") +319 curr = "modrm"; +320 else if (!contains_key(Operand_type, curr)) continue; // ignore unrecognized metadata +321 if (found) { +322 raise << "'" << w.original << "' has conflicting operand types; it should have only one\n" << end(); +323 return INVALID_OPERANDS; +324 } +325 bv = (1 << get(Operand_type, curr)); +326 found = true; +327 } +328 return bv; +329 } +330 +331 :(scenario conflicting_operand_type) +332 % Hide_errors = true; +333 == 0x1 +334 cd/software-interrupt 80/imm8/imm32 +335 +error: '80/imm8/imm32' has conflicting operand types; it should have only one +336 +337 //: Instructions computing effective addresses have more complex rules, so +338 //: we'll hard-code a common set of instruction-decoding rules. +339 +340 :(scenario check_missing_mod_operand) +341 % Hide_errors = true; +342 == 0x1 +343 81 0/add/subop 3/rm32/ebx 1/imm32 +344 +error: '81 0/add/subop 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing mod operand +345 +346 :(code) +347 void check_operands_modrm(const line& inst, const word& op) { +348 if (all_hex_bytes(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +349 check_operand_metadata_present(inst, "mod", op); +350 check_operand_metadata_present(inst, "rm32", op); +351 // no check for r32; some instructions don't use it; just assume it's 0 if missing +352 if (op.data == "81" || op.data == "8f" || op.data == "ff") { // keep sync'd with 'help subop' +353 check_operand_metadata_present(inst, "subop", op); +354 check_operand_metadata_absent(inst, "r32", op, "should be replaced by subop"); +355 } +356 if (trace_contains_errors()) return; +357 if (metadata(inst, "rm32").data != "4") return; +358 // SIB byte checks +359 uint8_t mod = hex_byte(metadata(inst, "mod").data); +360 if (mod != /*direct*/3) { +361 check_operand_metadata_present(inst, "base", op); +362 check_operand_metadata_present(inst, "index", op); // otherwise why go to SIB? +363 } +364 else { +365 check_operand_metadata_absent(inst, "base", op, "direct mode"); +366 check_operand_metadata_absent(inst, "index", op, "direct mode"); +367 } +368 // no check for scale; 0 (2**0 = 1) by default +369 } +370 +371 // same as compare_bitvector, with one additional exception for modrm-based +372 // instructions: they may use an extra displacement on occasion +373 void compare_bitvector_modrm(const line& inst, uint8_t expected, const word& op) { +374 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +375 uint8_t bitvector = computed_expected_operand_bitvector(inst); +376 if (trace_contains_errors()) return; // duplicate operand type +377 // update 'expected' bitvector for the additional exception +378 if (has_operand_metadata(inst, "mod")) { +379 int32_t mod = parse_int(metadata(inst, "mod").data); +380 switch (mod) { +381 case 0: +382 if (has_operand_metadata(inst, "rm32") && parse_int(metadata(inst, "rm32").data) == 5) +383 expected |= (1<<DISP32); +384 break; +385 case 1: +386 expected |= (1<<DISP8); +387 break; +388 case 2: +389 expected |= (1<<DISP32); +390 break; +391 } +392 } +393 if (bitvector == expected) return; // all good with this instruction +394 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { +395 //? cerr << "comparing for modrm " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; +396 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand +397 const string& optype = Operand_type_name.at(i); +398 if ((bitvector & 0x1) > (expected & 0x1)) +399 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << optype << " operand\n" << end(); +400 else +401 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << optype << " operand\n" << end(); +402 // continue giving all errors for a single instruction +403 } +404 // ignore settings in any unused bits +405 } +406 +407 void check_operand_metadata_present(const line& inst, const string& type, const word& op) { +408 if (!has_operand_metadata(inst, type)) +409 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": missing " << type << " operand\n" << end(); +410 } +411 +412 void check_operand_metadata_absent(const line& inst, const string& type, const word& op, const string& msg) { +413 if (has_operand_metadata(inst, type)) +414 raise << "'" << to_string(inst) << "'" << maybe_name(op) << ": unexpected " << type << " operand (" << msg << ")\n" << end(); +415 } +416 +417 :(scenarios transform) +418 :(scenario modrm_with_displacement) +419 % Reg[EAX].u = 0x1; +420 == 0x1 +421 # just avoid null pointer +422 8b/copy 1/mod/lookup+disp8 0/rm32/EAX 2/r32/EDX 4/disp8 # copy *(EAX+4) to EDX +423 $error: 0 +424 +425 :(scenario check_missing_disp8) +426 % Hide_errors = true; +427 == 0x1 +428 89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX # missing disp8 +429 +error: '89/copy 1/mod/lookup+disp8 0/rm32/EAX 1/r32/ECX' (copy r32 to rm32): missing disp8 operand +430 +431 :(scenario check_missing_disp32) +432 % Hide_errors = true; +433 == 0x1 +434 8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX # missing disp32 +435 +error: '8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX' (copy rm32 to r32): missing disp32 operand +436 :(scenarios run) +437 +438 :(scenario conflicting_operands_in_modrm_instruction) +439 % Hide_errors = true; +440 == 0x1 +441 01/add 0/mod 3/mod +442 +error: '01/add 0/mod 3/mod' has conflicting mod operands +443 +444 :(scenario conflicting_operand_type_modrm) +445 % Hide_errors = true; +446 == 0x1 +447 01/add 0/mod 3/rm32/r32 +448 +error: '3/rm32/r32' has conflicting operand types; it should have only one +449 +450 :(scenario check_missing_rm32_operand) +451 % Hide_errors = true; +452 == 0x1 +453 81 0/add/subop 0/mod 1/imm32 +454 +error: '81 0/add/subop 0/mod 1/imm32' (combine rm32 with imm32 based on subop): missing rm32 operand +455 +456 :(scenario check_missing_subop_operand) +457 % Hide_errors = true; +458 == 0x1 +459 81 0/mod 3/rm32/ebx 1/imm32 +460 +error: '81 0/mod 3/rm32/ebx 1/imm32' (combine rm32 with imm32 based on subop): missing subop operand +461 +462 :(scenario check_missing_base_operand) +463 % Hide_errors = true; +464 == 0x1 +465 81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32 +466 +error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 1/imm32' (combine rm32 with imm32 based on subop): missing base operand +467 +468 :(scenario check_missing_index_operand) +469 % Hide_errors = true; +470 == 0x1 +471 81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32 +472 +error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 0/base 1/imm32' (combine rm32 with imm32 based on subop): missing index operand +473 +474 :(scenario check_missing_base_operand_2) +475 % Hide_errors = true; +476 == 0x1 +477 81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32 +478 +error: '81 0/add/subop 0/mod/indirect 4/rm32/use-sib 2/index 3/scale 1/imm32' (combine rm32 with imm32 based on subop): missing base operand +479 +480 :(scenario check_extra_displacement) +481 % Hide_errors = true; +482 == 0x1 +483 89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8 +484 +error: '89/copy 0/mod/indirect 0/rm32/EAX 1/r32/ECX 4/disp8' (copy r32 to rm32): unexpected disp8 operand +485 +486 :(scenario check_base_operand_not_needed_in_direct_mode) +487 == 0x1 +488 81 0/add/subop 3/mod/indirect 4/rm32/use-sib 1/imm32 +489 $error: 0 +490 +491 :(scenario extra_modrm) +492 % Hide_errors = true; +493 == 0x1 +494 59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP +495 +error: '59/pop-to-ECX 3/mod/direct 1/rm32/ECX 4/r32/ESP' (pop top of stack to ECX): unexpected modrm operand +496 +497 //:: similarly handle multi-byte opcodes +498 +499 :(code) +500 void check_operands_0f(const line& inst) { +501 assert(inst.words.at(0).data == "0f"); +502 if (SIZE(inst.words) == 1) { +503 raise << "opcode '0f' requires a second opcode\n" << end(); +504 return; +505 } +506 word op = preprocess_op(inst.words.at(1)); +507 if (!contains_key(Name_0f, op.data)) { +508 raise << "unknown 2-byte opcode '0f " << op.data << "'\n" << end(); +509 return; +510 } +511 check_operands_0f(inst, op); +512 } +513 +514 void check_operands_f3(const line& /*unused*/) { +515 raise << "no supported opcodes starting with f3\n" << end(); +516 } +517 +518 :(scenario check_missing_disp32_operand) +519 % Hide_errors = true; +520 == 0x1 +521 # instruction effective address operand displacement immediate +522 # op subop mod rm32 base index scale r32 +523 # 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes +524 0f 84 # jmp if ZF to ?? +525 +error: '0f 84' (jump disp32 bytes away if equal, if ZF is set): missing disp32 operand +526 +527 :(before "End Globals") +528 map</*op*/string, /*bitvector*/uint8_t> Permitted_operands_0f; +529 :(before "End Init Permitted Operands") +530 //// Class D: just op and disp32 +531 // imm32 imm8 disp32 |disp16 disp8 subop modrm +532 // 0 0 1 |0 0 0 0 +533 put_new(Permitted_operands_0f, "84", 0x10); +534 put_new(Permitted_operands_0f, "85", 0x10); +535 put_new(Permitted_operands_0f, "8c", 0x10); +536 put_new(Permitted_operands_0f, "8d", 0x10); +537 put_new(Permitted_operands_0f, "8e", 0x10); +538 put_new(Permitted_operands_0f, "8f", 0x10); +539 +540 //// Class M: using ModR/M byte +541 // imm32 imm8 disp32 |disp16 disp8 subop modrm +542 // 0 0 0 |0 0 0 1 +543 put_new(Permitted_operands_0f, "af", 0x01); +544 +545 :(code) +546 void check_operands_0f(const line& inst, const word& op) { +547 uint8_t expected_bitvector = get(Permitted_operands_0f, op.data); +548 if (HAS(expected_bitvector, MODRM)) +549 check_operands_modrm(inst, op); +550 compare_bitvector_0f(inst, CLEAR(expected_bitvector, MODRM), op); +551 } +552 +553 void compare_bitvector_0f(const line& inst, uint8_t expected, const word& op) { +554 if (all_hex_bytes(inst) && has_operands(inst)) return; // deliberately programming in raw hex; we'll raise a warning elsewhere +555 uint8_t bitvector = computed_expected_operand_bitvector(inst); +556 if (trace_contains_errors()) return; // duplicate operand type +557 if (bitvector == expected) return; // all good with this instruction +558 for (int i = 0; i < NUM_OPERAND_TYPES; ++i, bitvector >>= 1, expected >>= 1) { +559 //? cerr << "comparing " << HEXBYTE << NUM(bitvector) << " with " << NUM(expected) << '\n'; +560 if ((bitvector & 0x1) == (expected & 0x1)) continue; // all good with this operand +561 const string& optype = Operand_type_name.at(i); +562 if ((bitvector & 0x1) > (expected & 0x1)) +563 raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": unexpected " << optype << " operand\n" << end(); +564 else +565 raise << "'" << to_string(inst) << "'" << maybe_name_0f(op) << ": missing " << optype << " operand\n" << end(); +566 // continue giving all errors for a single instruction +567 } +568 // ignore settings in any unused bits +569 } +570 +571 string maybe_name_0f(const word& op) { +572 if (!is_hex_byte(op)) return ""; +573 if (!contains_key(Name_0f, op.data)) return ""; +574 // strip stuff in parens from the name +575 const string& s = get(Name_0f, op.data); +576 return " ("+s.substr(0, s.find(" ("))+')'; +577 } +578 +579 string tolower(const char* s) { +580 ostringstream out; +581 for (/*nada*/; *s; ++s) +582 out << static_cast<char>(tolower(*s)); +583 return out.str(); +584 } +585 +586 #undef HAS +587 #undef SET +588 #undef CLEAR +589 +590 :(before "End Includes") +591 #include<cctype> diff --git a/html/subx/032check_operand_bounds.cc.html b/html/subx/032check_operand_bounds.cc.html index cc839fa1..1c28e1d3 100644 --- a/html/subx/032check_operand_bounds.cc.html +++ b/html/subx/032check_operand_bounds.cc.html @@ -92,7 +92,7 @@ if ('onhashchange' in window) { 30 trace(99, "transform") << "-- check operand bounds" << end(); 31 for (int i = 0; i < SIZE(code.lines); ++i) { 32 const line& inst = code.lines.at(i); -33 for (int j = first_operand(inst); j < SIZE(inst.words); ++j) +33 for (int j = first_operand(inst); j < SIZE(inst.words); ++j) 34 check_operand_bounds(inst.words.at(j)); 35 if (trace_contains_errors()) return; // stop at the first mal-formed instruction 36 } diff --git a/html/subx/036global_variables.cc.html b/html/subx/036global_variables.cc.html index 33d99940..f43c691a 100644 --- a/html/subx/036global_variables.cc.html +++ b/html/subx/036global_variables.cc.html @@ -266,7 +266,7 @@ if ('onhashchange' in window) { 203 00 204 +error: duplicate global 'x' 205 -206 :(scenario disp32_data_with_modrm) +206 :(scenario global_variable_disp32_with_modrm) 207 == code 208 8b/copy 0/mod/indirect 5/rm32/.disp32 2/r32/EDX x/disp32 209 == data @@ -275,7 +275,7 @@ if ('onhashchange' in window) { 212 $error: 0 213 214 :(scenarios transform) -215 :(scenario disp32_data_with_call) +215 :(scenario global_variable_disp32_with_call) 216 == code 217 foo: 218 e8/call bar/disp32 diff --git a/html/subx/039debug.cc.html b/html/subx/039debug.cc.html index 0f002bd4..5a8a9880 100644 --- a/html/subx/039debug.cc.html +++ b/html/subx/039debug.cc.html @@ -18,6 +18,7 @@ a { color:inherit; } .Constant { color: #008787; } .Delimiter { color: #c000c0; } .Special { color: #d70000; } +.Identifier { color: #af5f00; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Comment { color: #005faf; } --> @@ -80,6 +81,36 @@ if ('onhashchange' in window) { 23 :(after "Run One Instruction") 24 if (contains_key(Symbol_name, EIP)) 25 trace(90, "run") << "== label " << get(Symbol_name, EIP) << end(); +26 +27 // If a label starts with '$watch-', make a note of the effective address +28 // computed by the next instruction. Start dumping out its contents to the +29 // trace after every subsequent instruction. +30 +31 :(after "Run One Instruction") +32 dump_watch_points(); +33 :(before "End Globals") +34 map<string, uint32_t> Watch_points; +35 :(before "End Reset") +36 Watch_points.clear(); +37 :(code) +38 void dump_watch_points() { +39 if (Watch_points.empty()) return; +40 dbg << "watch points:" << end(); +41 for (map<string, uint32_t>::iterator p = Watch_points.begin(); p != Watch_points.end(); ++p) +42 dbg << " " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end(); +43 } +44 +45 :(before "End Globals") +46 string Watch_this_effective_address; +47 :(after "Run One Instruction") +48 Watch_this_effective_address = ""; +49 if (contains_key(Symbol_name, EIP) && starts_with(get(Symbol_name, EIP), "$watch-")) +50 Watch_this_effective_address = get(Symbol_name, EIP); +51 :(after "Found effective_address(addr)") +52 if (!Watch_this_effective_address.empty()) { +53 dbg << "now watching " << HEXWORD << addr << " for " << Watch_this_effective_address << end(); +54 Watch_points[Watch_this_effective_address] = addr; +55 } -- cgit 1.4.1-2-gfad0