Fixed decode and excute issues with Z80

This commit is contained in:
transistor 2021-11-10 13:28:31 -08:00
parent 0836b6de8b
commit f6105de939
8 changed files with 414 additions and 355 deletions

View File

@ -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> {

View File

@ -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)
}
}

View File

@ -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"),
}
}

View File

@ -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]

View File

@ -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!("");

View File

@ -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);
}
}
}

View File

@ -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)?;

View File

@ -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)