aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2022-04-30 11:11:56 -0700
committeriximeow <me@iximeow.net>2022-04-30 11:11:56 -0700
commit073d3b367ee4164fc5c89c4a7869770f0261a00c (patch)
tree9415e3feae5bce70001fcef1eed530c53322fcbf
parent2097524c851b15e89091fd3775817a06f0eeae4f (diff)
support 0x9a callf in 16/32-bit modes
-rw-r--r--CHANGELOG2
-rw-r--r--src/protected_mode/display.rs6
-rw-r--r--src/protected_mode/mod.rs29
-rw-r--r--src/real_mode/display.rs6
-rw-r--r--src/real_mode/mod.rs29
-rw-r--r--test/protected_mode/mod.rs2
-rw-r--r--test/real_mode/mod.rs2
7 files changed, 74 insertions, 2 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 4227196..1d676be 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -12,6 +12,8 @@
* adjust test structure so that exhaustive tests can be `#[ignored]` and
explicitly run anyway for completeness. this means the ignored at&t tests now
are both ignored and appear to succeed when run.
+* support `9a` encoding of `callf` with absolute segment/address operand
+ - this encoding is only present in 32-bit and 16-bit modes
## 1.1.4
* fix reachable unreachable under `DisplayStyle::C` in 64-, 32-, and 16-bit modes
diff --git a/src/protected_mode/display.rs b/src/protected_mode/display.rs
index 69cdbc7..4416141 100644
--- a/src/protected_mode/display.rs
+++ b/src/protected_mode/display.rs
@@ -163,6 +163,12 @@ impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for Operand {
write!(f, "{}",
colors.number(signed_i32_hex(imm)))
},
+ &Operand::AbsoluteFarAddress { segment, address } => {
+ write!(f, "{}:{}",
+ colors.number(u16_hex(segment as u16)),
+ colors.number(u32_hex(address as u32)),
+ )
+ },
&Operand::Register(ref spec) => {
f.write_str(regspec_label(spec))
}
diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs
index dd63d32..5457b9d 100644
--- a/src/protected_mode/mod.rs
+++ b/src/protected_mode/mod.rs
@@ -389,6 +389,9 @@ pub enum Operand {
/// use; the instruction's `operand_count` should be reduced so as to make this invisible to
/// library clients.
Nothing,
+ /// absolute far call. this is only used for `9a` far calls with absolute `u16:u{16,32}`
+ /// operand, and so only exists in 32- and 16-bit modes.
+ AbsoluteFarAddress { segment: u16, address: u32 },
}
impl OperandSpec {
@@ -450,6 +453,7 @@ impl OperandSpec {
OperandSpec::RegVex_maskmerge |
OperandSpec::Reg4 |
OperandSpec::ImmInDispField |
+ OperandSpec::AbsoluteFarAddress |
OperandSpec::Nothing => {
false
}
@@ -683,6 +687,12 @@ impl Operand {
Operand::RegIndexBaseScaleDisp(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32)
}
}
+ OperandSpec::AbsoluteFarAddress => {
+ Operand::AbsoluteFarAddress {
+ segment: inst.disp as u16,
+ address: inst.imm as u32,
+ }
+ }
}
}
/// returns `true` if this operand implies a memory access, `false` otherwise.
@@ -721,6 +731,7 @@ impl Operand {
Operand::RegisterMaskMerge(_, _, _) |
Operand::RegisterMaskMergeSae(_, _, _, _) |
Operand::RegisterMaskMergeSaeNoround(_, _, _) |
+ Operand::AbsoluteFarAddress { .. } |
Operand::Nothing => {
false
}
@@ -2639,6 +2650,8 @@ enum OperandSpec {
RegIndexBaseDisp_mask,
RegIndexBaseScale_mask,
RegIndexBaseScaleDisp_mask,
+ // u16:u{16,32} immediate address for a far call
+ AbsoluteFarAddress,
}
// the Hash, Eq, and PartialEq impls here are possibly misleading.
@@ -5180,6 +5193,7 @@ enum OperandCode {
PMOVX_E_G_xmm = OperandCodeBuilder::new().operand_case(110).bits(),
G_Ev_xmm_Ib = OperandCodeBuilder::new().operand_case(111).bits(),
G_E_mm_Ib = OperandCodeBuilder::new().operand_case(112).bits(),
+ AbsFar = OperandCodeBuilder::new().operand_case(113).bits(),
}
const LOCKABLE_INSTRUCTIONS: &[Opcode] = &[
@@ -5404,7 +5418,7 @@ const OPCODES: [OpcodeRecord; 256] = [
OpcodeRecord(Interpretation::Instruction(Opcode::XCHG), OperandCode::Zv_AX_R7),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::CVT_AA),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::CVT_DA),
- OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
+ OpcodeRecord(Interpretation::Instruction(Opcode::CALLF), OperandCode::AbsFar),
OpcodeRecord(Interpretation::Instruction(Opcode::WAIT), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::PUSHF), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::POPF), OperandCode::Nothing),
@@ -8568,6 +8582,19 @@ fn unlikely_operands<
S: DescriptionSink<FieldDescription>
>(decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, mem_oper: OperandSpec, sink: &mut S) -> Result<(), DecodeError> {
match operand_code {
+ OperandCode::AbsFar => {
+ instruction.operands[0] = OperandSpec::AbsoluteFarAddress;
+ instruction.operand_count = 1;
+ instruction.mem_size = 0;
+ // read segment
+ let addr_size = if instruction.prefixes.operand_size() {
+ 2
+ } else {
+ 4
+ };
+ instruction.imm = read_num(words, addr_size)?;
+ instruction.disp = read_num(words, 2)? as u16 as u32;
+ }
OperandCode::G_E_mm_Ib => {
let modrm = read_modrm(words)?;
diff --git a/src/real_mode/display.rs b/src/real_mode/display.rs
index 070ca56..6f6eb74 100644
--- a/src/real_mode/display.rs
+++ b/src/real_mode/display.rs
@@ -163,6 +163,12 @@ impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for Operand {
write!(f, "{}",
colors.number(signed_i32_hex(imm)))
},
+ &Operand::AbsoluteFarAddress { segment, address } => {
+ write!(f, "{}:{}",
+ colors.number(u16_hex(segment as u16)),
+ colors.number(u32_hex(address as u32)),
+ )
+ },
&Operand::Register(ref spec) => {
f.write_str(regspec_label(spec))
}
diff --git a/src/real_mode/mod.rs b/src/real_mode/mod.rs
index d7fda17..8749ae2 100644
--- a/src/real_mode/mod.rs
+++ b/src/real_mode/mod.rs
@@ -389,6 +389,9 @@ pub enum Operand {
/// use; the instruction's `operand_count` should be reduced so as to make this invisible to
/// library clients.
Nothing,
+ /// absolute far call. this is only used for `9a` far calls with absolute `u16:u{16,32}`
+ /// operand, and so only exists in 32- and 16-bit modes.
+ AbsoluteFarAddress { segment: u16, address: u32 },
}
impl OperandSpec {
@@ -450,6 +453,7 @@ impl OperandSpec {
OperandSpec::RegVex_maskmerge |
OperandSpec::Reg4 |
OperandSpec::ImmInDispField |
+ OperandSpec::AbsoluteFarAddress |
OperandSpec::Nothing => {
false
}
@@ -683,6 +687,12 @@ impl Operand {
Operand::RegIndexBaseScaleDisp(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32)
}
}
+ OperandSpec::AbsoluteFarAddress => {
+ Operand::AbsoluteFarAddress {
+ segment: inst.disp as u16,
+ address: inst.imm as u32,
+ }
+ }
}
}
/// returns `true` if this operand implies a memory access, `false` otherwise.
@@ -721,6 +731,7 @@ impl Operand {
Operand::RegisterMaskMerge(_, _, _) |
Operand::RegisterMaskMergeSae(_, _, _, _) |
Operand::RegisterMaskMergeSaeNoround(_, _, _) |
+ Operand::AbsoluteFarAddress { .. } |
Operand::Nothing => {
false
}
@@ -2639,6 +2650,8 @@ enum OperandSpec {
RegIndexBaseDisp_mask,
RegIndexBaseScale_mask,
RegIndexBaseScaleDisp_mask,
+ // u16:u{16,32} immediate address for a far call
+ AbsoluteFarAddress,
}
// the Hash, Eq, and PartialEq impls here are possibly misleading.
@@ -5180,6 +5193,7 @@ enum OperandCode {
PMOVX_E_G_xmm = OperandCodeBuilder::new().operand_case(110).bits(),
G_Ev_xmm_Ib = OperandCodeBuilder::new().operand_case(111).bits(),
G_E_mm_Ib = OperandCodeBuilder::new().operand_case(112).bits(),
+ AbsFar = OperandCodeBuilder::new().operand_case(113).bits(),
}
const LOCKABLE_INSTRUCTIONS: &[Opcode] = &[
@@ -5404,7 +5418,7 @@ const OPCODES: [OpcodeRecord; 256] = [
OpcodeRecord(Interpretation::Instruction(Opcode::XCHG), OperandCode::Zv_AX_R7),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::CVT_AA),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::CVT_DA),
- OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
+ OpcodeRecord(Interpretation::Instruction(Opcode::CALLF), OperandCode::AbsFar),
OpcodeRecord(Interpretation::Instruction(Opcode::WAIT), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::PUSHF), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::POPF), OperandCode::Nothing),
@@ -8570,6 +8584,19 @@ fn unlikely_operands<
S: DescriptionSink<FieldDescription>
>(decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, mem_oper: OperandSpec, sink: &mut S) -> Result<(), DecodeError> {
match operand_code {
+ OperandCode::AbsFar => {
+ instruction.operands[0] = OperandSpec::AbsoluteFarAddress;
+ instruction.operand_count = 1;
+ instruction.mem_size = 0;
+ // read segment
+ let addr_size = if instruction.prefixes.operand_size() {
+ 4
+ } else {
+ 2
+ };
+ instruction.imm = read_num(words, addr_size)?;
+ instruction.disp = read_num(words, 2)? as u16 as u32;
+ }
OperandCode::G_E_mm_Ib => {
let modrm = read_modrm(words)?;
diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs
index 9cda6be..93f4b8f 100644
--- a/test/protected_mode/mod.rs
+++ b/test/protected_mode/mod.rs
@@ -2393,6 +2393,8 @@ fn prefixed_f30f() {
#[test]
fn only_32bit() {
+ test_display(&[0x9a, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66], "callf 0x6655:0x44332211");
+ test_display(&[0x66, 0x9a, 0x11, 0x22, 0x33, 0x44], "callf 0x4433:0x2211");
test_display(&[0x67, 0xac], "lods al, byte ds:[si]");
test_display(&[0x67, 0xae], "scas byte es:[di], al");
test_display(&[0xac], "lods al, byte ds:[esi]");
diff --git a/test/real_mode/mod.rs b/test/real_mode/mod.rs
index dcd124c..27ef2f6 100644
--- a/test/real_mode/mod.rs
+++ b/test/real_mode/mod.rs
@@ -78,6 +78,8 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str
#[test]
fn only_16bit() {
+ test_display(&[0x66, 0x9a, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66], "callf 0x6655:0x44332211");
+ test_display(&[0x9a, 0x11, 0x22, 0x33, 0x44], "callf 0x4433:0x2211");
test_display(&[0xac], "lods al, byte ds:[si]");
test_display(&[0xae], "scas byte es:[di], al");
test_display(&[0x67, 0xac], "lods al, byte ds:[esi]");