Added start of Z80 decoder

This commit is contained in:
transistor 2021-11-01 22:06:40 -07:00
parent 58fc9ac827
commit e0ef1d8fd9
6 changed files with 788 additions and 11 deletions

View File

@ -1,3 +1,4 @@
pub mod m68k;
pub mod z80;

522
src/cpus/z80/decode.rs Normal file
View File

@ -0,0 +1,522 @@
use crate::error::Error;
use crate::devices::{Address, Addressable};
use super::state::{Z80, Z80Type};
#[derive(Copy, Clone, Debug)]
pub enum Condition {
NotZero,
Zero,
NotCarry,
Carry,
ParityOdd,
ParityEven,
Positive,
Negative,
}
#[derive(Copy, Clone, Debug)]
pub enum Register {
A,
B,
C,
D,
E,
H,
L,
F,
}
#[derive(Copy, Clone, Debug)]
pub enum RegisterPair {
BC,
DE,
HL,
SP,
AF,
}
#[derive(Copy, Clone, Debug)]
pub enum Target {
DirectRegByte(Register),
IndirectRegByte(RegisterPair),
ImmediateByte(u8),
}
#[derive(Copy, Clone, Debug)]
pub enum LoadTarget {
DirectRegByte(Register),
DirectRegWord(RegisterPair),
IndirectRegByte(RegisterPair),
IndirectRegWord(RegisterPair),
DirectAltRegByte(Register),
IndirectByte(u16),
IndirectWord(u16),
ImmediateByte(u8),
ImmediateWord(u16),
}
#[derive(Clone, Debug)]
pub enum Instruction {
ADDa(Target),
ADCa(Target),
ADDhl(RegisterPair),
AND(Target),
CP(Target),
CALL(u16),
CALLcc(Condition, u16),
DEC8(Target),
DEC16(RegisterPair),
DJNZ(i8),
DI,
EI,
EXX,
EXafaf,
EXhlsp,
EXhlde,
INx(u8),
INic(Register),
INC8(Target),
INC16(RegisterPair),
JP(u16),
JPcc(Condition, u16),
JPIndirectHL,
JR(i8),
JRcc(Condition, i8),
LD(LoadTarget, LoadTarget),
NOP,
HALT,
POP(RegisterPair),
PUSH(RegisterPair),
RET,
RETcc(Condition),
RST(u8),
OR(Target),
OUTx(u8),
OUTic(Register),
SUB(Target),
SBCa(Target),
XOR(Target),
RLC(Target),
RRC(Target),
RL(Target),
RR(Target),
SLA(Target),
SRA(Target),
SLL(Target),
SRL(Target),
BIT(u8, Target),
RES(u8, Target),
SET(u8, Target),
RLCA,
RRCA,
RLA,
RRA,
DAA,
CPL,
SCF,
CCF,
}
pub struct Z80Decoder {
pub start: u16,
pub end: u16,
pub instruction: Instruction,
}
impl Z80Decoder {
pub fn new() -> Self {
Self {
start: 0,
end: 0,
instruction: Instruction::NOP,
}
}
}
impl Z80Decoder {
pub fn decode_at(&mut self, memory: &mut dyn Addressable, start: u16) -> Result<(), Error> {
self.start = start;
self.end = start;
self.instruction = self.decode_one(memory)?;
Ok(())
}
pub fn decode_one(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Error> {
let ins = self.read_instruction_byte(memory)?;
match get_ins_x(ins) {
0 => {
match get_ins_z(ins) {
0 => {
match get_ins_y(ins) {
0 => Ok(Instruction::NOP),
1 => Ok(Instruction::EXafaf),
2 => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::DJNZ(offset))
},
3 => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::JR(offset))
},
y => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::JRcc(get_condition(y - 4), offset))
},
}
},
1 => {
if get_ins_q(ins) == 0 {
let data = self.read_instruction_word(memory)?;
Ok(Instruction::LD(LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))), LoadTarget::ImmediateWord(data)))
} else {
Ok(Instruction::ADDhl(get_register_pair(get_ins_p(ins))))
}
},
2 => {
if (ins & 0x20) == 0 {
let target = match (ins & 0x10) != 0 {
false => LoadTarget::IndirectRegByte(RegisterPair::BC),
true => LoadTarget::IndirectRegByte(RegisterPair::DE),
};
match (ins & 0x08) != 0 {
false => Ok(Instruction::LD(target, LoadTarget::DirectRegByte(Register::A))),
true => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), target)),
}
} else {
let addr = self.read_instruction_word(memory)?;
match ((ins >> 3) & 0x03) {
0 => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(RegisterPair::HL))),
1 => Ok(Instruction::LD(LoadTarget::IndirectByte(addr), LoadTarget::DirectRegByte(Register::A))),
2 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(addr))),
3 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), LoadTarget::IndirectByte(addr))),
_ => panic!("InternalError: impossible value"),
}
}
},
3 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::INC16(get_register_pair(get_ins_p(ins))))
} else {
Ok(Instruction::DEC16(get_register_pair(get_ins_p(ins))))
}
},
4 => {
Ok(Instruction::INC8(get_register(get_ins_y(ins))))
},
5 => {
Ok(Instruction::DEC8(get_register(get_ins_y(ins))))
},
6 => {
let data = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(to_load_target(get_register(get_ins_y(ins))), LoadTarget::ImmediateByte(data)))
},
7 => {
match get_ins_y(ins) {
0 => Ok(Instruction::RLCA),
1 => Ok(Instruction::RRCA),
2 => Ok(Instruction::RLA),
3 => Ok(Instruction::RRA),
4 => Ok(Instruction::DAA),
5 => Ok(Instruction::CPL),
6 => Ok(Instruction::SCF),
7 => Ok(Instruction::CCF),
_ => panic!("InternalError: impossible value"),
}
},
_ => panic!("InternalError: impossible value"),
}
},
1 => {
if ins == 0x76 {
Ok(Instruction::HALT)
} else {
Ok(Instruction::LD(to_load_target(get_register(get_ins_y(ins))), to_load_target(get_register(get_ins_z(ins)))))
}
},
2 => {
Ok(get_alu_instruction(get_ins_y(ins), get_register(get_ins_z(ins))))
},
3 => {
match get_ins_z(ins) {
0 => {
Ok(Instruction::RETcc(get_condition(get_ins_y(ins))))
},
1 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::POP(get_register_pair_alt(get_ins_p(ins))))
} else {
match get_ins_p(ins) {
0 => Ok(Instruction::RET),
1 => Ok(Instruction::EXX),
2 => Ok(Instruction::JPIndirectHL),
3 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::SP), LoadTarget::DirectRegWord(RegisterPair::HL))),
_ => panic!("InternalError: impossible value"),
}
}
},
2 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::JPcc(get_condition(get_ins_y(ins)), addr))
},
3 => {
match get_ins_y(ins) {
0 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::JP(addr))
},
1 => {
self.decode_prefix_cb(memory)
},
2 => {
let port = self.read_instruction_byte(memory)?;
Ok(Instruction::OUTx(port))
},
3 => {
let port = self.read_instruction_byte(memory)?;
Ok(Instruction::INx(port))
},
4 => Ok(Instruction::EXhlsp),
5 => Ok(Instruction::EXhlde),
6 => Ok(Instruction::DI),
7 => Ok(Instruction::EI),
_ => panic!("InternalError: impossible value"),
}
},
4 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::CALLcc(get_condition(get_ins_y(ins)), addr))
}
5 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::PUSH(get_register_pair_alt(get_ins_p(ins))))
} else {
match get_ins_p(ins) {
0 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::CALL(addr))
},
1 => self.decode_prefix_dd(memory),
2 => self.decode_prefix_ed(memory),
3 => self.decode_prefix_fd(memory),
_ => panic!("Undecoded Instruction"),
}
}
}
6 => {
let data = self.read_instruction_byte(memory)?;
Ok(get_alu_instruction(get_ins_y(ins), Target::ImmediateByte(data)))
},
7 => {
Ok(Instruction::RST(get_ins_y(ins) * 8))
},
_ => panic!("InternalError: impossible value"),
}
},
_ => panic!("Undecoded Instruction"),
}
}
pub fn decode_prefix_cb(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Error> {
let ins = self.read_instruction_byte(memory)?;
match get_ins_x(ins) {
0 => Ok(get_rot_instruction(get_ins_y(ins), get_register(get_ins_z(ins)))),
1 => Ok(Instruction::BIT(get_ins_y(ins), get_register(get_ins_z(ins)))),
2 => Ok(Instruction::RES(get_ins_y(ins), get_register(get_ins_z(ins)))),
3 => Ok(Instruction::SET(get_ins_y(ins), get_register(get_ins_z(ins)))),
_ => panic!("InternalError: impossible value"),
}
}
pub fn decode_prefix_dd(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Error> {
panic!("DD instructions unimplemented")
}
pub fn decode_prefix_ed(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Error> {
let ins = self.read_instruction_byte(memory)?;
match get_ins_x(ins) {
0 => Ok(Instruction::NOP),
1 => {
/*
match get_ins_z(ins) {
0 => {
let y = get_ins_y(ins);
if y == 6 {
//Ok(Instruction::INic),
panic!("");
} else {
//Ok(Instruction::INic(get_register(y))),
}
},
1 => {
},
2 => {
},
3 => {
},
4 => {
},
5 => {
},
6 => {
},
7 => {
},
_ => panic!("InternalError: impossible value"),
}
*/
panic!("random instructions are unimplemented");
},
2 => {
match ins & 0xF0 {
// TODO implement block
//0xA0 => {
//},
//0xB0 => {
//},
_ => Ok(Instruction::NOP),
}
},
3 => Ok(Instruction::NOP),
_ => panic!("InternalError: impossible value"),
}
}
pub fn decode_prefix_fd(&mut self, memory: &mut dyn Addressable) -> Result<Instruction, Error> {
panic!("FD instructions unimplemented")
}
fn read_instruction_byte(&mut self, device: &mut dyn Addressable) -> Result<u8, Error> {
let byte = device.read_u8(self.end as Address)?;
self.end += 1;
Ok(byte)
}
fn read_instruction_word(&mut self, device: &mut dyn Addressable) -> Result<u16, Error> {
let word = device.read_beu16(self.end as Address)?;
self.end += 2;
Ok(word)
}
pub fn dump_decoded(&mut self, memory: &mut dyn Addressable) {
let ins_data: Result<String, Error> =
(0..((self.end - self.start) / 2)).map(|offset|
Ok(format!("{:02x} ", memory.read_u8((self.start + offset) as Address).unwrap()))
).collect();
println!("{:#06x}: {}\n\t{:?}\n", self.start, ins_data.unwrap(), self.instruction);
}
}
fn get_alu_instruction(alu: u8, target: Target) -> Instruction {
match alu {
0 => Instruction::ADDa(target),
1 => Instruction::ADCa(target),
2 => Instruction::SUB(target),
3 => Instruction::SBCa(target),
4 => Instruction::AND(target),
5 => Instruction::XOR(target),
6 => Instruction::OR(target),
7 => Instruction::CP(target),
_ => panic!("InternalError: impossible value"),
}
}
fn get_rot_instruction(rot: u8, target: Target) -> Instruction {
match rot {
0 => Instruction::RLC(target),
1 => Instruction::RRC(target),
2 => Instruction::RL(target),
3 => Instruction::RR(target),
4 => Instruction::SLA(target),
5 => Instruction::SRA(target),
6 => Instruction::SLL(target),
7 => Instruction::SRL(target),
_ => panic!("InternalError: impossible value"),
}
}
fn get_register(reg: u8) -> Target {
match reg {
0 => Target::DirectRegByte(Register::B),
1 => Target::DirectRegByte(Register::C),
2 => Target::DirectRegByte(Register::D),
3 => Target::DirectRegByte(Register::E),
4 => Target::DirectRegByte(Register::H),
5 => Target::DirectRegByte(Register::L),
6 => Target::IndirectRegByte(RegisterPair::HL),
7 => Target::DirectRegByte(Register::A),
_ => panic!("InternalError: impossible value"),
}
}
fn to_load_target(target: Target) -> LoadTarget {
match target {
Target::DirectRegByte(reg) => LoadTarget::DirectRegByte(reg),
Target::IndirectRegByte(reg) => LoadTarget::IndirectRegByte(reg),
Target::ImmediateByte(data) => LoadTarget::ImmediateByte(data),
}
}
fn get_register_pair(reg: u8) -> RegisterPair {
match reg {
0 => RegisterPair::BC,
1 => RegisterPair::DE,
2 => RegisterPair::HL,
3 => RegisterPair::SP,
_ => panic!("InternalError: impossible value"),
}
}
fn get_register_pair_alt(reg: u8) -> RegisterPair {
match reg {
0 => RegisterPair::BC,
1 => RegisterPair::DE,
2 => RegisterPair::HL,
3 => RegisterPair::AF,
_ => panic!("InternalError: impossible value"),
}
}
fn get_condition(cond: u8) -> Condition {
match cond {
0 => Condition::NotZero,
1 => Condition::Zero,
2 => Condition::NotCarry,
3 => Condition::Carry,
4 => Condition::ParityOdd,
5 => Condition::ParityEven,
6 => Condition::Positive,
7 => Condition::Negative,
_ => panic!("InternalError: impossible value"),
}
}
fn get_ins_x(ins: u8) -> u8 {
(ins >> 6) & 0x03
}
fn get_ins_y(ins: u8) -> u8 {
(ins >> 3) & 0x07
}
fn get_ins_z(ins: u8) -> u8 {
ins & 0x07
}
fn get_ins_p(ins: u8) -> u8 {
(ins >> 4) & 0x03
}
fn get_ins_q(ins: u8) -> u8 {
(ins >> 3) & 0x01
}

97
src/cpus/z80/execute.rs Normal file
View File

@ -0,0 +1,97 @@
use crate::system::System;
use crate::error::{ErrorType, Error};
use crate::devices::{ClockElapsed, Address, Steppable, Interruptable, Addressable, Debuggable, Transmutable};
use super::decode::Z80Decoder;
use super::state::{Z80, Z80State, Status};
impl Steppable for Z80 {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
self.step_internal(system)?;
Ok((1_000_000_000 / self.frequency as u64) * 4)
}
fn on_error(&mut self, system: &System) {
//self.dump_state(system);
}
}
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<(), Error> {
match self.state.status {
Status::Init => self.init(system),
Status::Halted => Err(Error::new("CPU stopped")),
Status::Running => {
match self.cycle_one(system) {
Ok(()) => Ok(()),
//Err(Error { err: ErrorType::Processor, native, .. }) => {
Err(Error { err: ErrorType::Processor, native, .. }) => {
//self.exception(system, native as u8, false)?;
Ok(())
},
Err(err) => Err(err),
}
},
}
}
pub fn init(&mut self, system: &System) -> Result<(), Error> {
//self.state.msp = self.port.read_beu32(0)?;
//self.state.pc = self.port.read_beu32(4)?;
self.state.status = Status::Running;
Ok(())
}
pub fn cycle_one(&mut self, system: &System) -> Result<(), Error> {
//self.timer.cycle.start();
self.decode_next(system)?;
//self.execute_current(system)?;
//self.timer.cycle.end();
//if (self.timer.cycle.events % 500) == 0 {
// println!("{}", self.timer);
//}
//self.check_pending_interrupts(system)?;
Ok(())
}
pub fn decode_next(&mut self, system: &System) -> Result<(), Error> {
//self.check_breakpoints(system);
//self.timer.decode.start();
self.decoder.decode_at(&mut self.port, self.state.pc)?;
//self.timer.decode.end();
//if self.debugger.use_tracing {
self.decoder.dump_decoded(&mut self.port);
//}
self.state.pc = self.decoder.end;
Ok(())
}
pub fn execute_current(&mut self, system: &System) -> Result<(), Error> {
panic!("unimplemented");
}
}

