mirror of
https://github.com/transistorfet/moa.git
synced 2024-09-11 20:56:13 +00:00
I wanted to make this a bit more modular, so it's easier in theory to write external crates that can reuse bits, and selectively compile in bits, such as adding new systems or new cpu implementations
890 lines
37 KiB
Rust
890 lines
37 KiB
Rust
|
|
use moa_core::{System, Error, ErrorType, ClockElapsed, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16};
|
|
|
|
use crate::decode::{Condition, Instruction, LoadTarget, Target, RegisterPair, IndexRegister, SpecialRegister, IndexRegisterHalf, Size, Direction};
|
|
use crate::state::{Z80, Status, Flags, Register};
|
|
|
|
|
|
const DEV_NAME: &'static str = "z80-cpu";
|
|
|
|
const FLAGS_NUMERIC: u8 = 0xC0;
|
|
const FLAGS_ARITHMETIC: u8 = 0x17;
|
|
const FLAGS_CARRY_HALF_CARRY: u8 = 0x11;
|
|
|
|
|
|
enum RotateType {
|
|
Bit8,
|
|
Bit9,
|
|
}
|
|
|
|
|
|
impl Steppable for Z80 {
|
|
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
|
let clocks = if self.reset.get() {
|
|
//println!("RESET");
|
|
self.reset()?
|
|
} else if self.bus_request.get() {
|
|
//println!("BUS REQ");
|
|
4
|
|
} else {
|
|
//println!("RUNNING {:?}", self.decoder.instruction);
|
|
self.step_internal(system)?
|
|
};
|
|
|
|
Ok((1_000_000_000 / self.frequency as ClockElapsed) * clocks as ClockElapsed)
|
|
}
|
|
|
|
fn on_error(&mut self, _system: &System) {
|
|
self.dump_state();
|
|
}
|
|
}
|
|
|
|
impl Interruptable for Z80 { }
|
|
|
|
|
|
impl Transmutable for Z80 {
|
|
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
|
|
Some(self)
|
|
}
|
|
|
|
fn as_interruptable(&mut self) -> Option<&mut dyn Interruptable> {
|
|
Some(self)
|
|
}
|
|
|
|
fn as_debuggable(&mut self) -> Option<&mut dyn Debuggable> {
|
|
Some(self)
|
|
}
|
|
}
|
|
|
|
|
|
impl Z80 {
|
|
pub fn step_internal(&mut self, system: &System) -> Result<u16, Error> {
|
|
match self.state.status {
|
|
Status::Init => self.init(),
|
|
Status::Halted => Err(Error::new("CPU stopped")),
|
|
Status::Running => {
|
|
match self.cycle_one(system) {
|
|
Ok(clocks) => Ok(clocks),
|
|
Err(Error { err: ErrorType::Processor, .. }) => {
|
|
//self.exception(system, native as u8, false)?;
|
|
Ok(4)
|
|
},
|
|
Err(err) => Err(err),
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn init(&mut self) -> Result<u16, Error> {
|
|
self.state.pc = 0;
|
|
self.state.status = Status::Running;
|
|
Ok(16)
|
|
}
|
|
|
|
pub fn reset(&mut self) -> Result<u16, Error> {
|
|
self.clear_state();
|
|
Ok(16)
|
|
}
|
|
|
|
pub fn cycle_one(&mut self, system: &System) -> Result<u16, Error> {
|
|
self.decode_next()?;
|
|
self.execute_current()?;
|
|
//self.check_pending_interrupts(system)?;
|
|
self.check_breakpoints(system);
|
|
Ok(self.decoder.execution_time)
|
|
}
|
|
|
|
pub fn decode_next(&mut self) -> Result<(), Error> {
|
|
self.decoder.decode_at(&mut self.port, self.state.pc)?;
|
|
self.state.pc = self.decoder.end;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn execute_current(&mut self) -> Result<(), Error> {
|
|
match self.decoder.instruction {
|
|
Instruction::ADCa(target) => {
|
|
let src = self.get_target_value(target)?;
|
|
let acc = self.get_register_value(Register::A);
|
|
|
|
let (result1, carry1, overflow1, half_carry1) = add_bytes(acc, self.get_flag(Flags::Carry) as u8);
|
|
let (result2, carry2, overflow2, half_carry2) = add_bytes(result1, src);
|
|
self.set_arithmetic_op_flags(result2 as u16, Size::Byte, false, carry1 | carry2, overflow1 | overflow2, half_carry1 | half_carry2);
|
|
|
|
self.set_register_value(Register::A, result2);
|
|
},
|
|
Instruction::ADC16(dest_pair, src_pair) => {
|
|
let src = self.get_register_pair_value(src_pair);
|
|
let dest = self.get_register_pair_value(dest_pair);
|
|
|
|
let (result1, carry1, overflow1, half_carry1) = add_words(dest, self.get_flag(Flags::Carry) as u16);
|
|
let (result2, carry2, overflow2, half_carry2) = add_words(result1, src);
|
|
self.set_arithmetic_op_flags(result2, Size::Word, false, carry1 | carry2, overflow1 | overflow2, half_carry1 | half_carry2);
|
|
|
|
self.set_register_pair_value(dest_pair, result2);
|
|
},
|
|
Instruction::ADDa(target) => {
|
|
let src = self.get_target_value(target)?;
|
|
let acc = self.get_register_value(Register::A);
|
|
|
|
let (result, carry, overflow, half_carry) = add_bytes(acc, src);
|
|
self.set_arithmetic_op_flags(result as u16, Size::Byte, false, carry, overflow, half_carry);
|
|
|
|
self.set_register_value(Register::A, result);
|
|
},
|
|
Instruction::ADD16(dest_pair, src_pair) => {
|
|
let src = self.get_register_pair_value(src_pair);
|
|
let dest = self.get_register_pair_value(dest_pair);
|
|
|
|
let (result, carry, _, half_carry) = add_words(dest, src);
|
|
self.set_flag(Flags::AddSubtract, false);
|
|
self.set_flag(Flags::Carry, carry);
|
|
self.set_flag(Flags::HalfCarry, half_carry);
|
|
|
|
self.set_register_pair_value(dest_pair, result);
|
|
},
|
|
Instruction::AND(target) => {
|
|
let acc = self.get_register_value(Register::A);
|
|
let value = self.get_target_value(target)?;
|
|
let result = acc & value;
|
|
self.set_register_value(Register::A, result);
|
|
self.set_logic_op_flags(result, false, true);
|
|
},
|
|
Instruction::BIT(bit, target) => {
|
|
let value = self.get_target_value(target)?;
|
|
let result = value & (1 << bit);
|
|
self.set_flag(Flags::Zero, result == 0);
|
|
self.set_flag(Flags::AddSubtract, false);
|
|
self.set_flag(Flags::HalfCarry, true);
|
|
},
|
|
Instruction::CALL(addr) => {
|
|
self.push_word(self.decoder.end)?;
|
|
self.state.pc = addr;
|
|
},
|
|
Instruction::CALLcc(cond, addr) => {
|
|
if self.get_current_condition(cond) {
|
|
self.push_word(self.decoder.end)?;
|
|
self.state.pc = addr;
|
|
}
|
|
},
|
|
Instruction::CCF => {
|
|
self.set_flag(Flags::AddSubtract, false);
|
|
self.set_flag(Flags::HalfCarry, self.get_flag(Flags::Carry));
|
|
self.set_flag(Flags::Carry, !self.get_flag(Flags::Carry));
|
|
},
|
|
Instruction::CP(target) => {
|
|
let src = self.get_target_value(target)?;
|
|
let acc = self.get_register_value(Register::A);
|
|
|
|
let (result, carry, overflow) = sub_bytes(acc, src);
|
|
self.set_arithmetic_op_flags(result as u16, Size::Byte, true, carry, overflow, (result & 0x10) != 0);
|
|
},
|
|
//Instruction::CPD => {
|
|
//},
|
|
//Instruction::CPDR => {
|
|
//},
|
|
//Instruction::CPI => {
|
|
//},
|
|
//Instruction::CPIR => {
|
|
//},
|
|
Instruction::CPL => {
|
|
let value = self.get_register_value(Register::A);
|
|
self.set_register_value(Register::A, !value);
|
|
self.set_flag(Flags::HalfCarry, true);
|
|
self.set_flag(Flags::AddSubtract, true);
|
|
},
|
|
//Instruction::DAA => {
|
|
//},
|
|
Instruction::DEC16(regpair) => {
|
|
let value = self.get_register_pair_value(regpair);
|
|
|
|
let (result, _, _) = sub_words(value, 1);
|
|
|
|
self.set_register_pair_value(regpair, result);
|
|
},
|
|
Instruction::DEC8(target) => {
|
|
let value = self.get_target_value(target)?;
|
|
|
|
let (result, _, overflow) = sub_bytes(value, 1);
|
|
let carry = self.get_flag(Flags::Carry); // Preserve the carry bit, according to Z80 reference
|
|
self.set_arithmetic_op_flags(result as u16, Size::Byte, true, carry, overflow, (result & 0x10) != 0);
|
|
|
|
self.set_target_value(target, result)?;
|
|
},
|
|
Instruction::DI => {
|
|
self.state.interrupts_enabled = false;
|
|
},
|
|
Instruction::DJNZ(offset) => {
|
|
let value = self.get_register_value(Register::B);
|
|
let result = value.wrapping_sub(1);
|
|
self.set_register_value(Register::B, result);
|
|
|
|
if result != 0 {
|
|
self.state.pc = ((self.state.pc as i16) + (offset as i16)) as u16;
|
|
}
|
|
},
|
|
Instruction::EI => {
|
|
self.state.interrupts_enabled = true;
|
|
},
|
|
Instruction::EXX => {
|
|
for i in 0..6 {
|
|
let (normal, shadow) = (self.state.reg[i], self.state.shadow_reg[i]);
|
|
self.state.reg[i] = shadow;
|
|
self.state.shadow_reg[i] = normal;
|
|
}
|
|
},
|
|
Instruction::EXafaf => {
|
|
for i in 6..8 {
|
|
let (normal, shadow) = (self.state.reg[i], self.state.shadow_reg[i]);
|
|
self.state.reg[i] = shadow;
|
|
self.state.shadow_reg[i] = normal;
|
|
}
|
|
},
|
|
Instruction::EXhlde => {
|
|
let (hl, de) = (self.get_register_pair_value(RegisterPair::HL), self.get_register_pair_value(RegisterPair::DE));
|
|
self.set_register_pair_value(RegisterPair::DE, hl);
|
|
self.set_register_pair_value(RegisterPair::HL, de);
|
|
},
|
|
Instruction::EXsp(regpair) => {
|
|
let reg_value = self.get_register_pair_value(regpair);
|
|
let sp = self.get_register_pair_value(RegisterPair::SP);
|
|
let sp_value = self.port.read_leu16(sp as Address)?;
|
|
self.set_register_pair_value(regpair, sp_value);
|
|
self.port.write_leu16(sp as Address, reg_value)?;
|
|
},
|
|
Instruction::HALT => {
|
|
self.state.status = Status::Halted;
|
|
},
|
|
Instruction::IM(mode) => {
|
|
self.state.interrupt_mode = mode;
|
|
},
|
|
Instruction::INC16(regpair) => {
|
|
let value = self.get_register_pair_value(regpair);
|
|
|
|
let (result, _, _, _) = add_words(value, 1);
|
|
|
|
self.set_register_pair_value(regpair, result);
|
|
},
|
|
Instruction::INC8(target) => {
|
|
let value = self.get_target_value(target)?;
|
|
|
|
let (result, _, overflow, _) = add_bytes(value, 1);
|
|
let carry = self.get_flag(Flags::Carry); // Preserve the carry bit, according to Z80 reference
|
|
self.set_arithmetic_op_flags(result as u16, Size::Byte, false, carry, overflow, (result & 0x10) != 0);
|
|
|
|
self.set_target_value(target, result)?;
|
|
},
|
|
//Instruction::IND => {
|
|
//},
|
|
//Instruction::INDR => {
|
|
//},
|
|
//Instruction::INI => {
|
|
//},
|
|
//Instruction::INIR => {
|
|
//},
|
|
//Instruction::INic(reg) => {
|
|
//},
|
|
//Instruction::INx(u8) => {
|
|
//},
|
|
Instruction::JP(addr) => {
|
|
self.state.pc = addr;
|
|
},
|
|
Instruction::JPIndirect(regpair) => {
|
|
let value = self.get_register_pair_value(regpair);
|
|
//let addr = self.port.read_leu16(value as Address)?;
|
|
self.state.pc = value;
|
|
},
|
|
Instruction::JPcc(cond, addr) => {
|
|
if self.get_current_condition(cond) {
|
|
self.state.pc = addr;
|
|
}
|
|
},
|
|
Instruction::JR(offset) => {
|
|
self.state.pc = ((self.state.pc as i16) + (offset as i16)) as u16;
|
|
},
|
|
Instruction::JRcc(cond, offset) => {
|
|
if self.get_current_condition(cond) {
|
|
self.state.pc = ((self.state.pc as i16) + (offset as i16)) as u16;
|
|
}
|
|
},
|
|
Instruction::LD(dest, src) => {
|
|
let src_value = self.get_load_target_value(src)?;
|
|
self.set_load_target_value(dest, src_value)?;
|
|
},
|
|
Instruction::LDsr(special_reg, dir) => {
|
|
let addr = match special_reg {
|
|
SpecialRegister::I => &mut self.state.i,
|
|
SpecialRegister::R => &mut self.state.r,
|
|
};
|
|
|
|
match dir {
|
|
Direction::FromAcc => { *addr = self.state.reg[Register::A as usize]; },
|
|
Direction::ToAcc => { self.state.reg[Register::A as usize] = *addr; },
|
|
}
|
|
}
|
|
Instruction::LDD | Instruction::LDDR | Instruction::LDI | Instruction::LDIR => {
|
|
let diff = if self.decoder.instruction == Instruction::LDI || self.decoder.instruction == Instruction::LDIR {
|
|
1
|
|
} else {
|
|
-1
|
|
};
|
|
|
|
let src_value = self.get_load_target_value(LoadTarget::IndirectRegByte(RegisterPair::HL))?;
|
|
self.set_load_target_value(LoadTarget::IndirectRegByte(RegisterPair::DE), src_value)?;
|
|
self.add_to_regpair(RegisterPair::DE, diff);
|
|
self.add_to_regpair(RegisterPair::HL, diff);
|
|
let count = self.add_to_regpair(RegisterPair::BC, -1);
|
|
let mask = (Flags::AddSubtract as u8) | (Flags::HalfCarry as u8) | (Flags::Parity as u8);
|
|
let parity = if count != 0 { Flags::Parity as u8 } else { 0 };
|
|
self.set_flags(mask, parity);
|
|
|
|
if self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR {
|
|
if count != 0 {
|
|
self.state.pc -= 2;
|
|
}
|
|
}
|
|
},
|
|
Instruction::NEG => {
|
|
let acc = self.get_register_value(Register::A);
|
|
|
|
let (result, carry, overflow) = sub_bytes(0, acc);
|
|
self.set_arithmetic_op_flags(result as u16, Size::Byte, true, carry, overflow, (acc & 0x10) != 0 && (result & 0x10) == 0);
|
|
|
|
self.set_register_value(Register::A, result);
|
|
},
|
|
Instruction::NOP => { },
|
|
Instruction::OR(target) => {
|
|
let acc = self.get_register_value(Register::A);
|
|
let value = self.get_target_value(target)?;
|
|
let result = acc | value;
|
|
self.set_register_value(Register::A, result);
|
|
self.set_logic_op_flags(result, false, false);
|
|
},
|
|
//Instruction::OTDR => {
|
|
//},
|
|
//Instruction::OTIR => {
|
|
//},
|
|
//Instruction::OUTD => {
|
|
//},
|
|
//Instruction::OUTI => {
|
|
//},
|
|
//Instruction::OUTic(reg) => {
|
|
//},
|
|
Instruction::OUTx(port) => {
|
|
// TODO this needs to be fixed
|
|
println!("OUT ({:x}), {:x} {}", port, self.state.reg[Register::A as usize], self.state.reg[Register::A as usize] as char);
|
|
},
|
|
Instruction::POP(regpair) => {
|
|
let value = self.pop_word()?;
|
|
self.set_register_pair_value(regpair, value);
|
|
},
|
|
Instruction::PUSH(regpair) => {
|
|
let value = self.get_register_pair_value(regpair);
|
|
self.push_word(value)?;
|
|
},
|
|
Instruction::RES(bit, target, opt_copy) => {
|
|
let mut value = self.get_target_value(target)?;
|
|
value = value & !(1 << bit);
|
|
self.set_target_value(target, value)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::RET => {
|
|
self.state.pc = self.pop_word()?;
|
|
},
|
|
//Instruction::RETI => {
|
|
//},
|
|
//Instruction::RETN => {
|
|
//},
|
|
Instruction::RETcc(cond) => {
|
|
if self.get_current_condition(cond) {
|
|
self.state.pc = self.pop_word()?;
|
|
}
|
|
},
|
|
Instruction::RL(target, opt_copy) => {
|
|
let value = self.get_target_value(target)?;
|
|
let (result, out_bit) = self.rotate_left(value, RotateType::Bit9);
|
|
self.set_logic_op_flags(result, out_bit, false);
|
|
self.set_target_value(target, result)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::RLA => {
|
|
let value = self.get_register_value(Register::A);
|
|
let (result, out_bit) = self.rotate_left(value, RotateType::Bit9);
|
|
self.set_flag(Flags::AddSubtract, false);
|
|
self.set_flag(Flags::HalfCarry, false);
|
|
self.set_flag(Flags::Carry, out_bit);
|
|
self.set_register_value(Register::A, result);
|
|
},
|
|
Instruction::RLC(target, opt_copy) => {
|
|
let value = self.get_target_value(target)?;
|
|
let (result, out_bit) = self.rotate_left(value, RotateType::Bit8);
|
|
self.set_logic_op_flags(result, out_bit, false);
|
|
self.set_target_value(target, result)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::RLCA => {
|
|
let value = self.get_register_value(Register::A);
|
|
let (result, out_bit) = self.rotate_left(value, RotateType::Bit8);
|
|
self.set_flag(Flags::AddSubtract, false);
|
|
self.set_flag(Flags::HalfCarry, false);
|
|
self.set_flag(Flags::Carry, out_bit);
|
|
self.set_register_value(Register::A, result);
|
|
},
|
|
//Instruction::RLD => {
|
|
//},
|
|
Instruction::RR(target, opt_copy) => {
|
|
let value = self.get_target_value(target)?;
|
|
let (result, out_bit) = self.rotate_right(value, RotateType::Bit9);
|
|
self.set_logic_op_flags(result, out_bit, false);
|
|
self.set_target_value(target, result)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::RRA => {
|
|
let value = self.get_register_value(Register::A);
|
|
let (result, out_bit) = self.rotate_right(value, RotateType::Bit9);
|
|
self.set_flag(Flags::AddSubtract, false);
|
|
self.set_flag(Flags::HalfCarry, false);
|
|
self.set_flag(Flags::Carry, out_bit);
|
|
self.set_register_value(Register::A, result);
|
|
},
|
|
Instruction::RRC(target, opt_copy) => {
|
|
let value = self.get_target_value(target)?;
|
|
let (result, out_bit) = self.rotate_right(value, RotateType::Bit8);
|
|
self.set_logic_op_flags(result, out_bit, false);
|
|
self.set_target_value(target, result)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::RRCA => {
|
|
let value = self.get_register_value(Register::A);
|
|
let (result, out_bit) = self.rotate_right(value, RotateType::Bit8);
|
|
self.set_flag(Flags::AddSubtract, false);
|
|
self.set_flag(Flags::HalfCarry, false);
|
|
self.set_flag(Flags::Carry, out_bit);
|
|
self.set_register_value(Register::A, result);
|
|
},
|
|
//Instruction::RRD => {
|
|
//},
|
|
Instruction::RST(addr) => {
|
|
self.push_word(self.decoder.end)?;
|
|
self.state.pc = addr as u16;
|
|
},
|
|
Instruction::SBCa(target) => {
|
|
let src = self.get_target_value(target)?;
|
|
let acc = self.get_register_value(Register::A);
|
|
|
|
let (result1, carry1, overflow1) = sub_bytes(acc, self.get_flag(Flags::Carry) as u8);
|
|
let (result2, carry2, overflow2) = sub_bytes(result1, src);
|
|
self.set_arithmetic_op_flags(result2 as u16, Size::Byte, true, carry1 | carry2, overflow1 | overflow2, (result2 & 0x10) != 0);
|
|
|
|
self.set_register_value(Register::A, result2);
|
|
},
|
|
Instruction::SBC16(dest_pair, src_pair) => {
|
|
let src = self.get_register_pair_value(src_pair);
|
|
let dest = self.get_register_pair_value(dest_pair);
|
|
|
|
let (result1, carry1, overflow1) = sub_words(dest, self.get_flag(Flags::Carry) as u16);
|
|
let (result2, carry2, overflow2) = sub_words(result1, src);
|
|
self.set_arithmetic_op_flags(result2, Size::Word, true, carry1 | carry2, overflow1 | overflow2, (result2 & 0x10) != 0);
|
|
|
|
self.set_register_pair_value(dest_pair, result2);
|
|
},
|
|
Instruction::SCF => {
|
|
self.set_flag(Flags::AddSubtract, false);
|
|
self.set_flag(Flags::HalfCarry, false);
|
|
self.set_flag(Flags::Carry, true);
|
|
},
|
|
Instruction::SET(bit, target, opt_copy) => {
|
|
let mut value = self.get_target_value(target)?;
|
|
value = value | (1 << bit);
|
|
self.set_target_value(target, value)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::SLA(target, opt_copy) => {
|
|
let value = self.get_target_value(target)?;
|
|
let out_bit = get_msb(value as u16, Size::Byte);
|
|
let result = value << 1;
|
|
self.set_logic_op_flags(result, out_bit, false);
|
|
self.set_target_value(target, result)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::SLL(target, opt_copy) => {
|
|
let value = self.get_target_value(target)?;
|
|
let out_bit = get_msb(value as u16, Size::Byte);
|
|
let result = (value << 1) | 0x01;
|
|
self.set_logic_op_flags(result, out_bit, false);
|
|
self.set_target_value(target, result)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::SRA(target, opt_copy) => {
|
|
let value = self.get_target_value(target)?;
|
|
let out_bit = (value & 0x01) != 0;
|
|
let msb_mask = if get_msb(value as u16, Size::Byte) { 0x80 } else { 0 };
|
|
let result = (value >> 1) | msb_mask;
|
|
self.set_logic_op_flags(result, out_bit, false);
|
|
self.set_target_value(target, result)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::SRL(target, opt_copy) => {
|
|
let value = self.get_target_value(target)?;
|
|
let out_bit = (value & 0x01) != 0;
|
|
let result = value >> 1;
|
|
self.set_logic_op_flags(result, out_bit, false);
|
|
self.set_target_value(target, result)?;
|
|
if let Some(target) = opt_copy {
|
|
self.set_target_value(target, value)?;
|
|
}
|
|
},
|
|
Instruction::SUB(target) => {
|
|
let src = self.get_target_value(target)?;
|
|
let acc = self.get_register_value(Register::A);
|
|
|
|
let (result, carry, overflow) = sub_bytes(acc, src);
|
|
self.set_arithmetic_op_flags(result as u16, Size::Byte, true, carry, overflow, (result & 0x10) != 0);
|
|
|
|
self.set_register_value(Register::A, result);
|
|
},
|
|
Instruction::XOR(target) => {
|
|
let acc = self.get_register_value(Register::A);
|
|
let value = self.get_target_value(target)?;
|
|
let result = acc ^ value;
|
|
self.set_register_value(Register::A, result);
|
|
self.set_logic_op_flags(result, false, false);
|
|
},
|
|
_ => {
|
|
return Err(Error::new(&format!("{}: unimplemented instruction: {:?}", DEV_NAME, self.decoder.instruction)));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn rotate_left(&mut self, mut value: u8, rtype: RotateType) -> (u8, bool) {
|
|
let out_bit = get_msb(value as u16, Size::Byte);
|
|
|
|
let in_bit = match rtype {
|
|
RotateType::Bit9 => self.get_flag(Flags::Carry),
|
|
RotateType::Bit8 => out_bit,
|
|
};
|
|
|
|
value <<= 1;
|
|
value |= in_bit as u8;
|
|
(value, out_bit)
|
|
}
|
|
|
|
fn rotate_right(&mut self, mut value: u8, rtype: RotateType) -> (u8, bool) {
|
|
let out_bit = (value & 0x01) != 0;
|
|
|
|
let in_bit = match rtype {
|
|
RotateType::Bit9 => self.get_flag(Flags::Carry),
|
|
RotateType::Bit8 => out_bit,
|
|
};
|
|
|
|
value >>= 1;
|
|
value |= if in_bit { 0x80 } else { 0 };
|
|
(value, out_bit)
|
|
}
|
|
|
|
fn add_to_regpair(&mut self, regpair: RegisterPair, value: i16) -> u16 {
|
|
let addr = match regpair {
|
|
RegisterPair::BC => &mut self.state.reg[0..2],
|
|
RegisterPair::DE => &mut self.state.reg[2..4],
|
|
RegisterPair::HL => &mut self.state.reg[4..6],
|
|
RegisterPair::AF => &mut self.state.reg[6..8],
|
|
_ => panic!("RegPair is not supported by inc/dec"),
|
|
};
|
|
|
|
let result = (read_beu16(addr) as i16).wrapping_add(value) as u16;
|
|
write_beu16(addr, result);
|
|
result
|
|
}
|
|
|
|
|
|
|
|
fn push_word(&mut self, value: u16) -> Result<(), Error> {
|
|
self.state.sp = self.state.sp.wrapping_sub(1);
|
|
self.port.write_u8(self.state.sp as Address, (value >> 8) as u8)?;
|
|
self.state.sp = self.state.sp.wrapping_sub(1);
|
|
self.port.write_u8(self.state.sp as Address, (value & 0x00FF) as u8)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn pop_word(&mut self) -> Result<u16, Error> {
|
|
let mut value;
|
|
value = self.port.read_u8(self.state.sp as Address)? as u16;
|
|
self.state.sp = self.state.sp.wrapping_add(1);
|
|
value |= (self.port.read_u8(self.state.sp as Address)? as u16) << 8;
|
|
self.state.sp = self.state.sp.wrapping_add(1);
|
|
Ok(value)
|
|
}
|
|
|
|
fn get_load_target_value(&mut self, target: LoadTarget) -> Result<u16, Error> {
|
|
let value = match target {
|
|
LoadTarget::DirectRegByte(reg) => self.get_register_value(reg) as u16,
|
|
LoadTarget::DirectRegHalfByte(reg) => self.get_index_register_half_value(reg) as u16,
|
|
LoadTarget::DirectRegWord(regpair) => self.get_register_pair_value(regpair),
|
|
LoadTarget::IndirectRegByte(regpair) => {
|
|
let addr = self.get_register_pair_value(regpair);
|
|
self.port.read_u8(addr as Address)? as u16
|
|
},
|
|
LoadTarget::IndirectOffsetByte(index_reg, offset) => {
|
|
let addr = self.get_index_register_value(index_reg);
|
|
self.port.read_u8(((addr as i16).wrapping_add(offset as i16)) as Address)? as u16
|
|
},
|
|
LoadTarget::IndirectRegWord(regpair) => {
|
|
let addr = self.get_register_pair_value(regpair);
|
|
self.port.read_leu16(addr as Address)?
|
|
},
|
|
LoadTarget::IndirectByte(addr) => {
|
|
self.port.read_u8(addr as Address)? as u16
|
|
},
|
|
LoadTarget::IndirectWord(addr) => {
|
|
self.port.read_leu16(addr as Address)?
|
|
},
|
|
LoadTarget::ImmediateByte(data) => data as u16,
|
|
LoadTarget::ImmediateWord(data) => data,
|
|
_ => panic!("Unsupported LoadTarget for set"),
|
|
};
|
|
Ok(value)
|
|
}
|
|
|
|
fn set_load_target_value(&mut self, target: LoadTarget, value: u16) -> Result<(), Error> {
|
|
match target {
|
|
LoadTarget::DirectRegByte(reg) => self.set_register_value(reg, value as u8),
|
|
LoadTarget::DirectRegHalfByte(reg) => self.set_index_register_half_value(reg, value as u8),
|
|
LoadTarget::DirectRegWord(regpair) => self.set_register_pair_value(regpair, value),
|
|
LoadTarget::IndirectRegByte(regpair) => {
|
|
let addr = self.get_register_pair_value(regpair);
|
|
self.port.write_u8(addr as Address, value as u8)?;
|
|
},
|
|
LoadTarget::IndirectOffsetByte(index_reg, offset) => {
|
|
let addr = self.get_index_register_value(index_reg);
|
|
self.port.write_u8(((addr as i16).wrapping_add(offset as i16)) as Address, value as u8)?;
|
|
},
|
|
LoadTarget::IndirectRegWord(regpair) => {
|
|
let addr = self.get_register_pair_value(regpair);
|
|
self.port.write_leu16(addr as Address, value)?;
|
|
},
|
|
LoadTarget::IndirectByte(addr) => {
|
|
self.port.write_u8(addr as Address, value as u8)?;
|
|
},
|
|
LoadTarget::IndirectWord(addr) => {
|
|
self.port.write_leu16(addr as Address, value)?;
|
|
},
|
|
_ => panic!("Unsupported LoadTarget for set: {:?}", target),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn get_target_value(&mut self, target: Target) -> Result<u8, Error> {
|
|
match target {
|
|
Target::DirectReg(reg) => Ok(self.get_register_value(reg)),
|
|
Target::DirectRegHalf(reg) => Ok(self.get_index_register_half_value(reg)),
|
|
Target::IndirectReg(regpair) => {
|
|
let addr = self.get_register_pair_value(regpair);
|
|
Ok(self.port.read_u8(addr as Address)?)
|
|
},
|
|
Target::IndirectOffset(reg, offset) => {
|
|
let addr = (self.get_index_register_value(reg) as i16) + (offset as i16);
|
|
Ok(self.port.read_u8(addr as Address)?)
|
|
},
|
|
Target::Immediate(data) => Ok(data),
|
|
}
|
|
}
|
|
|
|
fn set_target_value(&mut self, target: Target, value: u8) -> Result<(), Error> {
|
|
match target {
|
|
Target::DirectReg(reg) => self.set_register_value(reg, value),
|
|
Target::DirectRegHalf(reg) => self.set_index_register_half_value(reg, value),
|
|
Target::IndirectReg(regpair) => {
|
|
let addr = self.get_register_pair_value(regpair);
|
|
self.port.write_u8(addr as Address, value)?;
|
|
},
|
|
Target::IndirectOffset(reg, offset) => {
|
|
let addr = (self.get_index_register_value(reg) as i16) + (offset as i16);
|
|
self.port.write_u8(addr as Address, value)?;
|
|
},
|
|
_ => panic!("Unsupported LoadTarget for set"),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
|
|
fn get_register_value(&mut self, reg: Register) -> u8 {
|
|
self.state.reg[reg as usize]
|
|
}
|
|
|
|
fn set_register_value(&mut self, reg: Register, value: u8) {
|
|
self.state.reg[reg as usize] = value;
|
|
}
|
|
|
|
fn get_index_register_half_value(&mut self, reg: IndexRegisterHalf) -> u8 {
|
|
match reg {
|
|
IndexRegisterHalf::IXH => (self.state.ix >> 8) as u8,
|
|
IndexRegisterHalf::IXL => (self.state.ix & 0x00FF) as u8,
|
|
IndexRegisterHalf::IYH => (self.state.iy >> 8) as u8,
|
|
IndexRegisterHalf::IYL => (self.state.iy & 0x00FF) as u8,
|
|
}
|
|
}
|
|
|
|
fn set_index_register_half_value(&mut self, reg: IndexRegisterHalf, value: u8) {
|
|
match reg {
|
|
IndexRegisterHalf::IXH => { self.state.ix |= (value as u16) << 8; },
|
|
IndexRegisterHalf::IXL => { self.state.ix |= value as u16; },
|
|
IndexRegisterHalf::IYH => { self.state.iy |= (value as u16) << 8; },
|
|
IndexRegisterHalf::IYL => { self.state.iy |= value as u16; },
|
|
}
|
|
}
|
|
|
|
fn get_register_pair_value(&mut self, regpair: RegisterPair) -> u16 {
|
|
match regpair {
|
|
RegisterPair::BC => read_beu16(&self.state.reg[0..2]),
|
|
RegisterPair::DE => read_beu16(&self.state.reg[2..4]),
|
|
RegisterPair::HL => read_beu16(&self.state.reg[4..6]),
|
|
RegisterPair::AF => read_beu16(&self.state.reg[6..8]),
|
|
RegisterPair::SP => self.state.sp,
|
|
RegisterPair::IX => self.state.ix,
|
|
RegisterPair::IY => self.state.iy,
|
|
}
|
|
}
|
|
|
|
fn set_register_pair_value(&mut self, regpair: RegisterPair, value: u16) {
|
|
match regpair {
|
|
RegisterPair::BC => { write_beu16(&mut self.state.reg[0..2], value); },
|
|
RegisterPair::DE => { write_beu16(&mut self.state.reg[2..4], value); },
|
|
RegisterPair::HL => { write_beu16(&mut self.state.reg[4..6], value); },
|
|
RegisterPair::AF => { write_beu16(&mut self.state.reg[6..8], value); },
|
|
RegisterPair::SP => { self.state.sp = value; },
|
|
RegisterPair::IX => { self.state.ix = value; },
|
|
RegisterPair::IY => { self.state.iy = value; },
|
|
}
|
|
}
|
|
|
|
fn get_index_register_value(&mut self, reg: IndexRegister) -> u16 {
|
|
match reg {
|
|
IndexRegister::IX => self.state.ix,
|
|
IndexRegister::IY => self.state.iy,
|
|
}
|
|
}
|
|
|
|
fn get_current_condition(&mut self, cond: Condition) -> bool {
|
|
match cond {
|
|
Condition::NotZero => !self.get_flag(Flags::Zero),
|
|
Condition::Zero => self.get_flag(Flags::Zero),
|
|
Condition::NotCarry => !self.get_flag(Flags::Carry),
|
|
Condition::Carry => self.get_flag(Flags::Carry),
|
|
Condition::ParityOdd => !self.get_flag(Flags::Parity),
|
|
Condition::ParityEven => self.get_flag(Flags::Parity),
|
|
Condition::Positive => !self.get_flag(Flags::Sign),
|
|
Condition::Negative => self.get_flag(Flags::Sign),
|
|
}
|
|
}
|
|
|
|
fn set_numeric_flags(&mut self, value: u16, size: Size) {
|
|
let sign = if get_msb(value, size) { Flags::Sign as u8 } else { 0 };
|
|
let zero = if value == 0 { Flags::Zero as u8 } else { 0 };
|
|
self.set_flags(FLAGS_NUMERIC, sign | zero);
|
|
}
|
|
|
|
fn set_parity_flags(&mut self, value: u8) {
|
|
let mask = (Flags::Parity as u8) | (Flags::AddSubtract as u8);
|
|
let parity = if (value.count_ones() & 0x01) == 0 { Flags::Parity as u8 } else { 0 };
|
|
self.set_flags(mask, parity);
|
|
}
|
|
|
|
fn set_arithmetic_op_flags(&mut self, value: u16, size: Size, addsub: bool, carry: bool, overflow: bool, half_carry: bool) {
|
|
self.state.reg[Register::F as usize] = 0;
|
|
self.set_numeric_flags(value, size);
|
|
|
|
let addsub_flag = if addsub { Flags::AddSubtract as u8 } else { 0 };
|
|
let overflow_flag = if overflow { Flags::Parity as u8 } else { 0 };
|
|
let carry_flag = if carry { Flags::Carry as u8 } else { 0 };
|
|
let half_carry_flag = if half_carry { Flags::HalfCarry as u8 } else { 0 };
|
|
self.set_flags(FLAGS_ARITHMETIC, addsub_flag | overflow_flag | carry_flag | half_carry_flag);
|
|
}
|
|
|
|
fn set_logic_op_flags(&mut self, value: u8, carry: bool, half_carry: bool) {
|
|
self.state.reg[Register::F as usize] = 0;
|
|
self.set_numeric_flags(value as u16, Size::Byte);
|
|
self.set_parity_flags(value);
|
|
|
|
let carry_flag = if carry { Flags::Carry as u8 } else { 0 };
|
|
let half_carry_flag = if half_carry { Flags::HalfCarry as u8 } else { 0 };
|
|
self.set_flags(FLAGS_CARRY_HALF_CARRY, carry_flag | half_carry_flag);
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn get_flag(&self, flag: Flags) -> bool {
|
|
self.get_flags() & (flag as u8) != 0
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn set_flag(&mut self, flag: Flags, value: bool) {
|
|
self.state.reg[Register::F as usize] = self.state.reg[Register::F as usize] & !(flag as u8);
|
|
if value {
|
|
self.state.reg[Register::F as usize] |= flag as u8;
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn get_flags(&self) -> u8 {
|
|
self.state.reg[Register::F as usize]
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn set_flags(&mut self, mask: u8, values: u8) {
|
|
self.state.reg[Register::F as usize] = (self.state.reg[Register::F as usize] & !mask) | values;
|
|
}
|
|
}
|
|
|
|
fn add_bytes(operand1: u8, operand2: u8) -> (u8, bool, bool, bool) {
|
|
let (result, carry) = operand1.overflowing_add(operand2);
|
|
let (_, overflow) = (operand1 as i8).overflowing_add(operand2 as i8);
|
|
let half_carry = (operand1 & 0x10) != 0 && (result & 0x10) == 0;
|
|
(result, carry, overflow, half_carry)
|
|
}
|
|
|
|
fn add_words(operand1: u16, operand2: u16) -> (u16, bool, bool, bool) {
|
|
let (result, carry) = operand1.overflowing_add(operand2);
|
|
let (_, overflow) = ((operand1 as i8) as i16).overflowing_add((operand2 as i8) as i16);
|
|
let half_carry = (operand1 & 0x10) != 0 && (result & 0x10) == 0;
|
|
(result, carry, overflow, half_carry)
|
|
}
|
|
|
|
fn sub_bytes(operand1: u8, operand2: u8) -> (u8, bool, bool) {
|
|
let (result, carry) = operand1.overflowing_sub(operand2);
|
|
let (_, overflow) = (operand1 as i8).overflowing_sub(operand2 as i8);
|
|
(result, carry, overflow)
|
|
}
|
|
|
|
fn sub_words(operand1: u16, operand2: u16) -> (u16, bool, bool) {
|
|
let (result, carry) = operand1.overflowing_sub(operand2);
|
|
let (_, overflow) = ((operand1 as i8) as i16).overflowing_sub((operand2 as i8) as i16);
|
|
(result, carry, overflow)
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn get_msb(value: u16, size: Size) -> bool {
|
|
match size {
|
|
Size::Byte => (value & 0x0080) != 0,
|
|
Size::Word => (value & 0x8000) != 0,
|
|
}
|
|
}
|
|
|