diff options
author | Kartik Agaram <vc@akkartik.com> | 2018-12-03 23:26:56 -0800 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2018-12-03 23:26:56 -0800 |
commit | 5f3b3e7aea4174f13da9b430538564c68257b647 (patch) | |
tree | 64b0ca70a7508aa10a008ad7d996f5d4f38214c8 /subx | |
parent | ee73f5eb31e9dd5a185da097bd990ac94c3d282b (diff) | |
download | mu-5f3b3e7aea4174f13da9b430538564c68257b647.tar.gz |
4830
New helper: printing a byte in textual (hex) form. This required adding instructions for bitwise shift operations.
Diffstat (limited to 'subx')
-rw-r--r-- | subx/013direct_addressing.cc | 160 | ||||
-rw-r--r-- | subx/015immediate_addressing.cc | 151 | ||||
-rw-r--r-- | subx/031check_operands.cc | 12 | ||||
-rw-r--r-- | subx/063print-byte.subx | 139 | ||||
-rw-r--r-- | subx/opcodes | 2 |
5 files changed, 460 insertions, 4 deletions
diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc index 8d9f0555..c18b5f5c 100644 --- a/subx/013direct_addressing.cc +++ b/subx/013direct_addressing.cc @@ -117,7 +117,7 @@ put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on s +run: storing 0x0000000c :(before "End Single-Byte Opcodes") -case 0xf7: { // xor r32 with r/m32 +case 0xf7: { const uint8_t modrm = next(); trace(90, "run") << "operate on r/m32" << end(); int32_t* arg1 = effective_address(modrm); @@ -209,6 +209,164 @@ case 3: { // negate r/m32 +run: subop: negate +run: overflow +//:: shift left + +:(before "End Initialize Op Names") +put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"); + +:(scenario shift_left_r32_with_cl) +% Reg[EBX].i = 13; +% Reg[ECX].i = 1; +== 0x1 +# op ModR/M SIB displacement immediate + d3 e3 # negate EBX +# ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift left by CL bits ++run: storing 0x0000001a + +:(before "End Single-Byte Opcodes") +case 0xd3: { + const uint8_t modrm = next(); + trace(90, "run") << "operate on r/m32" << end(); + int32_t* arg1 = effective_address(modrm); + const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits + switch (subop) { + case 4: { // shift left r/m32 by CL + trace(90, "run") << "subop: shift left by CL bits" << end(); + uint8_t count = Reg[ECX].u & 0x1f; + // OF is only defined if count is 1 + if (count == 1) { + bool msb = (*arg1 & 0x80000000) >> 1; + bool pnsb = (*arg1 & 0x40000000); + OF = (msb != pnsb); + } + *arg1 = (*arg1 << count); + ZF = (*arg1 == 0); + SF = (*arg1 < 0); + trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + break; + } + // End Op d3 Subops + default: + cerr << "unrecognized sub-opcode after d3: " << NUM(subop) << '\n'; + exit(1); + } + break; +} + +//:: shift right arithmetic + +:(scenario shift_right_arithmetic_r32_with_cl) +% Reg[EBX].i = 26; +% Reg[ECX].i = 1; +== 0x1 +# op ModR/M SIB displacement immediate + d3 fb # negate EBX +# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while preserving sign ++run: storing 0x0000000d + +:(before "End Op d3 Subops") +case 7: { // shift right r/m32 by CL, preserving sign + trace(90, "run") << "subop: shift right by CL bits, while preserving sign" << end(); + uint8_t count = Reg[ECX].u & 0x1f; + *arg1 = (*arg1 >> count); + ZF = (*arg1 == 0); + SF = (*arg1 < 0); + // OF is only defined if count is 1 + if (count == 1) OF = false; + trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + break; +} + +:(scenario shift_right_arithmetic_odd_r32_with_cl) +% Reg[EBX].i = 27; +% Reg[ECX].i = 1; +== 0x1 +# op ModR/M SIB displacement immediate + d3 fb # negate EBX +# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while preserving sign +# result: 13 ++run: storing 0x0000000d + +:(scenario shift_right_arithmetic_negative_r32_with_cl) +% Reg[EBX].i = 0xfffffffd; // -3 +% Reg[ECX].i = 1; +== 0x1 +# op ModR/M SIB displacement immediate + d3 fb # negate EBX +# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while preserving sign +# result: -2 ++run: storing 0xfffffffe + +//:: shift right logical + +:(scenario shift_right_logical_r32_with_cl) +% Reg[EBX].i = 26; +% Reg[ECX].i = 1; +== 0x1 +# op ModR/M SIB displacement immediate + d3 eb # negate EBX +# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while padding zeroes ++run: storing 0x0000000d + +:(before "End Op d3 Subops") +case 5: { // shift right r/m32 by CL, preserving sign + trace(90, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); + uint8_t count = Reg[ECX].u & 0x1f; + // OF is only defined if count is 1 + if (count == 1) { + bool msb = (*arg1 & 0x80000000) >> 1; + bool pnsb = (*arg1 & 0x40000000); + OF = (msb != pnsb); + } + uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1); + *uarg1 = (*uarg1 >> count); + ZF = (*uarg1 == 0); + // result is always positive by definition + SF = false; + trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + break; +} + +:(scenario shift_right_logical_odd_r32_with_cl) +% Reg[EBX].i = 27; +% Reg[ECX].i = 1; +== 0x1 +# op ModR/M SIB displacement immediate + d3 eb # negate EBX +# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while padding zeroes +# result: 13 ++run: storing 0x0000000d + +:(scenario shift_right_logical_negative_r32_with_cl) +% Reg[EBX].i = 0xfffffffd; +% Reg[ECX].i = 1; +== 0x1 +# op ModR/M SIB displacement immediate + d3 eb # negate EBX +# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while padding zeroes ++run: storing 0x7ffffffe + //:: and :(before "End Initialize Op Names") diff --git a/subx/015immediate_addressing.cc b/subx/015immediate_addressing.cc index a5d385b7..ce4bc4db 100644 --- a/subx/015immediate_addressing.cc +++ b/subx/015immediate_addressing.cc @@ -110,6 +110,157 @@ case 5: { +run: subop subtract +run: storing 0x00000009 +//:: shift left + +:(before "End Initialize Op Names") +put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)"); + +:(scenario shift_left_r32_with_imm8) +% Reg[EBX].i = 13; +== 0x1 +# op ModR/M SIB displacement immediate + c1 e3 01 # negate EBX +# ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift left by CL bits ++run: storing 0x0000001a + +:(before "End Single-Byte Opcodes") +case 0xc1: { + const uint8_t modrm = next(); + trace(90, "run") << "operate on r/m32" << end(); + int32_t* arg1 = effective_address(modrm); + const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits + switch (subop) { + case 4: { // shift left r/m32 by CL + trace(90, "run") << "subop: shift left by CL bits" << end(); + uint8_t count = next() & 0x1f; + // OF is only defined if count is 1 + if (count == 1) { + bool msb = (*arg1 & 0x80000000) >> 1; + bool pnsb = (*arg1 & 0x40000000); + OF = (msb != pnsb); + } + *arg1 = (*arg1 << count); + ZF = (*arg1 == 0); + SF = (*arg1 < 0); + trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + break; + } + // End Op c1 Subops + default: + cerr << "unrecognized sub-opcode after c1: " << NUM(subop) << '\n'; + exit(1); + } + break; +} + +//:: shift right arithmetic + +:(scenario shift_right_arithmetic_r32_with_imm8) +% Reg[EBX].i = 26; +== 0x1 +# op ModR/M SIB displacement immediate + c1 fb 01 # negate EBX +# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while preserving sign ++run: storing 0x0000000d + +:(before "End Op c1 Subops") +case 7: { // shift right r/m32 by CL, preserving sign + trace(90, "run") << "subop: shift right by CL bits, while preserving sign" << end(); + uint8_t count = next() & 0x1f; + *arg1 = (*arg1 >> count); + ZF = (*arg1 == 0); + SF = (*arg1 < 0); + // OF is only defined if count is 1 + if (count == 1) OF = false; + trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + break; +} + +:(scenario shift_right_arithmetic_odd_r32_with_imm8) +% Reg[EBX].i = 27; +== 0x1 +# op ModR/M SIB displacement immediate + c1 fb 01 # negate EBX +# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while preserving sign +# result: 13 ++run: storing 0x0000000d + +:(scenario shift_right_arithmetic_negative_r32_with_imm8) +% Reg[EBX].i = 0xfffffffd; // -3 +== 0x1 +# op ModR/M SIB displacement immediate + c1 fb 01 # negate EBX +# ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while preserving sign +# result: -2 ++run: storing 0xfffffffe + +//:: shift right logical + +:(scenario shift_right_logical_r32_with_imm8) +% Reg[EBX].i = 26; +== 0x1 +# op ModR/M SIB displacement immediate + c1 eb 01 # negate EBX +# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while padding zeroes ++run: storing 0x0000000d + +:(before "End Op c1 Subops") +case 5: { // shift right r/m32 by CL, preserving sign + trace(90, "run") << "subop: shift right by CL bits, while padding zeroes" << end(); + uint8_t count = next() & 0x1f; + // OF is only defined if count is 1 + if (count == 1) { + bool msb = (*arg1 & 0x80000000) >> 1; + bool pnsb = (*arg1 & 0x40000000); + OF = (msb != pnsb); + } + uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1); + *uarg1 = (*uarg1 >> count); + ZF = (*uarg1 == 0); + // result is always positive by definition + SF = false; + trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end(); + break; +} + +:(scenario shift_right_logical_odd_r32_with_imm8) +% Reg[EBX].i = 27; +== 0x1 +# op ModR/M SIB displacement immediate + c1 eb 01 # negate EBX +# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while padding zeroes +# result: 13 ++run: storing 0x0000000d + +:(scenario shift_right_logical_negative_r32_with_imm8) +% Reg[EBX].i = 0xfffffffd; +== 0x1 +# op ModR/M SIB displacement immediate + c1 eb 01 # negate EBX +# ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX) ++run: operate on r/m32 ++run: r/m32 is EBX ++run: subop: shift right by CL bits, while padding zeroes ++run: storing 0x7ffffffe + //:: and :(before "End Initialize Op Names") diff --git a/subx/031check_operands.cc b/subx/031check_operands.cc index f59fb005..949bc533 100644 --- a/subx/031check_operands.cc +++ b/subx/031check_operands.cc @@ -212,18 +212,24 @@ void init_permitted_operands() { // pop put(Permitted_operands, "8f", 0x01); - //// Class O: op, ModR/M and subop (not r32) + //// Class N: op, ModR/M and subop (not r32) // imm32 imm8 disp32 |disp16 disp8 subop modrm // 0 0 0 |0 0 1 1 + put(Permitted_operands, "d3", 0x03); // shift put(Permitted_operands, "f7", 0x03); // test/not/mul/div put(Permitted_operands, "ff", 0x03); // jump/push/call - //// Class N: op, ModR/M and imm32 + //// Class O: op, ModR/M, subop (not r32) and imm8 + // imm32 imm8 disp32 |disp16 disp8 subop modrm + // 0 1 0 |0 0 1 1 + put(Permitted_operands, "c1", 0x23); // combine + + //// Class P: op, ModR/M and imm32 // imm32 imm8 disp32 |disp16 disp8 subop modrm // 1 0 0 |0 0 0 1 put(Permitted_operands, "c7", 0x41); // copy - //// Class P: op, ModR/M, subop (not r32) and imm32 + //// Class Q: op, ModR/M, subop (not r32) and imm32 // imm32 imm8 disp32 |disp16 disp8 subop modrm // 1 0 0 |0 0 1 1 put(Permitted_operands, "81", 0x43); // combine diff --git a/subx/063print-byte.subx b/subx/063print-byte.subx new file mode 100644 index 00000000..89f9e103 --- /dev/null +++ b/subx/063print-byte.subx @@ -0,0 +1,139 @@ +# Print the (hex) textual representation of the lowest byte of a number. + +== code +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 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 + +# main: + e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + # syscall(exit, Num-test-failures) + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + b8/copy-to-EAX 1/imm32 + cd/syscall 0x80/imm8 + +print-byte: # f : (address buffered-file), n : num -> <void> + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # . save registers + 50/push-EAX + # AL = convert upper nibble to hex + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + c1/shift 5/subop/logic-right 3/mod/direct 0/rm32/EAX . . . . . 4/imm8 # shift EAX right by 4 bits, while padding zeroes + # . hex-char(AL) + # . . push args + 50/push-EAX + # . . call + e8/call hex-char/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write-byte(f, AL) + # . . push args + 50/push-EAX + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + # . . call + e8/call write-byte/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # AL = convert lower nibble to hex + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + 25/and-EAX 0xf/imm32 + # . hex-char(AL) + # . . push args + 50/push-EAX + # . . call + e8/call hex-char/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # write-byte(f, AL) + # . . push args + 50/push-EAX + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + # . . call + e8/call write-byte/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # . restore registers + 59/pop-to-ECX + 58/pop-to-EAX + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +# extract lowest 4 bits and convert to 8-byte ascii +# return 0xffffffff if more than 4 bits are set +hex-char: # n : num -> char_or_error/EAX + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # EAX = n + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX + # if it's <= 9 add '0' to it + 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0x9/imm32 # compare EAX + 7f/jump-if-greater $hex-char:check2/disp8 + 05/add-to-EAX 0x30/imm32/'0' + eb/jump $hex-char:end/disp8 +$hex-char:check2: + # else if it's <= 15 add ('a' - 10) to it + 81 7/subop/compare 3/mod/direct 0/rm32/EAX . . . . . 0xf/imm32 # compare EAX + 7f/jump-if-greater $hex-char:error/disp8 + 05/add-to-EAX 0x57/imm32 # 'a' - 10 + eb/jump $hex-char:end/disp8 +$hex-char:error: + # otherwise return 0xffffffff + b8/copy-to-EAX 0xffffffff/imm32 +$hex-char:end: + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-print-byte: + # - check that print-byte prints the hex textual representation + # setup + # . clear-stream(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . clear-stream(_test-buffered-file+4) + # . . push args + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # print-byte(_test-buffered-file, 0xa) # exercises digit, non-digit as well as leading zero + # . . push args + 68/push 0xa/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call print-byte/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # flush(_test-buffered-file) + # . . push args + 68/push _test-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-ints-equal(*_test-stream->data, '0a', msg) + # . . push args + 68/push "F - test-print-byte"/imm32 + 68/push 0x6130/imm32/0a + # . . push *_test-stream->data + b8/copy-to-EAX _test-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . end + c3/return diff --git a/subx/opcodes b/subx/opcodes index 94d9c5f3..3437f30a 100644 --- a/subx/opcodes +++ b/subx/opcodes @@ -72,9 +72,11 @@ Opcodes currently supported by SubX: bd: copy imm32 to EBP (mov) be: copy imm32 to ESI (mov) bf: copy imm32 to EDI (mov) + c1: shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr) c3: return from most recent unfinished call (ret) c7: copy imm32 to rm32 (mov) cd: software interrupt (int) + d3: shift rm32 by CL bits depending on subop (sal/sar/shl/shr) e8: call disp32 (call) e9: jump disp32 bytes away (jmp) eb: jump disp8 bytes away (jmp) |