7
src/cpus/z80/mod.rs Normal file
View File

@ -0,0 +1,7 @@
pub mod state;
pub mod decode;
pub mod execute;
pub use self::state::{Z80, Z80Type};

161
src/cpus/z80/state.rs Normal file
View File

@ -0,0 +1,161 @@
use crate::system::System;
use crate::devices::Address;
use crate::memory::BusPort;
use super::decode::Z80Decoder;
//use super::debugger::M68kDebugger;
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Z80Type {
Z80,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Status {
Init,
Running,
Halted,
}
#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Flags {
Carry = 0x0001,
Overflow = 0x0002,
Zero = 0x0004,
Negative = 0x0008,
Extend = 0x0010,
}
/*
#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Exceptions {
BusError = 2,
AddressError = 3,
IllegalInstruction = 4,
ZeroDivide = 5,
ChkInstruction = 6,
TrapvInstruction = 7,
PrivilegeViolation = 8,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum InterruptPriority {
NoInterrupt = 0,
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
Level5 = 5,
Level6 = 6,
Level7 = 7,
}
impl InterruptPriority {
pub fn from_u8(priority: u8) -> InterruptPriority {
match priority {
0 => InterruptPriority::NoInterrupt,
1 => InterruptPriority::Level1,
2 => InterruptPriority::Level2,
3 => InterruptPriority::Level3,
4 => InterruptPriority::Level4,
5 => InterruptPriority::Level5,
6 => InterruptPriority::Level6,
_ => InterruptPriority::Level7,
}
}
}
*/
#[derive(Clone, Debug, PartialEq)]
pub struct Z80State {
pub status: Status,
pub pc: u16,
pub sp: u16,
pub ix: u16,
pub iy: u16,
pub reg: [u8; 8],
pub alt_reg: [u8; 8],
pub i: u8,
pub r: u8,
}
impl Z80State {
pub fn new() -> Self {
Self {
status: Status::Init,
pc: 0,
sp: 0,
ix: 0,
iy: 0,
reg: [0; 8],
alt_reg: [0; 8],
i: 0,
r: 0,
}
}
}
pub struct Z80 {
pub cputype: Z80Type,
pub frequency: u32,
pub state: Z80State,
pub decoder: Z80Decoder,
//pub debugger: M68kDebugger,
pub port: BusPort,
}
impl Z80 {
pub fn new(cputype: Z80Type, frequency: u32, port: BusPort) -> Self {
Self {
cputype,
frequency,
state: Z80State::new(),
decoder: Z80Decoder::new(),
//debugger: M68kDebugger::new(),
port: port,
}
}
#[allow(dead_code)]
pub fn reset(&mut self) {
self.state = Z80State::new();
//self.decoder = M68kDecoder::new(self.cputype, 0);
//self.debugger = M68kDebugger::new();
}
/*
pub fn dump_state(&mut self, system: &System) {
println!("Status: {:?}", self.state.status);
println!("PC: {:#010x}", self.state.pc);
println!("SR: {:#06x}", self.state.sr);
for i in 0..7 {
println!("D{}: {:#010x} A{}: {:#010x}", i, self.state.d_reg[i as usize], i, self.state.a_reg[i as usize]);
}
println!("D7: {:#010x}", self.state.d_reg[7]);
println!("MSP: {:#010x}", self.state.msp);
println!("USP: {:#010x}", self.state.usp);
println!("Current Instruction: {:#010x} {:?}", self.decoder.start, self.decoder.instruction);
println!("");
self.port.dump_memory(self.state.msp as Address, 0x40);
println!("");
}
*/
}

View File

@ -2,11 +2,6 @@
* fix ym7101 to better handle V/H interrupts (right now it sets and then the next step will clear, but it'd be nice if it could 'edge trigger')
* could have a remapper device, which takes a big swath of addresses in and maps them to another set of addresses (for Mac VIA generic to bus-hookup-in-mac adapter)
* sort out how inputing keys will work
* separate the debugger out of m68k, use on_debug or something to print out debug info
* make devices nameable, using a hashmap to store them
* how can you do devices that change their address map during operation, like mac which puts rom at 0 and ram at 600000 temporarily
* i need a better way of handling disperate reads/writes to I/O spaces, rather than having multiple devices or having a massive chunk of address space allocated, continuously
* should you modify Addressable to also take the absolute address as input? I'm thinking of how the same device could be mapped to multiple addresses in memory instead
@ -15,12 +10,6 @@
So both could share the same Signal, one setting it and the other reading it, but how would you actually configure/build that?
We Need:
* devices that change address mapping during operation
* device that can be mapped to multiple separated locations while sharing data
* subdevices that can share data (eg. signal from one to another (mac128 via outputs), or being a subdevice with a special trait (joystick))
* implement a Z80
* maybe see about a Mac 128k or something