diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index 430566d..b1b9839 100644 --- a/src/cpus/m68k/execute.rs +++ b/src/cpus/m68k/execute.rs @@ -29,7 +29,7 @@ use super::state::{M68k, M68kType, Status, Flags, Exceptions, InterruptPriority} impl Steppable for M68k { fn step(&mut self, system: &System) -> Result { self.step_internal(system)?; - Ok((1_000_000_000 / self.frequency as u64) * 4 as ClockElapsed) + Ok((1_000_000_000 / self.frequency as u64) * self.timing.calculate_clocks(false, 1) as ClockElapsed) } fn on_error(&mut self, system: &System) { @@ -151,10 +151,14 @@ impl M68k { } pub fn decode_next(&mut self) -> Result<(), Error> { + self.timing.reset(); + self.timer.decode.start(); self.decoder.decode_at(&mut self.port, self.state.pc)?; self.timer.decode.end(); + self.timing.add_instruction(&self.decoder.instruction); + if self.debugger.use_tracing { self.decoder.dump_decoded(&mut self.port); } diff --git a/src/cpus/m68k/mod.rs b/src/cpus/m68k/mod.rs index f1e48f9..b6c0841 100644 --- a/src/cpus/m68k/mod.rs +++ b/src/cpus/m68k/mod.rs @@ -4,7 +4,9 @@ pub mod decode; pub mod execute; pub mod debugger; pub mod instructions; +pub mod timing; pub mod tests; +//pub mod testcases; pub use self::state::{M68k, M68kType}; diff --git a/src/cpus/m68k/state.rs b/src/cpus/m68k/state.rs index 0c64e51..8237df7 100644 --- a/src/cpus/m68k/state.rs +++ b/src/cpus/m68k/state.rs @@ -6,7 +6,7 @@ use crate::memory::BusPort; use super::decode::M68kDecoder; use super::debugger::M68kDebugger; - +use super::timing::M68kInstructionTiming; #[allow(dead_code)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -126,6 +126,7 @@ pub struct M68k { pub frequency: u32, pub state: M68kState, pub decoder: M68kDecoder, + pub timing: M68kInstructionTiming, pub debugger: M68kDebugger, pub port: BusPort, pub timer: CpuTimer, @@ -138,6 +139,7 @@ impl M68k { frequency, state: M68kState::new(), decoder: M68kDecoder::new(cputype, 0), + timing: M68kInstructionTiming::new(cputype, port.data_width()), debugger: M68kDebugger::new(), port: port, timer: CpuTimer::new(), @@ -148,6 +150,7 @@ impl M68k { pub fn reset(&mut self) { self.state = M68kState::new(); self.decoder = M68kDecoder::new(self.cputype, 0); + self.timing = M68kInstructionTiming::new(self.cputype, self.port.data_width()); self.debugger = M68kDebugger::new(); } diff --git a/src/cpus/m68k/tests.rs b/src/cpus/m68k/tests.rs index 8420c17..53f0cf7 100644 --- a/src/cpus/m68k/tests.rs +++ b/src/cpus/m68k/tests.rs @@ -9,6 +9,7 @@ mod decode_tests { use crate::cpus::m68k::{M68k, M68kType}; use crate::cpus::m68k::state::Exceptions; use crate::cpus::m68k::instructions::{Instruction, Target, Size, Sign, XRegister, BaseRegister, IndexRegister, Direction, ShiftDirection}; + use crate::cpus::m68k::timing::M68kInstructionTiming; const INIT_STACK: Address = 0x00002000; const INIT_ADDR: Address = 0x00000010; @@ -26,8 +27,15 @@ mod decode_tests { TestCase { cpu: M68kType::MC68000, data: &[0x003C, 0x00FF], ins: Some(Instruction::ORtoCCR(0xFF)) }, TestCase { cpu: M68kType::MC68000, data: &[0x007C, 0x1234], ins: Some(Instruction::ORtoSR(0x1234)) }, TestCase { cpu: M68kType::MC68000, data: &[0x0263, 0x1234], ins: Some(Instruction::AND(Target::Immediate(0x1234), Target::IndirectARegDec(3), Size::Word)) }, + TestCase { cpu: M68kType::MC68000, data: &[0x0240, 0x1234], ins: Some(Instruction::AND(Target::Immediate(0x1234), Target::DirectDReg(0), Size::Word)) }, + TestCase { cpu: M68kType::MC68000, data: &[0x02A3, 0x1234, 0x5678], ins: Some(Instruction::AND(Target::Immediate(0x12345678), Target::IndirectARegDec(3), Size::Long)) }, + TestCase { cpu: M68kType::MC68000, data: &[0x0280, 0x1234, 0x5678], ins: Some(Instruction::AND(Target::Immediate(0x12345678), Target::DirectDReg(0), Size::Long)) }, TestCase { cpu: M68kType::MC68000, data: &[0x023C, 0x1234], ins: Some(Instruction::ANDtoCCR(0x34)) }, TestCase { cpu: M68kType::MC68000, data: &[0x027C, 0xF8FF], ins: Some(Instruction::ANDtoSR(0xF8FF)) }, + TestCase { cpu: M68kType::MC68000, data: &[0x4240], ins: Some(Instruction::CLR(Target::DirectDReg(0), Size::Word)) }, + TestCase { cpu: M68kType::MC68000, data: &[0x4280], ins: Some(Instruction::CLR(Target::DirectDReg(0), Size::Long)) }, + TestCase { cpu: M68kType::MC68000, data: &[0x4250], ins: Some(Instruction::CLR(Target::IndirectAReg(0), Size::Word)) }, + TestCase { cpu: M68kType::MC68000, data: &[0x4290], ins: Some(Instruction::CLR(Target::IndirectAReg(0), Size::Long)) }, TestCase { cpu: M68kType::MC68000, data: &[0x0487, 0x1234, 0x5678], ins: Some(Instruction::SUB(Target::Immediate(0x12345678), Target::DirectDReg(7), Size::Long)) }, TestCase { cpu: M68kType::MC68000, data: &[0x063A, 0x1234, 0x0055], ins: Some(Instruction::ADD(Target::Immediate(0x34), Target::IndirectRegOffset(BaseRegister::PC, None, 0x55), Size::Byte)) }, TestCase { cpu: M68kType::MC68000, data: &[0x0A23, 0x1234], ins: Some(Instruction::EOR(Target::Immediate(0x34), Target::IndirectARegDec(3), Size::Byte)) }, @@ -116,6 +124,65 @@ mod decode_tests { } } + //use super::super::testcases::{TimingCase, TIMING_TESTS}; + + struct TimingCase { + cpu: M68kType, + data: &'static [u16], + timing: (u16, u16, u16), + ins: Instruction, + } + + const TIMING_TESTS: &'static [TimingCase] = &[ + TimingCase { cpu: M68kType::MC68000, data: &[0xD090], timing: ( 14, 14, 6), ins: Instruction::ADD(Target::IndirectAReg(0), Target::DirectDReg(0), Size::Long) }, + ]; + + fn run_timing_test(case: &TimingCase) -> Result<(), Error> { + let (mut cpu, system) = init_decode_test(case.cpu); + let mut timing = M68kInstructionTiming::new(case.cpu, 16); + + load_memory(&system, case.data); + cpu.decode_next().unwrap(); + assert_eq!(cpu.decoder.instruction, case.ins.clone()); + + timing.add_instruction(&cpu.decoder.instruction); + let result = timing.calculate_clocks(false, 1); + let expected = match case.cpu { + M68kType::MC68000 => case.timing.0, + M68kType::MC68010 => case.timing.1, + _ => case.timing.2, + }; + + //assert_eq!(expected, result); + if expected == result { + Ok(()) + } else { + println!("{:?}", timing); + Err(Error::new(&format!("expected {} but found {}", expected, result))) + } + } + + #[test] + pub fn run_timing_tests() { + let mut errors = 0; + for case in TIMING_TESTS { + // NOTE switched to only show the failures rather than all tests + //print!("Testing for {:?}...", case.ins); + //match run_timing_test(case) { + // Ok(()) => println!("ok"), + // Err(err) => { println!("{}", err.msg); errors += 1 }, + //} + + if let Err(err) = run_timing_test(case) { + errors += 1; + } + } + + if errors > 0 { + panic!("{} errors", errors); + } + } + // // Addressing Mode Target Tests // @@ -306,7 +373,7 @@ mod decode_tests { system.get_bus().write_beu16(INIT_ADDR, expected as u16).unwrap(); let target = cpu.decoder.get_mode_as_target(&mut cpu.port, 0b111, 0b000, Some(size)).unwrap(); - assert_eq!(target, Target::IndirectMemory(expected)); + assert_eq!(target, Target::IndirectMemory(expected, Size::Word)); } #[test] @@ -319,7 +386,7 @@ mod decode_tests { system.get_bus().write_beu32(INIT_ADDR, expected).unwrap(); let target = cpu.decoder.get_mode_as_target(&mut cpu.port, 0b111, 0b001, Some(size)).unwrap(); - assert_eq!(target, Target::IndirectMemory(expected)); + assert_eq!(target, Target::IndirectMemory(expected, Size::Long)); } #[test] diff --git a/src/cpus/m68k/timing.rs b/src/cpus/m68k/timing.rs new file mode 100644 index 0000000..bf2cb41 --- /dev/null +++ b/src/cpus/m68k/timing.rs @@ -0,0 +1,369 @@ + +use super::state::M68kType; + +use super::instructions::{ + Register, + Size, + Sign, + Direction, + ShiftDirection, + XRegister, + BaseRegister, + IndexRegister, + RegOrImmediate, + ControlRegister, + Condition, + Target, + Instruction, +}; + + +#[derive(Clone, Debug, PartialEq)] +pub struct M68kInstructionTiming { + pub cputype: M68kType, + pub bus_size: Size, + + pub accesses: u8, + pub internal: u8, + pub on_branch: u8, + pub per_rep: u8, +} + +impl M68kInstructionTiming { + pub fn new(cputype: M68kType, bus_width: u8) -> Self { + let bus_size = if bus_width <= 16 { Size::Word } else { Size::Long }; + Self { + cputype, + bus_size, + + accesses: 0, + internal: 0, + on_branch: 0, + per_rep: 0, + } + } + + pub fn reset(&mut self) { + self.accesses = 0; + self.internal = 0; + self.on_branch = 0; + self.per_rep = 0; + } + + #[inline(always)] + pub fn add_access(&mut self, size: Size) -> &mut Self { + self.accesses += self.access_size(size); + self + } + + #[inline(always)] + pub fn add_internal(&mut self, clocks: u8) -> &mut Self { + self.internal += clocks; + self + } + + #[inline(always)] + pub fn sub_internal(&mut self, clocks: u8) -> &mut Self { + self.internal = self.internal.saturating_sub(clocks); + self + } + + #[inline(always)] + pub fn add_if_long(&mut self, size: Size, clocks: u8) -> &mut Self { + self.internal += if size == Size::Long { clocks } else { 0 }; + self + } + + #[inline(always)] + pub fn add_reg_v_mem(&mut self, target: &Target, reg: u8, mem: u8) -> &mut Self { + self.internal += match target { + Target::DirectDReg(_) | + Target::DirectAReg(_) => reg, + _ => mem, + }; + self + } + + #[inline(always)] + pub fn add_word_v_long(&mut self, size: Size, word: u8, long: u8) -> &mut Self { + self.internal += if size != Size::Long { word } else { long }; + self + } + + #[inline(always)] + pub fn add_standard_set(&mut self, size: Size, target: &Target, areg: (u8, u8), dreg: (u8, u8), mem: (u8, u8)) -> &mut Self { + match target { + Target::DirectDReg(_) => self.add_word_v_long(size, dreg.0, dreg.1), + Target::DirectAReg(_) => self.add_word_v_long(size, areg.0, dreg.1), + _ => self.add_word_v_long(size, mem.0, mem.1), + } + } + + #[inline(always)] + pub fn add_immediate_set(&mut self, size: Size, target: &Target, reg: (u8, u8), mem: (u8, u8)) -> &mut Self { + match target { + Target::DirectDReg(_) | Target::DirectAReg(_) => self.add_word_v_long(size, reg.0, reg.1), + _ => self.add_word_v_long(size, mem.0, mem.1) + } + } + + #[inline(always)] + pub fn add_indirect_set(&mut self, target: &Target, areg: u8, aoff: u8, indoff: u8, indw: u8, indl: u8) -> &mut Self { + match target { + Target::IndirectAReg(_) => self.add_internal(areg), + Target::IndirectRegOffset(_, None, _) => self.add_internal(aoff), + Target::IndirectRegOffset(_, Some(_), _) => self.add_internal(indoff), + Target::IndirectMemory(_, Size::Long) => self.add_internal(indl), + Target::IndirectMemory(_, _) => self.add_internal(indw), + _ => panic!("This timing set can't be used with {:?} targetting", target), + } + } + + #[inline(always)] + pub fn add_reg_mem_set(&mut self, size: Size, target: &Target, reg: (u8, u8), mem: (u8, u8)) -> &mut Self { + match target { + Target::DirectDReg(_) | Target::DirectAReg(_) => self.add_word_v_long(size, reg.0, reg.1), + _ => self.add_word_v_long(size, mem.0, mem.1) + } + } + + #[inline(always)] + pub fn add_on_branch(&mut self, clocks: u8) -> &mut Self { + self.on_branch += clocks; + self + } + + #[inline(always)] + pub fn add_per_rep(&mut self, clocks: u8) -> &mut Self { + self.per_rep += clocks; + self + } + + pub fn add_target(&mut self, size: Size, target: &Target) -> &mut Self { + match target { + Target::Immediate(_) => self.add_access(size), + Target::DirectDReg(_) => self, + Target::DirectAReg(_) => self, + Target::IndirectAReg(_) => self.add_access(size), + Target::IndirectARegInc(_) => self.add_access(size), + Target::IndirectARegDec(_) => self.add_access(size).add_internal(2), + Target::IndirectRegOffset(base_reg, index_reg, offset) => { + match index_reg { + None => self.add_access(size).add_internal(4), + Some(_) => self.add_access(size).add_internal(6), + } + }, + Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) | + Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => { + // TODO this is very wrong, but the 68020 timings are complicated + match index_reg { + None => self.add_access(size).add_internal(4), + Some(_) => self.add_access(size).add_internal(6), + } + }, + Target::IndirectMemory(_, addr_size) => self.add_access(*addr_size).add_access(size), + } + } + + pub fn add_two_targets(&mut self, size: Size, src: &Target, dest: &Target) -> &mut Self { + match (src, dest) { + (Target::IndirectARegDec(_), Target::IndirectARegDec(_)) => self.add_target(size, src).add_target(size, dest).sub_internal(2), + _ => self.add_target(size, src).add_target(size, dest), + } + } + + pub fn add_movem(&mut self, size: Size, target: &Target, dir: Direction, n: u8) -> &mut Self { + if dir == Direction::FromTarget { + match target { + Target::IndirectAReg(_) => self.add_word_v_long(size, 12 + 4 * n, 12 + 8 * n), + Target::IndirectARegInc(_) => self.add_word_v_long(size, 12 + 4 * n, 12 + 8 * n), + Target::IndirectRegOffset(_, None, _) => self.add_word_v_long(size, 16 + 4 * n, 16 + 8 * n), + Target::IndirectRegOffset(_, Some(_), _) => self.add_word_v_long(size, 18 + 4 * n, 18 + 8 * n), + Target::IndirectMemory(_, Size::Long) => self.add_word_v_long(size, 20 + 4 * n, 20 + 8 * n), + Target::IndirectMemory(_, _) => self.add_word_v_long(size, 16 + 4 * n, 16 + 8 * n), + _ => panic!("This timing set can't be used with {:?} targetting", target), + } + } else { + match target { + Target::IndirectAReg(_) => self.add_word_v_long(size, 8 + 4 * n, 8 + 8 * n), + Target::IndirectARegDec(_) => self.add_word_v_long(size, 8 + 4 * n, 8 + 8 * n), + Target::IndirectRegOffset(_, None, _) => self.add_word_v_long(size, 12 + 4 * n, 12 + 8 * n), + Target::IndirectRegOffset(_, Some(_), _) => self.add_word_v_long(size, 14 + 4 * n, 14 + 8 * n), + Target::IndirectMemory(_, Size::Long) => self.add_word_v_long(size, 16 + 4 * n, 16 + 8 * n), + Target::IndirectMemory(_, _) => self.add_word_v_long(size, 12 + 4 * n, 12 + 8 * n), + _ => panic!("This timing set can't be used with {:?} targetting", target), + } + } + } + + pub fn add_instruction(&mut self, instruction: &Instruction) -> &mut Self { + match self.cputype { + M68kType::MC68000 | M68kType::MC68010 => self.add_instruction_68000(instruction), + _ => self.add_instruction_68020(instruction), + } + } + + pub fn add_instruction_68000(&mut self, instruction: &Instruction) -> &mut Self { + match instruction { + Instruction::ABCD(src, dest) => self.add_reg_v_mem(dest, 6, 18), + + Instruction::ADD(src @ Target::Immediate(x), dest, size) if *x <= 8 => self.add_immediate_set(*size, dest, (4, 8), (8, 12)).add_target(*size, dest),// ADDQ + Instruction::ADD(src @ Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest), // ADDI + + Instruction::ADD(src, dest, size) => self.add_standard_set(*size, dest, (8, 6), (4, 6), (8, 12)).add_two_targets(*size, src, dest), + Instruction::ADDA(target, reg, size) => self.add_word_v_long(*size, 8, 6).add_target(*size, target), + Instruction::ADDX(src, dest, size) => self.add_reg_mem_set(*size, dest, (4, 8), (18, 30)), + + Instruction::AND(src @ Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 14), (12, 20)).add_target(*size, dest), + Instruction::AND(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12)).add_two_targets(*size, src, dest), + Instruction::ANDtoCCR(_) => self.add_internal(20), + Instruction::ANDtoSR(_) => self.add_internal(20), + + Instruction::ASd(count, target, size, dir) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target), + + Instruction::Bcc(_, _) => self.add_internal(8).add_on_branch(2), + Instruction::BRA(_) => self.add_internal(10), + Instruction::BSR(_) => self.add_internal(18), + + Instruction::BCHG(bit, target, size) => match bit { + Target::Immediate(_) => self.add_reg_v_mem(target, 12, 12), + _ => self.add_reg_v_mem(target, 8, 8), + }.add_target(*size, target), + Instruction::BCLR(bit, target, size) => match bit { + Target::Immediate(_) => self.add_reg_v_mem(target, 14, 12), + _ => self.add_reg_v_mem(target, 10, 8), + }.add_target(*size, target), + Instruction::BSET(bit, target, size) => match bit { + Target::Immediate(_) => self.add_reg_v_mem(target, 12, 12), + _ => self.add_reg_v_mem(target, 8, 8), + }.add_target(*size, target), + Instruction::BTST(bit, target, size) => match bit { + Target::Immediate(_) => self.add_reg_v_mem(target, 10, 8), + _ => self.add_reg_v_mem(target, 6, 4), + }.add_target(*size, target), + + Instruction::CHK(target, reg, size) => self.add_internal(10), + Instruction::CLR(target, size) => self.add_reg_v_mem(target, 4, 8).add_word_v_long(*size, 0, 2).add_target(*size, target), + + Instruction::CMP(src @ Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 14), (8, 12)).add_target(*size, dest), + Instruction::CMP(src, dest, size) => self.add_standard_set(*size, dest, (6, 6), (4, 6), (0, 0)).add_two_targets(*size, src, dest), + Instruction::CMPA(target, _, size) => self.add_word_v_long(*size, 6, 6).add_target(*size, target), + + Instruction::DBcc(_, _, _) => self.add_internal(10).add_on_branch(4), + Instruction::DIVW(src, _, sign) => match sign { + Sign::Unsigned => self.add_internal(140).add_target(Size::Long, src), + Sign::Signed => self.add_internal(158).add_target(Size::Long, src), + }, + + Instruction::EOR(src @ Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest), + Instruction::EOR(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 8), (8, 12)).add_two_targets(*size, src, dest), + Instruction::EORtoCCR(_) => self.add_internal(20), + Instruction::EORtoSR(_) => self.add_internal(20), + + Instruction::EXG(_, _) => self.add_internal(6), + Instruction::EXT(_, _, _) => self.add_internal(4), + + Instruction::ILLEGAL => self.add_internal(4), + + Instruction::JMP(target) => self.add_indirect_set(target, 8, 10, 14, 10, 12), + Instruction::JSR(target) => self.add_indirect_set(target, 16, 18, 22, 18, 20), + + Instruction::LEA(target, reg) => self.add_indirect_set(target, 4, 8, 12, 8, 12), + Instruction::LINK(_, _) => self.add_internal(16), + Instruction::LSd(count, target, size, dir) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target), + + Instruction::MOVE(src, dest, size) => self.add_internal(4).add_two_targets(*size, src, dest), + Instruction::MOVEA(target, _, size) => self.add_internal(4).add_target(*size, target), + Instruction::MOVEfromSR(target) => self.add_reg_v_mem(target, 6, 8).add_target(Size::Word, target), + Instruction::MOVEtoSR(target) => self.add_internal(12).add_target(Size::Word, target), + Instruction::MOVEfromCCR(target) => self.add_internal(12).add_target(Size::Word, target), + Instruction::MOVEtoCCR(target) => self.add_internal(12).add_target(Size::Word, target), + Instruction::MOVEC(target, reg, dir) => self.add_reg_v_mem(target, 10, 12), + Instruction::MOVEM(target, size, dir, mask) => self.add_movem(*size, target, *dir, mask.count_ones() as u8), + Instruction::MOVEP(_, _, _, size, _) => self.add_word_v_long(*size, 16, 24), + Instruction::MOVEQ(value, reg) => self.add_internal(4), + Instruction::MOVEUSP(_, _) => self.add_internal(4), + + Instruction::MULW(src, _, _) => self.add_internal(70).add_target(Size::Word, src), + + Instruction::NBCD(target) => self.add_reg_v_mem(target, 6, 8), + Instruction::NEG(target, size) => self.add_reg_mem_set(*size, target, (4, 6), (8, 12)).add_target(*size, target), + Instruction::NEGX(target, size) => self.add_reg_mem_set(*size, target, (4, 6), (8, 12)).add_target(*size, target), + + Instruction::NOP => self.add_internal(4), + Instruction::NOT(target, size) => self.add_reg_mem_set(*size, target, (4, 6), (8, 12)).add_target(*size, target), + + Instruction::OR(src @ Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest), + Instruction::OR(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12)).add_two_targets(*size, src, dest), + Instruction::ORtoCCR(_) => self.add_internal(20), + Instruction::ORtoSR(_) => self.add_internal(20), + + Instruction::PEA(target) => self.add_indirect_set(target, 12, 16, 20, 16, 20), + + Instruction::RESET => self.add_internal(132), + + Instruction::ROd(count, target, size, dir) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target), + Instruction::ROXd(count, target, size, dir) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target), + + Instruction::RTE => self.add_internal(20), + Instruction::RTR => self.add_internal(20), + Instruction::RTS => self.add_internal(16), + //Instruction::RTD(offset) => , + + Instruction::SBCD(src, dest) => self.add_reg_v_mem(dest, 6, 18), + Instruction::Scc(_, target) => self.add_reg_v_mem(target, 4, 8).add_on_branch(2).add_target(Size::Byte, target), + Instruction::STOP(_) => self.add_internal(4), + + Instruction::SUB(Target::Immediate(x), Target::DirectAReg(_), Size::Byte) + | Instruction::SUB(Target::Immediate(x), Target::DirectAReg(_), Size::Word) if *x <= 8 => self.add_internal(8), // SUBQ with an address reg as dest + Instruction::SUB(src @ Target::Immediate(x), dest, size) if *x <= 8 => self.add_immediate_set(*size, dest, (4, 8), (8, 12)).add_target(*size, dest), // SUBQ + Instruction::SUB(src @ Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest), // SUBI + + Instruction::SUB(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12)).add_two_targets(*size, src, dest), + Instruction::SUBA(target, reg, size) => self.add_word_v_long(*size, 8, 6).add_target(*size, target), + Instruction::SUBX(src, dest, size) => self.add_reg_mem_set(*size, dest, (4, 8), (18, 30)), + + Instruction::SWAP(_) => self.add_internal(4), + + Instruction::TAS(target) => self.add_reg_v_mem(target, 4, 14).add_target(Size::Byte, target), + Instruction::TST(target, size) => self.add_internal(4).add_target(*size, target), + Instruction::TRAP(_) => self.add_internal(34), + Instruction::TRAPV => self.add_internal(34), + + Instruction::UNLK(_) => self.add_internal(12), + Instruction::UnimplementedA(_) => self.add_internal(34), + Instruction::UnimplementedF(_) => self.add_internal(34), + _ => { + //panic!("Unexpected instruction for cpu type {:?}: {:?}", self.cputype, instruction); + self.add_internal(4) + }, + } + } + + pub fn add_instruction_68020(&mut self, instruction: &Instruction) -> &mut Self { + match instruction { + // TODO implement + _ => self.add_internal(4), + } + } + + pub fn calculate_clocks(&self, branched: bool, reps: u16) -> u16 { + //println!("{:?}", self); + (self.accesses as u16 * 4) + + self.internal as u16 + + (if branched { self.on_branch as u16 } else { 0 }) + + self.per_rep as u16 * reps + } + + #[inline(always)] + pub fn access_size(&self, size: Size) -> u8 { + if self.bus_size == Size::Word && size == Size::Long { + 2 + } else { + 1 + } + } +} + + +