mirror of
https://github.com/transistorfet/moa.git
synced 2024-06-25 18:29:33 +00:00
Fixed decode and excute issues with Z80
This commit is contained in:
parent
0836b6de8b
commit
f6105de939
|
@ -65,7 +65,7 @@ impl Debuggable for M68k {
|
|||
|
||||
fn print_disassembly(&mut self, addr: Address, count: usize) {
|
||||
let mut decoder = M68kDecoder::new(self.cputype, 0);
|
||||
decoder.dump_disassembly(&mut self.port, self.state.pc, 0x1000);
|
||||
decoder.dump_disassembly(&mut self.port, addr as u32, count as u32);
|
||||
}
|
||||
|
||||
fn execute_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error> {
|
||||
|
|
|
@ -39,10 +39,17 @@ impl Debuggable for Z80 {
|
|||
|
||||
fn print_disassembly(&mut self, addr: Address, count: usize) {
|
||||
let mut decoder = Z80Decoder::new();
|
||||
//decoder.dump_disassembly(&mut self.port, self.state.pc, 0x1000);
|
||||
decoder.dump_disassembly(&mut self.port, addr as u16, count as u16);
|
||||
}
|
||||
|
||||
fn execute_command(&mut self, system: &System, args: &[&str]) -> Result<bool, Error> {
|
||||
match args[0] {
|
||||
"l" => {
|
||||
use super::state::Register;
|
||||
self.state.reg[Register::L as usize] = 0x05
|
||||
},
|
||||
_ => { return Ok(true); },
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,12 @@ use crate::devices::{Address, Addressable};
|
|||
use super::state::{Z80, Z80Type, Register};
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Direction {
|
||||
ToAcc,
|
||||
FromAcc,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Size {
|
||||
Byte,
|
||||
|
@ -72,14 +78,13 @@ pub enum LoadTarget {
|
|||
IndirectRegWord(RegisterPair),
|
||||
IndirectOffsetByte(IndexRegister, i8),
|
||||
DirectAltRegByte(Register),
|
||||
DirectSpecialRegByte(SpecialRegister),
|
||||
IndirectByte(u16),
|
||||
IndirectWord(u16),
|
||||
ImmediateByte(u8),
|
||||
ImmediateWord(u16),
|
||||
}
|
||||
|
||||
pub type OptionalSource = Option<(IndexRegister, i8)>;
|
||||
pub type UndocumentedCopy = Option<Target>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Instruction {
|
||||
|
@ -124,6 +129,7 @@ pub enum Instruction {
|
|||
JR(i8),
|
||||
JRcc(Condition, i8),
|
||||
LD(LoadTarget, LoadTarget),
|
||||
LDsr(SpecialRegister, Direction),
|
||||
LDD,
|
||||
LDDR,
|
||||
LDI,
|
||||
|
@ -139,30 +145,30 @@ pub enum Instruction {
|
|||
OUTx(u8),
|
||||
POP(RegisterPair),
|
||||
PUSH(RegisterPair),
|
||||
RES(u8, Target, OptionalSource),
|
||||
RES(u8, Target, UndocumentedCopy),
|
||||
RET,
|
||||
RETI,
|
||||
RETN,
|
||||
RETcc(Condition),
|
||||
RL(Target, OptionalSource),
|
||||
RL(Target, UndocumentedCopy),
|
||||
RLA,
|
||||
RLC(Target, OptionalSource),
|
||||
RLC(Target, UndocumentedCopy),
|
||||
RLCA,
|
||||
RLD,
|
||||
RR(Target, OptionalSource),
|
||||
RR(Target, UndocumentedCopy),
|
||||
RRA,
|
||||
RRC(Target, OptionalSource),
|
||||
RRC(Target, UndocumentedCopy),
|
||||
RRCA,
|
||||
RRD,
|
||||
RST(u8),
|
||||
SBCa(Target),
|
||||
SBC16(RegisterPair, RegisterPair),
|
||||
SCF,
|
||||
SET(u8, Target, OptionalSource),
|
||||
SLA(Target, OptionalSource),
|
||||
SLL(Target, OptionalSource),
|
||||
SRA(Target, OptionalSource),
|
||||
SRL(Target, OptionalSource),
|
||||
SET(u8, Target, UndocumentedCopy),
|
||||
SLA(Target, UndocumentedCopy),
|
||||
SLL(Target, UndocumentedCopy),
|
||||
SRA(Target, UndocumentedCopy),
|
||||
SRL(Target, UndocumentedCopy),
|
||||
SUB(Target),
|
||||
XOR(Target),
|
||||
}
|
||||
|
@ -380,25 +386,18 @@ impl Z80Decoder {
|
|||
}
|
||||
|
||||
pub fn decode_sub_prefix_cb(&mut self, memory: &mut dyn Addressable, reg: IndexRegister) -> Result<Instruction, Error> {
|
||||
let offset = self.read_instruction_byte(memory)? as i8;
|
||||
let ins = self.read_instruction_byte(memory)?;
|
||||
let opt_copy = match get_ins_z(ins) {
|
||||
6 => None, //Some(Target::DirectReg(Register::F)),
|
||||
z => Some(get_register(z)),
|
||||
};
|
||||
|
||||
match get_ins_x(ins) {
|
||||
0 => {
|
||||
// TODO you need to fix the case where (HL) is specified...
|
||||
let opt_src = Some((reg, self.read_instruction_byte(memory)? as i8));
|
||||
Ok(get_rot_instruction(get_ins_y(ins), get_register(get_ins_z(ins)), opt_src))
|
||||
},
|
||||
1 => {
|
||||
let offset = self.read_instruction_byte(memory)? as i8;
|
||||
Ok(Instruction::BIT(get_ins_y(ins), Target::IndirectOffset(reg, offset)))
|
||||
},
|
||||
2 => {
|
||||
let opt_src = Some((reg, self.read_instruction_byte(memory)? as i8));
|
||||
Ok(Instruction::RES(get_ins_y(ins), get_register(get_ins_z(ins)), opt_src))
|
||||
},
|
||||
3 => {
|
||||
let opt_src = Some((reg, self.read_instruction_byte(memory)? as i8));
|
||||
Ok(Instruction::SET(get_ins_y(ins), get_register(get_ins_z(ins)), opt_src))
|
||||
},
|
||||
0 => Ok(get_rot_instruction(get_ins_y(ins), Target::IndirectOffset(reg, offset), opt_copy)),
|
||||
1 => Ok(Instruction::BIT(get_ins_y(ins), Target::IndirectOffset(reg, offset))),
|
||||
2 => Ok(Instruction::RES(get_ins_y(ins), Target::IndirectOffset(reg, offset), opt_copy)),
|
||||
3 => Ok(Instruction::SET(get_ins_y(ins), Target::IndirectOffset(reg, offset), opt_copy)),
|
||||
_ => panic!("InternalError: impossible value"),
|
||||
}
|
||||
}
|
||||
|
@ -458,10 +457,10 @@ impl Z80Decoder {
|
|||
},
|
||||
7 => {
|
||||
match get_ins_y(ins) {
|
||||
0 => Ok(Instruction::LD(LoadTarget::DirectSpecialRegByte(SpecialRegister::I), LoadTarget::DirectRegByte(Register::A))),
|
||||
1 => Ok(Instruction::LD(LoadTarget::DirectSpecialRegByte(SpecialRegister::R), LoadTarget::DirectRegByte(Register::A))),
|
||||
2 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), LoadTarget::DirectSpecialRegByte(SpecialRegister::I))),
|
||||
3 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), LoadTarget::DirectSpecialRegByte(SpecialRegister::R))),
|
||||
0 => Ok(Instruction::LDsr(SpecialRegister::I, Direction::FromAcc)),
|
||||
1 => Ok(Instruction::LDsr(SpecialRegister::R, Direction::FromAcc)),
|
||||
2 => Ok(Instruction::LDsr(SpecialRegister::I, Direction::ToAcc)),
|
||||
3 => Ok(Instruction::LDsr(SpecialRegister::R, Direction::ToAcc)),
|
||||
4 => Ok(Instruction::RRD),
|
||||
5 => Ok(Instruction::RLD),
|
||||
_ => Ok(Instruction::NOP),
|
||||
|
@ -690,6 +689,22 @@ impl Z80Decoder {
|
|||
let ins_data = self.format_instruction_bytes(memory);
|
||||
println!("{:#06x}: {}\n\t{:?}\n", self.start, ins_data, self.instruction);
|
||||
}
|
||||
|
||||
pub fn dump_disassembly(&mut self, memory: &mut dyn Addressable, start: u16, length: u16) {
|
||||
let mut next = start;
|
||||
while next < (start + length) {
|
||||
match self.decode_at(memory, next) {
|
||||
Ok(()) => {
|
||||
self.dump_decoded(memory);
|
||||
next = self.end;
|
||||
},
|
||||
Err(err) => {
|
||||
println!("{:?}", err);
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_alu_instruction(alu: u8, target: Target) -> Instruction {
|
||||
|
@ -706,16 +721,16 @@ fn get_alu_instruction(alu: u8, target: Target) -> Instruction {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_rot_instruction(rot: u8, target: Target, opt_src: OptionalSource) -> Instruction {
|
||||
fn get_rot_instruction(rot: u8, target: Target, opt_copy: UndocumentedCopy) -> Instruction {
|
||||
match rot {
|
||||
0 => Instruction::RLC(target, opt_src),
|
||||
1 => Instruction::RRC(target, opt_src),
|
||||
2 => Instruction::RL(target, opt_src),
|
||||
3 => Instruction::RR(target, opt_src),
|
||||
4 => Instruction::SLA(target, opt_src),
|
||||
5 => Instruction::SRA(target, opt_src),
|
||||
6 => Instruction::SLL(target, opt_src),
|
||||
7 => Instruction::SRL(target, opt_src),
|
||||
0 => Instruction::RLC(target, opt_copy),
|
||||
1 => Instruction::RRC(target, opt_copy),
|
||||
2 => Instruction::RL(target, opt_copy),
|
||||
3 => Instruction::RR(target, opt_copy),
|
||||
4 => Instruction::SLA(target, opt_copy),
|
||||
5 => Instruction::SRA(target, opt_copy),
|
||||
6 => Instruction::SLL(target, opt_copy),
|
||||
7 => Instruction::SRL(target, opt_copy),
|
||||
_ => panic!("InternalError: impossible value"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::system::System;
|
|||
use crate::error::{ErrorType, Error};
|
||||
use crate::devices::{ClockElapsed, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16};
|
||||
|
||||
use super::decode::{Condition, Instruction, LoadTarget, Target, OptionalSource, RegisterPair, IndexRegister, IndexRegisterHalf, Size};
|
||||
use super::decode::{Condition, Instruction, LoadTarget, Target, RegisterPair, IndexRegister, IndexRegisterHalf, Size, Direction, UndocumentedCopy};
|
||||
use super::state::{Z80, Status, Flags, Register};
|
||||
|
||||
|
||||
|
@ -273,6 +273,7 @@ impl Z80 {
|
|||
let value = self.get_target_value(target)?;
|
||||
|
||||
let (result, carry, 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)?;
|
||||
|
@ -312,11 +313,10 @@ impl Z80 {
|
|||
},
|
||||
Instruction::LD(dest, src) => {
|
||||
let src_value = self.get_load_target_value(src)?;
|
||||
if let LoadTarget::DirectSpecialRegByte(_) = src {
|
||||
self.set_logic_op_flags(src_value as u8, self.get_flag(Flags::Carry), false);
|
||||
}
|
||||
self.set_load_target_value(dest, src_value)?;
|
||||
},
|
||||
//Instruction::LDsr(special_reg, dir) => {
|
||||
//}
|
||||
//Instruction::LDD => {
|
||||
//},
|
||||
//Instruction::LDDR => {
|
||||
|
@ -364,7 +364,7 @@ impl Z80 {
|
|||
//},
|
||||
Instruction::OUTx(port) => {
|
||||
// TODO this needs to be fixed
|
||||
println!("OUT ({:x}), {:x}", port, self.state.reg[Register::A as usize]);
|
||||
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()?;
|
||||
|
@ -374,10 +374,13 @@ impl Z80 {
|
|||
let value = self.get_register_pair_value(regpair);
|
||||
self.push_word(value)?;
|
||||
},
|
||||
Instruction::RES(bit, target, opt_src) => {
|
||||
let mut value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
Instruction::RES(bit, target, opt_copy) => {
|
||||
let mut value = self.get_target_value(target)?;
|
||||
value = value & !(1 << bit);
|
||||
self.set_target_value(target, value);
|
||||
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()?;
|
||||
|
@ -391,11 +394,14 @@ impl Z80 {
|
|||
self.state.pc = self.pop_word()?;
|
||||
}
|
||||
},
|
||||
Instruction::RL(target, opt_src) => {
|
||||
let value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
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);
|
||||
|
@ -403,11 +409,14 @@ impl Z80 {
|
|||
self.set_logic_op_flags(result, out_bit, false);
|
||||
self.set_register_value(Register::A, result);
|
||||
},
|
||||
Instruction::RLC(target, opt_src) => {
|
||||
let value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
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);
|
||||
|
@ -417,11 +426,14 @@ impl Z80 {
|
|||
},
|
||||
//Instruction::RLD => {
|
||||
//},
|
||||
Instruction::RR(target, opt_src) => {
|
||||
let value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
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);
|
||||
|
@ -429,11 +441,14 @@ impl Z80 {
|
|||
self.set_logic_op_flags(result, out_bit, false);
|
||||
self.set_register_value(Register::A, result);
|
||||
},
|
||||
Instruction::RRC(target, opt_src) => {
|
||||
let value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
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);
|
||||
|
@ -472,39 +487,54 @@ impl Z80 {
|
|||
self.set_flag(Flags::HalfCarry, false);
|
||||
self.set_flag(Flags::Carry, true);
|
||||
},
|
||||
Instruction::SET(bit, target, opt_src) => {
|
||||
let mut value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
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_src) => {
|
||||
let value = self.get_opt_src_target_value(opt_src, 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)?;
|
||||
},
|
||||
Instruction::SLL(target, opt_src) => {
|
||||
let value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
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::SRA(target, opt_src) => {
|
||||
let value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
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;
|
||||
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_src) => {
|
||||
let value = self.get_opt_src_target_value(opt_src, target)?;
|
||||
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)?;
|
||||
|
@ -675,21 +705,15 @@ impl Z80 {
|
|||
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_opt_src_target_value(&mut self, opt_src: OptionalSource, target: Target) -> Result<u8, Error> {
|
||||
match opt_src {
|
||||
None => self.get_target_value(target),
|
||||
Some((reg, offset)) => {
|
||||
let addr = (self.get_index_register_value(reg) as i16) + (offset as i16);
|
||||
self.port.read_u8(addr as Address)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn get_register_value(&mut self, reg: Register) -> u8 {
|
||||
self.state.reg[reg as usize]
|
||||
|
|
|
@ -127,10 +127,10 @@ impl Z80 {
|
|||
println!("IX: {:#06x}", self.state.ix);
|
||||
println!("IY: {:#06x}", self.state.iy);
|
||||
|
||||
println!("A: {:#04x} F: {:#04x}", self.state.reg[Register::A as usize], self.state.reg[Register::F as usize]);
|
||||
println!("B: {:#04x} C: {:#04x}", self.state.reg[Register::B as usize], self.state.reg[Register::C as usize]);
|
||||
println!("D: {:#04x} E: {:#04x}", self.state.reg[Register::D as usize], self.state.reg[Register::E as usize]);
|
||||
println!("H: {:#04x} L: {:#04x}", self.state.reg[Register::H as usize], self.state.reg[Register::L as usize]);
|
||||
println!("A: {:#04x} F: {:#04x} A': {:#04x} F': {:#04x}", self.state.reg[Register::A as usize], self.state.reg[Register::F as usize], self.state.shadow_reg[Register::A as usize], self.state.shadow_reg[Register::F as usize]);
|
||||
println!("B: {:#04x} C: {:#04x} B': {:#04x} C': {:#04x}", self.state.reg[Register::B as usize], self.state.reg[Register::C as usize], self.state.shadow_reg[Register::B as usize], self.state.shadow_reg[Register::C as usize]);
|
||||
println!("D: {:#04x} E: {:#04x} D': {:#04x} E': {:#04x}", self.state.reg[Register::D as usize], self.state.reg[Register::E as usize], self.state.shadow_reg[Register::D as usize], self.state.shadow_reg[Register::E as usize]);
|
||||
println!("H: {:#04x} L: {:#04x} H': {:#04x} L': {:#04x}", self.state.reg[Register::H as usize], self.state.reg[Register::L as usize], self.state.shadow_reg[Register::H as usize], self.state.shadow_reg[Register::L as usize]);
|
||||
|
||||
println!("Current Instruction: {} {:?}", self.decoder.format_instruction_bytes(&mut self.port), self.decoder.instruction);
|
||||
println!("");
|
||||
|
|
|
@ -73,44 +73,6 @@ mod decode_tests {
|
|||
(&[0xDD, 0x84], Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXH))),
|
||||
(&[0xDD, 0x85], Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXL))),
|
||||
];
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn decode_add_ix_bc() {
|
||||
let instruction = run_decode_test(&[0xDD, 0x09]);
|
||||
assert_eq!(instruction, Instruction::ADD16(RegisterPair::IX, RegisterPair::BC));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_ld_b_ixh() {
|
||||
let instruction = run_decode_test(&[0xDD, 0x44]);
|
||||
assert_eq!(instruction, Instruction::LD(LoadTarget::DirectRegByte(Register::B), LoadTarget::DirectRegHalfByte(IndexRegisterHalf::IXH)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_ld_h_ix_offset() {
|
||||
let instruction = run_decode_test(&[0xDD, 0x66, 0x12]);
|
||||
assert_eq!(instruction, Instruction::LD(LoadTarget::DirectRegByte(Register::H), LoadTarget::IndirectOffsetByte(IndexRegister::IX, 0x12)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_ld_l_ix_offset() {
|
||||
let instruction = run_decode_test(&[0xDD, 0x6E, 0x12]);
|
||||
assert_eq!(instruction, Instruction::LD(LoadTarget::DirectRegByte(Register::L), LoadTarget::IndirectOffsetByte(IndexRegister::IX, 0x12)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_add_ixh() {
|
||||
let instruction = run_decode_test(&[0xDD, 0x84]);
|
||||
assert_eq!(instruction, Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXH)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_add_ixl() {
|
||||
let instruction = run_decode_test(&[0xDD, 0x85]);
|
||||
assert_eq!(instruction, Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXL)));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -124,6 +86,216 @@ mod execute_tests {
|
|||
use super::super::state::{Z80State, Register};
|
||||
use super::super::decode::{Instruction, LoadTarget, Target, RegisterPair, IndexRegister, IndexRegisterHalf, Condition};
|
||||
|
||||
struct TestState {
|
||||
pc: u16,
|
||||
sp: u16,
|
||||
ix: u16,
|
||||
iy: u16,
|
||||
bc: u16,
|
||||
de: u16,
|
||||
hl: u16,
|
||||
af: u16,
|
||||
}
|
||||
|
||||
struct TestCase {
|
||||
name: &'static str,
|
||||
ins: Instruction,
|
||||
data: &'static [u8],
|
||||
init: TestState,
|
||||
fini: TestState,
|
||||
}
|
||||
|
||||
const TEST_CASES: &'static [TestCase] = &[
|
||||
/*
|
||||
TestCase {
|
||||
name: ,
|
||||
ins: ,
|
||||
data: &[ 0x88 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0000 },
|
||||
fini: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0000 },
|
||||
},
|
||||
*/
|
||||
|
||||
TestCase {
|
||||
name: "adc with no carry",
|
||||
ins: Instruction::ADCa(Target::DirectReg(Register::B)),
|
||||
data: &[ 0x88 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0100, de: 0x0000, hl: 0x0000, af: 0xFE00 },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0100, de: 0x0000, hl: 0x0000, af: 0xFF90 },
|
||||
},
|
||||
TestCase {
|
||||
name: "adc with carry already set",
|
||||
ins: Instruction::ADCa(Target::DirectReg(Register::B)),
|
||||
data: &[ 0x88 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0xFE01 },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0xFF90 },
|
||||
},
|
||||
TestCase {
|
||||
name: "adc with carry already set while causing a carry",
|
||||
ins: Instruction::ADCa(Target::DirectReg(Register::B)),
|
||||
data: &[ 0x88 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0100, de: 0x0000, hl: 0x0000, af: 0xFE01 },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0100, de: 0x0000, hl: 0x0000, af: 0x0041 },
|
||||
},
|
||||
TestCase {
|
||||
name: "adc16 with bc",
|
||||
ins: Instruction::ADC16(RegisterPair::HL, RegisterPair::BC),
|
||||
data: &[ 0xED, 0x4A ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x1010, de: 0x0000, hl: 0x8080, af: 0x0000 },
|
||||
fini: TestState { pc: 0x0002, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x1010, de: 0x0000, hl: 0x9090, af: 0x0090 },
|
||||
},
|
||||
TestCase {
|
||||
name: "add a with h",
|
||||
ins: Instruction::ADDa(Target::DirectReg(Register::H)),
|
||||
data: &[ 0x84 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x2200, af: 0x1000 },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x2200, af: 0x3210 },
|
||||
},
|
||||
TestCase {
|
||||
name: "add a with h with overflow",
|
||||
ins: Instruction::ADDa(Target::DirectReg(Register::H)),
|
||||
data: &[ 0x84 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0100, af: 0x7F00 },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0100, af: 0x8084 },
|
||||
},
|
||||
TestCase {
|
||||
name: "add hl and bc",
|
||||
ins: Instruction::ADD16(RegisterPair::HL, RegisterPair::BC),
|
||||
data: &[ 0x09 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x1080, de: 0x0000, hl: 0x0080, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x1080, de: 0x0000, hl: 0x1100, af: 0x00FC },
|
||||
},
|
||||
TestCase {
|
||||
name: "and with c",
|
||||
ins: Instruction::AND(Target::DirectReg(Register::C)),
|
||||
data: &[ 0xA1 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x00F0, de: 0x0000, hl: 0x0000, af: 0x55FF },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x00F0, de: 0x0000, hl: 0x0000, af: 0x5014 },
|
||||
},
|
||||
TestCase {
|
||||
name: "bit 3, c",
|
||||
ins: Instruction::BIT(3, Target::DirectReg(Register::C)),
|
||||
data: &[ 0xCB, 0x59 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x000F, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0002, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x000F, de: 0x0000, hl: 0x0000, af: 0x00BD },
|
||||
},
|
||||
TestCase {
|
||||
name: "call",
|
||||
ins: Instruction::CALL(0x1234),
|
||||
data: &[ 0xCD, 0x34, 0x12 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0000 },
|
||||
fini: TestState { pc: 0x1234, sp: 0xFFFE, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0000 },
|
||||
},
|
||||
TestCase {
|
||||
name: "call cc true",
|
||||
ins: Instruction::CALLcc(Condition::Zero, 0x1234),
|
||||
data: &[ 0xCC, 0x34, 0x12 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
fini: TestState { pc: 0x1234, sp: 0xFFFE, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
},
|
||||
TestCase {
|
||||
name: "call cc false",
|
||||
ins: Instruction::CALLcc(Condition::Zero, 0x1234),
|
||||
data: &[ 0xCC, 0x34, 0x12 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0000 },
|
||||
fini: TestState { pc: 0x0003, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0000 },
|
||||
},
|
||||
TestCase {
|
||||
name: "ccf",
|
||||
ins: Instruction::CCF,
|
||||
data: &[ 0x3F ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FC },
|
||||
},
|
||||
TestCase {
|
||||
name: "ccf invert",
|
||||
ins: Instruction::CCF,
|
||||
data: &[ 0x3F ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0000 },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0001 },
|
||||
},
|
||||
TestCase {
|
||||
name: "cp c where not equal",
|
||||
ins: Instruction::CP(Target::DirectReg(Register::C)),
|
||||
data: &[ 0xB9 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x00F0, de: 0x0000, hl: 0x0000, af: 0x55FF },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x00F0, de: 0x0000, hl: 0x0000, af: 0x5503 },
|
||||
},
|
||||
TestCase {
|
||||
name: "cp c where not equal",
|
||||
ins: Instruction::CP(Target::DirectReg(Register::C)),
|
||||
data: &[ 0xB9 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0055, de: 0x0000, hl: 0x0000, af: 0x55FF },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0055, de: 0x0000, hl: 0x0000, af: 0x5542 },
|
||||
},
|
||||
TestCase {
|
||||
name: "cpl",
|
||||
ins: Instruction::CPL,
|
||||
data: &[ 0x2F ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x5500 },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0xAA12 },
|
||||
},
|
||||
TestCase {
|
||||
name: "dec hl",
|
||||
ins: Instruction::DEC16(RegisterPair::HL),
|
||||
data: &[ 0x2B ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0xFFFF, af: 0x00FF },
|
||||
},
|
||||
TestCase {
|
||||
name: "dec8",
|
||||
ins: Instruction::DEC8(Target::DirectReg(Register::C)),
|
||||
data: &[ 0x0D ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x0000 },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x00ff, de: 0x0000, hl: 0x0000, af: 0x0092 },
|
||||
},
|
||||
TestCase {
|
||||
name: "djnz with jump",
|
||||
ins: Instruction::DJNZ(0x10),
|
||||
data: &[ 0x10, 0x10 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0012, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0xff00, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
},
|
||||
TestCase {
|
||||
name: "djnz without jump",
|
||||
ins: Instruction::DJNZ(0x10),
|
||||
data: &[ 0x10, 0x10 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0100, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0002, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
},
|
||||
TestCase {
|
||||
name: "ex de, hl",
|
||||
ins: Instruction::EXhlde,
|
||||
data: &[ 0xEB ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x55AA, hl: 0x1234, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x1234, hl: 0x55AA, af: 0x00FF },
|
||||
},
|
||||
//TestCase {
|
||||
// name: "ex sp location",
|
||||
// ins: Instruction::EXhlde,
|
||||
// data: &[ 0xEB ],
|
||||
// init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x55AA, hl: 0x1234, af: 0x00FF },
|
||||
// fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x1234, hl: 0x55AA, af: 0x00FF },
|
||||
//},
|
||||
|
||||
TestCase {
|
||||
name: "inc ix",
|
||||
ins: Instruction::INC16(RegisterPair::IX),
|
||||
data: &[ 0xDD, 0x23 ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0002, sp: 0x0000, ix: 0x0001, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
},
|
||||
TestCase {
|
||||
name: "inc c",
|
||||
ins: Instruction::INC8(Target::DirectReg(Register::C)),
|
||||
data: &[ 0x0C ],
|
||||
init: TestState { pc: 0x0000, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0000, de: 0x0000, hl: 0x0000, af: 0x00FF },
|
||||
fini: TestState { pc: 0x0001, sp: 0x0000, ix: 0x0000, iy: 0x0000, bc: 0x0001, de: 0x0000, hl: 0x0000, af: 0x0001 },
|
||||
},
|
||||
|
||||
|
||||
];
|
||||
|
||||
fn init_execute_test() -> (Z80, System) {
|
||||
let mut system = System::new();
|
||||
|
||||
|
@ -139,239 +311,51 @@ mod execute_tests {
|
|||
(cpu, system)
|
||||
}
|
||||
|
||||
fn run_execute_test(init_state: Z80State, expected_state: Z80State, instruction: Instruction) {
|
||||
fn build_state(state: &TestState) -> Z80State {
|
||||
let mut new_state = Z80State::new();
|
||||
new_state.pc = state.pc;
|
||||
new_state.sp = state.sp;
|
||||
new_state.ix = state.ix;
|
||||
new_state.iy = state.iy;
|
||||
new_state.set_register(Register::B, (state.bc >> 8) as u8);
|
||||
new_state.set_register(Register::C, state.bc as u8);
|
||||
new_state.set_register(Register::D, (state.de >> 8) as u8);
|
||||
new_state.set_register(Register::E, state.de as u8);
|
||||
new_state.set_register(Register::H, (state.hl >> 8) as u8);
|
||||
new_state.set_register(Register::L, state.hl as u8);
|
||||
new_state.set_register(Register::A, (state.af >> 8) as u8);
|
||||
new_state.set_register(Register::F, state.af as u8);
|
||||
new_state
|
||||
}
|
||||
|
||||
fn load_memory(system: &System, data: &[u8]) {
|
||||
for i in 0..data.len() {
|
||||
system.get_bus().write_u8(i as Address, data[i]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test(case: &TestCase) {
|
||||
let (mut cpu, system) = init_execute_test();
|
||||
|
||||
let init_state = build_state(&case.init);
|
||||
let expected_state = build_state(&case.fini);
|
||||
|
||||
load_memory(&system, case.data);
|
||||
cpu.state = init_state;
|
||||
cpu.decoder.instruction = instruction;
|
||||
|
||||
cpu.decode_next(&system).unwrap();
|
||||
assert_eq!(cpu.decoder.instruction, case.ins);
|
||||
|
||||
cpu.execute_current(&system).unwrap();
|
||||
assert_eq!(cpu.state, expected_state);
|
||||
}
|
||||
|
||||
|
||||
/////////////////////
|
||||
// Execution Tests //
|
||||
/////////////////////
|
||||
|
||||
|
||||
#[test]
|
||||
fn execute_adca_b_carry_clear() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0xFE);
|
||||
init_state.set_register(Register::B, 0x01);
|
||||
init_state.set_register(Register::F, 0x00);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::A, 0xFF);
|
||||
expected_state.set_register(Register::F, 0x90);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::ADCa(Target::DirectReg(Register::B)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_adca_b_carry_set() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0xFE);
|
||||
init_state.set_register(Register::B, 0x00);
|
||||
init_state.set_register(Register::F, 0x01);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::A, 0xFF);
|
||||
expected_state.set_register(Register::F, 0x90);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::ADCa(Target::DirectReg(Register::B)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_adca_b_carry_set_with_carry() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0xFE);
|
||||
init_state.set_register(Register::B, 0x01);
|
||||
init_state.set_register(Register::F, 0x01);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::A, 0x00);
|
||||
expected_state.set_register(Register::F, 0x41);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::ADCa(Target::DirectReg(Register::B)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_adc16_ixl() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::H, 0x80);
|
||||
init_state.set_register(Register::L, 0x80);
|
||||
init_state.ix = 0x1010;
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::H, 0x90);
|
||||
expected_state.set_register(Register::L, 0x90);
|
||||
expected_state.set_register(Register::F, 0x90);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::ADC16(RegisterPair::HL, RegisterPair::IX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_adda_h() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0x10);
|
||||
init_state.set_register(Register::H, 0x22);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::A, 0x32);
|
||||
expected_state.set_register(Register::F, 0x10);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::ADDa(Target::DirectReg(Register::H)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_adda_h_with_overflow() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0x7F);
|
||||
init_state.set_register(Register::H, 0x01);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::A, 0x80);
|
||||
expected_state.set_register(Register::F, 0x84);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::ADDa(Target::DirectReg(Register::H)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_add16_ixl() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.ix = 0x1080;
|
||||
init_state.set_register(Register::H, 0x00);
|
||||
init_state.set_register(Register::L, 0x80);
|
||||
init_state.set_register(Register::F, 0xFF); // S and Z flags should not be affected
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::H, 0x11);
|
||||
expected_state.set_register(Register::L, 0x00);
|
||||
expected_state.set_register(Register::F, 0xFC);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::ADD16(RegisterPair::HL, RegisterPair::IX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_and_c() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0x55);
|
||||
init_state.set_register(Register::C, 0xF0);
|
||||
init_state.set_register(Register::F, 0xFF);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::A, 0x50);
|
||||
expected_state.set_register(Register::F, 0x14);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::AND(Target::DirectReg(Register::C)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_bit() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::C, 0x0F);
|
||||
init_state.set_register(Register::F, 0xFF);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::F, 0xBD);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::BIT(3, Target::DirectReg(Register::C)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_call() {
|
||||
let mut init_state = Z80State::new();
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.pc = 0x1234;
|
||||
expected_state.sp = 0xFFFE;
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::CALL(0x1234));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_call_cc_true() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::F, 0xFF);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.pc = 0x1234;
|
||||
expected_state.sp = 0xFFFE;
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::CALLcc(Condition::Zero, 0x1234));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_call_cc_false() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::F, 0xFF);
|
||||
|
||||
let expected_state = init_state.clone();
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::CALLcc(Condition::NotZero, 0x1234));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_ccf() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::F, 0xFF);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::F, 0xFC);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::CCF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_ccf_invert() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::F, 0x00);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::F, 0x01);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::CCF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_cp_c_diff() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0x55);
|
||||
init_state.set_register(Register::C, 0xF0);
|
||||
init_state.set_register(Register::F, 0xFF);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::F, 0x03);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::CP(Target::DirectReg(Register::C)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_cp_c_equal() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0x55);
|
||||
init_state.set_register(Register::C, 0x55);
|
||||
init_state.set_register(Register::F, 0xFF);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::F, 0x42);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::CP(Target::DirectReg(Register::C)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_cpl() {
|
||||
let mut init_state = Z80State::new();
|
||||
init_state.set_register(Register::A, 0x55);
|
||||
init_state.set_register(Register::F, 0x00);
|
||||
|
||||
let mut expected_state = init_state.clone();
|
||||
expected_state.set_register(Register::A, 0xAA);
|
||||
expected_state.set_register(Register::F, 0x12);
|
||||
|
||||
run_execute_test(init_state, expected_state, Instruction::CPL);
|
||||
pub fn run_execute_tests() {
|
||||
for case in TEST_CASES {
|
||||
println!("Running test {}", case.name);
|
||||
run_test(case);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -115,7 +115,19 @@ impl Debugger {
|
|||
}
|
||||
},
|
||||
"dis" | "disassemble" => {
|
||||
debug_obj.print_disassembly(0, 0);
|
||||
let addr = if args.len() > 1 {
|
||||
Address::from_str_radix(args[1], 16).map_err(|_| Error::new("Unable to parse address"))?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let count = if args.len() > 2 {
|
||||
usize::from_str_radix(args[2], 16).map_err(|_| Error::new("Unable to parse address"))?
|
||||
} else {
|
||||
0x1000
|
||||
};
|
||||
|
||||
debug_obj.print_disassembly(addr, count);
|
||||
},
|
||||
"c" | "continue" => {
|
||||
self.check_repeat_arg(args)?;
|
||||
|
|
|
@ -8,7 +8,6 @@ use crate::cpus::z80::{Z80, Z80Type};
|
|||
use crate::peripherals::trs80;
|
||||
|
||||
use crate::host::traits::Host;
|
||||
use crate::host::tty::SimplePty;
|
||||
|
||||
|
||||
pub fn build_trs80<H: Host>(host: &mut H) -> Result<System, Error> {
|
||||
|
@ -16,11 +15,11 @@ pub fn build_trs80<H: Host>(host: &mut H) -> Result<System, Error> {
|
|||
|
||||
let mut rom = MemoryBlock::new(vec![0; 0x4000]);
|
||||
rom.load_at(0x0000, "binaries/trs80/level1.rom")?;
|
||||
rom.load_at(0x1000, "binaries/trs80/level2.rom")?;
|
||||
//rom.load_at(0x0000, "binaries/trs80/level2.rom")?;
|
||||
rom.read_only();
|
||||
system.add_addressable_device(0x0000, wrap_transmutable(rom))?;
|
||||
|
||||
let mut ram = MemoryBlock::new(vec![0; 0xC000]);
|
||||
let ram = MemoryBlock::new(vec![0; 0xC000]);
|
||||
system.add_addressable_device(0x4000, wrap_transmutable(ram))?;
|
||||
|
||||
let model1 = trs80::model1::Model1Peripherals::create(host)?;
|
||||
|
@ -33,6 +32,24 @@ pub fn build_trs80<H: Host>(host: &mut H) -> Result<System, Error> {
|
|||
//cpu.add_breakpoint(0x1e5);
|
||||
//cpu.add_breakpoint(0x340); // "exec", the function that executes the line typed in
|
||||
//cpu.add_breakpoint(0x357);
|
||||
//cpu.add_breakpoint(0x401); // LIST command exec
|
||||
//cpu.add_breakpoint(0x10); // putchar
|
||||
//cpu.add_breakpoint(0x970);
|
||||
//cpu.add_breakpoint(0x9f9);
|
||||
//cpu.add_breakpoint(0xa58); // return from printing the line number
|
||||
//cpu.add_breakpoint(0xc59); // the function called first thing when printing a decimal number
|
||||
//cpu.add_breakpoint(0xe00); // normalize the float??
|
||||
//cpu.add_breakpoint(0x970); // just after the decimal number print function is called, but after the call at the start is complete
|
||||
//cpu.add_breakpoint(0xa6c);
|
||||
|
||||
//cpu.add_breakpoint(0xe00);
|
||||
//cpu.add_breakpoint(0xc77);
|
||||
//cpu.add_breakpoint(0xc83);
|
||||
//cpu.add_breakpoint(0x96d);
|
||||
cpu.add_breakpoint(0x970);
|
||||
cpu.add_breakpoint(0x9e2);
|
||||
cpu.add_breakpoint(0x9f9);
|
||||
|
||||
system.add_interruptable_device("cpu", wrap_transmutable(cpu))?;
|
||||
|
||||
Ok(system)
|
||||
|
|
Loading…
Reference in New Issue
Block a user