462 lines
17 KiB
Rust
462 lines
17 KiB
Rust
use std::cell::Cell;
|
|
use std::fmt::{Debug, Formatter};
|
|
|
|
use super::byteorder::{ByteOrder, LittleEndian};
|
|
|
|
use emu::operands::{Addr, Reg, RegHi, RegLo};
|
|
use emu::operations as ops;
|
|
use emu::pc::Bus;
|
|
use emu::util::segoff_to_addr;
|
|
|
|
#[allow(non_camel_case_types)]
|
|
#[derive(Clone, Debug, Default)]
|
|
pub struct i8088 {
|
|
// Data Registers
|
|
pub a: Cell<u16>,
|
|
pub b: Cell<u16>,
|
|
pub c: Cell<u16>,
|
|
pub d: Cell<u16>,
|
|
|
|
// Index Registers
|
|
pub si: Cell<u16>, // Source Index
|
|
pub di: Cell<u16>, // Dest Index
|
|
pub bp: Cell<u16>, // Base Pointer
|
|
pub sp: Cell<u16>, // Stack Pointer
|
|
|
|
// Segment Registers
|
|
pub cs: Cell<u16>, // Code Segment
|
|
pub ds: Cell<u16>, // Data Segment
|
|
pub es: Cell<u16>, // Extra Segment
|
|
pub ss: Cell<u16>, // Stack Segment
|
|
|
|
// Pointer Register
|
|
pub ip: Cell<u16>,
|
|
|
|
// Status Register
|
|
pub flags: Flags,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default)]
|
|
pub struct Flags {
|
|
cf: bool, // 0: Carry Flag: 1=CY(Carry), 0=NC(No Carry)
|
|
// 1: Reserved
|
|
pf: bool, // 2: Parity Flag: 1=PE(Even), 0=PO(Odd)
|
|
// 3: Reserved
|
|
af: bool, // 4: Adjust Flag: 1=AC(Aux Carry), 0=NA(No Aux Carry)
|
|
// 5: Reserved
|
|
zf: bool, // 6: Zero Flag: 1=ZR(Zero), 0=NZ(Not Zero)
|
|
sf: bool, // 7: Sign Flag: 1=NG(Negative), 0=PL(Positive)
|
|
tf: bool, // 8: Trap Flag
|
|
ie: bool, // 9: (Real name "IF") Interrupt Enable: 1=EI(Enable Interrupt), 0=DI(Disable Interrupt)
|
|
df: bool, // 10: Direction Flag: 1=DN(Down), 0=UP(Up)
|
|
of: bool, // 11: Overflow Flag: 1=OV(Overflow), 0=NV(Not Overflow)
|
|
// bits 12-15 always 1
|
|
}
|
|
|
|
macro_rules! step {
|
|
// Base case: all args processed and ready to call op
|
|
(@code ( $($done:tt)* ),
|
|
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal,
|
|
()) => {
|
|
ops::$name($($done),*);
|
|
};
|
|
|
|
// Inductive case: decode next arg to be placed in list
|
|
(@code ( $($done:tt)* ),
|
|
$cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal,
|
|
($next:ident $(= $nextrhs:tt)? $($rest:ident $(= $restrhs:tt)?)*)) => {
|
|
step!(@$next$(= $nextrhs)?
|
|
// "cookie" tt to be passed thru to @arg so we have all the args for the recursive step
|
|
( ($($done)*), $cpu, $bus, $prefix, $modrm, $name, $cycles, ($($rest $(= $restrhs)?)*) ),
|
|
$cpu, $bus, $prefix, $modrm)
|
|
};
|
|
|
|
// accept an argument from a decoder and recur to look for next arg
|
|
(@arg ( ($($done:tt)*), $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt,
|
|
$name:ident, $cycles:literal, $rest:tt )
|
|
$(, $arg:expr)?) => {
|
|
step!(@code ($($done)* $($arg)?), $cpu, $bus, $prefix, $modrm, $name, $cycles, $rest)
|
|
};
|
|
|
|
// decoder deferring to sub-decoders, push them onto the decoder stack to process next
|
|
(@push ( $done:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt, $name:ident, $cycles:literal, ( $($rest:tt)* ) ),
|
|
( $($args:tt)* )) => {
|
|
step!(@code $done, $cpu, $bus, $prefix, $modrm, $name, $cycles, ( $($args)* $($rest)* ))
|
|
};
|
|
// Argument Decoders
|
|
|
|
(@addr $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { {
|
|
let a16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
step!(@arg $cookie, &mut Addr { bus: $bus, segment: $segment.unwrap(), offset: a16 } )
|
|
} };
|
|
|
|
(@bus $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
|
step!(@arg $cookie, $bus)
|
|
};
|
|
|
|
(@cpu $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
|
step!(@arg $cookie, $cpu)
|
|
};
|
|
|
|
(@d8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { {
|
|
let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
step!(@arg $cookie, &d8)
|
|
} };
|
|
|
|
(@d16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { {
|
|
let d16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
step!(@arg $cookie, &d16)
|
|
} };
|
|
|
|
(@displace0=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => {
|
|
step!(@arg $cookie, &mut Addr { bus: $bus,
|
|
segment: $segment.unwrap(),
|
|
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? })
|
|
};
|
|
|
|
(@displace8=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { {
|
|
let d8 = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
step!(@arg $cookie, &mut Addr { bus: $bus,
|
|
segment: $segment.unwrap(),
|
|
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? + d8 as u16 })
|
|
} };
|
|
|
|
(@displace16=($reg1:ident $(+ $reg2:ident)? ) $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { {
|
|
let d16 = i8088::next_ip16($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
step!(@arg $cookie, &mut Addr { bus: $bus,
|
|
segment: $segment.unwrap(),
|
|
offset: $cpu.$reg1.get() $(+ $cpu.$reg2.get())? + d16 })
|
|
} };
|
|
|
|
(@mem $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
|
step!(@arg $cookie, &mut $bus.ram)
|
|
};
|
|
|
|
(@modrm $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt,
|
|
($modrm_val:ident,
|
|
{ $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? },
|
|
$modrm16:tt,
|
|
$modrm8:tt
|
|
)) => { {
|
|
let modrm_val = $modrm_val & !0x38;
|
|
match modrm_val {
|
|
$( $val => step!(@push $cookie, ($($args $(= $argrhs)?)* ) ) ),*,
|
|
_ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu)
|
|
}
|
|
} };
|
|
|
|
(@modrm16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt,
|
|
($modrm_val:ident,
|
|
{ $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? },
|
|
{ $($val16:literal => $($args16:ident $(= $argrhs16:tt)?),* / $mode16:ident $cycles16:literal),*$(,)? },
|
|
$modrm8:tt
|
|
)) => { {
|
|
let modrm_val = $modrm_val & !0x38;
|
|
match modrm_val {
|
|
$( $val => step!(@push $cookie, ($($args $(= $argrhs)?)* ) ) ),*,
|
|
$( $val16 => step!(@push $cookie, ($($args16 $(= $argrhs16)?)* ) ) ),*,
|
|
_ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu)
|
|
}
|
|
} };
|
|
|
|
(@modrm8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt,
|
|
($modrm_val:ident,
|
|
{ $($val:literal => $($args:ident $(= $argrhs:tt)?),* / $mode:ident $cycles:literal),*$(,)? },
|
|
$modrm16:tt,
|
|
{ $($val8:literal => $($args8:ident $(= $argrhs8:tt)?),* / $mode8:ident $cycles8:literal),*$(,)? }
|
|
)) => { {
|
|
let modrm_val = $modrm_val & !0x38;
|
|
match modrm_val {
|
|
$( $val => step!(@push $cookie, ($($args $(= $argrhs)?)* ) ) ),*,
|
|
$( $val8 => step!(@push $cookie, ($($args8 $(= $argrhs8)?)* ) ) ),*,
|
|
_ => unimplemented!("modrm: {:02X}({:02X})\ncpu: {:#X?}", $modrm_val, modrm_val, $cpu)
|
|
}
|
|
} };
|
|
|
|
(@prefix $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { continue $prefix_loop; };
|
|
|
|
(@r16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, $modrm:tt, $modrm16:tt, $modrm8:tt)) => {
|
|
// TODO: Should these also be passed into the macro like the modrm specs?
|
|
match $modrm_val >> 3 & 0x7 {
|
|
0 => step!(@arg $cookie, &mut Reg { reg: &$cpu.a } ),
|
|
1 => step!(@arg $cookie, &mut Reg { reg: &$cpu.c } ),
|
|
2 => step!(@arg $cookie, &mut Reg { reg: &$cpu.d } ),
|
|
3 => step!(@arg $cookie, &mut Reg { reg: &$cpu.b } ),
|
|
4 => step!(@arg $cookie, &mut Reg { reg: &$cpu.sp } ),
|
|
5 => step!(@arg $cookie, &mut Reg { reg: &$cpu.bp } ),
|
|
6 => step!(@arg $cookie, &mut Reg { reg: &$cpu.si } ),
|
|
7 => step!(@arg $cookie, &mut Reg { reg: &$cpu.di } ),
|
|
_ => unreachable!()
|
|
};
|
|
};
|
|
|
|
(@r8 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, $modrm:tt, $modrm16:tt, $modrm8:tt)) => {
|
|
// TODO: Should these also be passed into the macro like the modrm specs?
|
|
match $modrm_val >> 3 & 0x7 {
|
|
0 => step!(@arg $cookie, &mut RegHi { reg: &$cpu.a } ),
|
|
1 => step!(@arg $cookie, &mut RegHi { reg: &$cpu.c } ),
|
|
2 => step!(@arg $cookie, &mut RegHi { reg: &$cpu.d } ),
|
|
3 => step!(@arg $cookie, &mut RegHi { reg: &$cpu.b } ),
|
|
4 => step!(@arg $cookie, &mut RegLo { reg: &$cpu.a } ),
|
|
5 => step!(@arg $cookie, &mut RegLo { reg: &$cpu.c } ),
|
|
6 => step!(@arg $cookie, &mut RegLo { reg: &$cpu.d } ),
|
|
7 => step!(@arg $cookie, &mut RegLo { reg: &$cpu.b } ),
|
|
_ => unreachable!()
|
|
};
|
|
};
|
|
|
|
(@reg=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
|
step!(@arg $cookie, &mut Reg { reg: &$cpu.$reg });
|
|
};
|
|
|
|
(@reghi=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
|
step!(@arg $cookie, &mut RegHi { reg: &$cpu.$reg });
|
|
};
|
|
|
|
(@reglo=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
|
step!(@arg $cookie, &mut RegLo { reg: &$cpu.$reg });
|
|
};
|
|
|
|
(@regval=$reg:ident $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => {
|
|
step!(@arg $cookie, $cpu.$reg.get());
|
|
};
|
|
|
|
(@rel16 $cookie:tt, $cpu:expr, $bus:expr, $prefix:tt, $modrm:tt) => { {
|
|
let mut buf = [0; 2];
|
|
buf[0] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
buf[1] = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
step!(@arg $cookie, LittleEndian::read_i16(&buf))
|
|
} };
|
|
|
|
(@seg=$seg:ident $cookie:tt, $cpu:expr, $bus:expr, ($segment:ident, $prefix_loop:lifetime), $modrm:tt) => { {
|
|
$segment = $segment.or(Some($cpu.$seg.get()));
|
|
step!(@arg $cookie)
|
|
} };
|
|
|
|
// Group Decoder
|
|
(@group $cpu:expr, $bus:expr, $prefix:tt, ($modrm_val:ident, $modrm:tt, $modrm16:tt, $modrm8:tt), $code:literal,
|
|
{ $( $subcode:literal =>
|
|
$name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal),*$(,)? } ) => { {
|
|
let subcode = $modrm_val & 0x38;
|
|
match subcode {
|
|
$( $subcode => step!(@code (), $cpu, $bus, $prefix, ($modrm_val, $modrm, $modrm16, $modrm8), $name, $cycles, ($($args $(= $argrhs)?)*)) ),*,
|
|
_ => unimplemented!("opcode: {:02X} {:02X}({:02X})\ncpu: {:#X?}", $code, $modrm_val, subcode, $cpu)
|
|
} }
|
|
};
|
|
|
|
// Entry Point
|
|
(($cpu:expr, $bus:expr) =>
|
|
opcodes: { $(
|
|
$code:literal
|
|
|
|
// Non-group opcodes
|
|
$( $($ext:pat)? =>
|
|
$name:ident[$($args:ident $(= $argrhs:tt)?),*] / $cycles:literal
|
|
)?
|
|
|
|
// Group opcodes
|
|
$( : $subcodes:tt )?
|
|
),*
|
|
$(,)? },
|
|
modrm: $modrm:tt,
|
|
modrm16: $modrm16:tt,
|
|
modrm8: $modrm8:tt
|
|
) => {
|
|
{
|
|
let mut segment = None;
|
|
let modrm_val: u8; // Type ascription unnecessary but gives better err messages when missing $ext
|
|
'prefix_loop: loop {
|
|
let opcode = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
match(opcode) {
|
|
$( $( $code => {
|
|
$( let $ext = (); /* No-op just to trigger expansion. */
|
|
modrm_val = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus); )?
|
|
step!(@code (), $cpu, $bus, (segment, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), $name, $cycles, ($($args $(= $argrhs)?)*))
|
|
}
|
|
)?
|
|
$( $code => {
|
|
modrm_val = i8088::next_ip($cpu.cs.get(), &mut $cpu.ip, $bus);
|
|
step!(@group $cpu, $bus, (segment, 'prefix_loop), (modrm_val, $modrm, $modrm16, $modrm8), $code, $subcodes)
|
|
}
|
|
)?
|
|
),*,
|
|
_ => unimplemented!("opcode: {:02X}\ncpu: {:#X?}", opcode, $cpu)
|
|
}
|
|
// Only prefixes loop and they do so with an explicit continue statement
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl i8088 {
|
|
pub fn run(&mut self, bus: &mut Bus) {
|
|
loop {
|
|
step!((self, bus) =>
|
|
opcodes: {
|
|
0x26 => nop[seg=es, prefix] / 2,
|
|
0x2e => nop[seg=cs, prefix] / 2,
|
|
0x36 => nop[seg=ss, prefix] / 2,
|
|
0x3e => nop[seg=ds, prefix] / 2,
|
|
0x60 => show[cpu] / 0, // Fake opcode for debugging
|
|
0x61 => peek[seg=ds, addr] / 0, // Fake opcode for debugging
|
|
0x88 _ => mov[modrm8, r8] / "2/13+",
|
|
0x89 _ => mov[modrm16, r16] / "2/13+",
|
|
0x8A _ => mov[r8, modrm8] / "2/12+",
|
|
0x8B _ => mov[r16, modrm16] / "2/12+",
|
|
0x8C: { 0x00 => mov[modrm16, reg=es] / "2/13+",
|
|
0x08 => mov[modrm16, reg=cs] / "2/13+",
|
|
0x10 => mov[modrm16, reg=ss] / "2/13+",
|
|
0x18 => mov[modrm16, reg=ds] / "2/13+", },
|
|
0x8D _ => lea[r16, modrm] / "2+",
|
|
0x8E: { 0x00 => mov[reg=es, modrm16] / "2/12+",
|
|
0x08 => mov[reg=cs, modrm16] / "2/12+",
|
|
0x10 => mov[reg=ss, modrm16] / "2/12+",
|
|
0x18 => mov[reg=ds, modrm16] / "2/12+", },
|
|
0x90 => nop[] / 3,
|
|
0xA0 => mov[reglo=a, addr] / 14,
|
|
0xA1 => mov[reg=a, addr] / 14,
|
|
0xA2 => mov[addr, reglo=a] / 14,
|
|
0xA3 => mov[addr, reg=a] / 14,
|
|
0xB0 => mov[reglo=a, d8] / 4,
|
|
0xB1 => mov[reglo=c, d8] / 4,
|
|
0xB2 => mov[reglo=d, d8] / 4,
|
|
0xB3 => mov[reglo=b, d8] / 4,
|
|
0xB4 => mov[reghi=a, d8] / 4,
|
|
0xB5 => mov[reghi=c, d8] / 4,
|
|
0xB6 => mov[reghi=d, d8] / 4,
|
|
0xB7 => mov[reghi=b, d8] / 4,
|
|
0xB8 => mov[reg=a, d16] / 4,
|
|
0xB9 => mov[reg=c, d16] / 4,
|
|
0xBA => mov[reg=d, d16] / 4,
|
|
0xBB => mov[reg=b, d16] / 4,
|
|
0xBC => mov[reg=sp, d16] / 4,
|
|
0xBD => mov[reg=bp, d16] / 4,
|
|
0xBE => mov[reg=si, d16] / 4,
|
|
0xBF => mov[reg=di, d16] / 4,
|
|
0xC3 => ret[reg=ip, regval=ss, reg=sp, mem] / 20,
|
|
0xCD => int[cpu, bus, d8] / 71,
|
|
0xE8 => call[reg=ip, regval=ss, reg=sp, mem, rel16] / 23,
|
|
},
|
|
modrm: {
|
|
0x00 => seg=ds, displace0=(b+si) / M 7,
|
|
0x01 => seg=ds, displace0=(b+di) / M 8,
|
|
0x02 => seg=ss, displace0=(bp+si) / M 8,
|
|
0x03 => seg=ss, displace0=(bp+di) / M 7,
|
|
0x04 => seg=ds, displace0=(si) / M 5,
|
|
0x05 => seg=ds, displace0=(di) / M 5,
|
|
0x06 => seg=ds, addr / M 6,
|
|
0x07 => seg=ds, displace0=(b) / M 5,
|
|
0x40 => seg=ds, displace8=(b+si) / M 11,
|
|
0x41 => seg=ds, displace8=(b+di) / M 12,
|
|
0x42 => seg=ss, displace8=(bp+si) / M 12,
|
|
0x43 => seg=ss, displace8=(bp+di) / M 11,
|
|
0x44 => seg=ds, displace8=(si) / M 9,
|
|
0x45 => seg=ds, displace8=(di) / M 9,
|
|
0x46 => seg=ss, displace8=(bp) / M 9,
|
|
0x47 => seg=ds, displace8=(b) / M 9,
|
|
0x80 => seg=ds, displace16=(b+si) / M 11,
|
|
0x81 => seg=ds, displace16=(b+di) / M 12,
|
|
0x82 => seg=ss, displace16=(bp+si) / M 12,
|
|
0x83 => seg=ss, displace16=(bp+di) / M 11,
|
|
0x84 => seg=ds, displace16=(si) / M 9,
|
|
0x85 => seg=ds, displace16=(di) / M 9,
|
|
0x86 => seg=ss, displace16=(bp) / M 9,
|
|
0x87 => seg=ds, displace16=(b) / M 9,
|
|
},
|
|
modrm16: {
|
|
0xC0 => reg=a / R 0,
|
|
0xC1 => reg=c / R 0,
|
|
0xC2 => reg=d / R 0,
|
|
0xC3 => reg=b / R 0,
|
|
0xC4 => reg=sp / R 0,
|
|
0xC5 => reg=bp / R 0,
|
|
0xC6 => reg=si / R 0,
|
|
0xC7 => reg=di / R 0,
|
|
},
|
|
modrm8: {
|
|
0xC0 => reglo=a / R 0,
|
|
0xC1 => reglo=c / R 0,
|
|
0xC2 => reglo=d / R 0,
|
|
0xC3 => reglo=b / R 0,
|
|
0xC4 => reghi=a / R 0,
|
|
0xC5 => reghi=c / R 0,
|
|
0xC6 => reghi=d / R 0,
|
|
0xC7 => reghi=b / R 0,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn next_ip<'a>(cs: u16, ip: &mut Cell<u16>, bus: &Bus) -> u8 {
|
|
let eip = segoff_to_addr(cs, ip.get());
|
|
ip.set(ip.get() + 1);
|
|
|
|
// We'll assume cpu is always executing in RAM. Also assume the
|
|
// IP doesn't reach the end of the segment (My guess is that it
|
|
// should loop within the segment if it did)
|
|
bus.ram[eip - Bus::RAM_LOCATION]
|
|
}
|
|
|
|
fn next_ip16<'a>(cs: u16, ip: &mut Cell<u16>, bus: &Bus) -> u16 {
|
|
let eip = segoff_to_addr(cs, ip.get());
|
|
ip.set(ip.get() + 2);
|
|
|
|
// We'll assume cpu is always executing in RAM. Also assume the
|
|
// IP doesn't reach the end of the segment (My guess is that it
|
|
// should loop within the segment if it did)
|
|
let buf = &bus.ram[eip - Bus::RAM_LOCATION .. eip + 2 - Bus::RAM_LOCATION];
|
|
LittleEndian::read_u16(buf)
|
|
}
|
|
}
|
|
|
|
impl From<u16> for Flags {
|
|
fn from(flags: u16) -> Self {
|
|
Self {
|
|
cf: flags & 1 != 0,
|
|
pf: flags & 1 << 2 != 0,
|
|
af: flags & 1 << 4 != 0,
|
|
zf: flags & 1 << 6 != 0,
|
|
sf: flags & 1 << 7 != 0,
|
|
tf: flags & 1 << 8 != 0,
|
|
ie: flags & 1 << 9 != 0,
|
|
df: flags & 1 << 10 != 0,
|
|
of: flags & 1 << 11 != 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Flags> for u16 {
|
|
fn from(flags: Flags) -> Self {
|
|
0b1111_0000_0010_1010 // Not sure what all reserved bits should be, but it shouldn't matter
|
|
| (flags.cf as u16)
|
|
| (flags.pf as u16) << 2
|
|
| (flags.af as u16) << 4
|
|
| (flags.zf as u16) << 6
|
|
| (flags.sf as u16) << 7
|
|
| (flags.tf as u16) << 8
|
|
| (flags.ie as u16) << 9
|
|
| (flags.df as u16) << 10
|
|
| (flags.of as u16) << 11
|
|
}
|
|
}
|
|
|
|
impl Debug for Flags {
|
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> {
|
|
use std::fmt::Write;
|
|
fmt.write_str("[ ")?;
|
|
for flag in [ (self.cf, "CF "),
|
|
(self.pf, "PF "),
|
|
(self.af, "AF "),
|
|
(self.zf, "ZF "),
|
|
(self.sf, "SF "),
|
|
(self.tf, "TF "),
|
|
(self.ie, "IF "),
|
|
(self.df, "DF "),
|
|
(self.of, "OF ") ].iter() {
|
|
if flag.0 { fmt.write_str(flag.1)? };
|
|
}
|
|
fmt.write_char(']')?;
|
|
Ok(())
|
|
}
|
|
}
|