From 1b6cbfa83112f13f81b178ea3ef9fbe2155a92a7 Mon Sep 17 00:00:00 2001 From: transistor Date: Tue, 14 Dec 2021 16:06:34 -0800 Subject: [PATCH] Fixed some decoder bugs in m68k --- src/cpus/m68k/decode.rs | 17 ++++---- src/cpus/m68k/execute.rs | 20 +++++----- src/cpus/m68k/instructions.rs | 75 +++++++++++++++++++++++++++++++---- src/cpus/z80/debugger.rs | 2 +- src/memory.rs | 4 ++ todo.txt | 34 +++++++++------- 6 files changed, 112 insertions(+), 40 deletions(-) diff --git a/src/cpus/m68k/decode.rs b/src/cpus/m68k/decode.rs index 5122abf..e2544e3 100644 --- a/src/cpus/m68k/decode.rs +++ b/src/cpus/m68k/decode.rs @@ -74,7 +74,7 @@ impl M68kDecoder { OPCG_BIT_OPS => { let optype = (ins & 0x0F00) >> 8; - if (ins & 0x3F) == 0b111100 { + if (ins & 0x13F) == 0x03C { match (ins & 0x00C0) >> 6 { 0b00 => { let data = self.read_instruction_word(memory)?; @@ -171,11 +171,15 @@ impl M68kDecoder { let ins_0f00 = ins & 0xF00; let ins_00f0 = ins & 0x0F0; - if (ins & 0x180) == 0x180 && (ins & 0x038) != 0 { + if (ins & 0x180) == 0x180 { if (ins & 0x040) == 0 { let size = match get_size(ins) { Some(Size::Word) => Size::Word, - Some(Size::Long) if self.cputype >= M68kType::MC68020 => Size::Long, + // TODO 2021-12-14: the docs for the 68000 show the size field, even though it says it only operates on words + // and according to some other sources, it seems they are parsed and executed on the 68000, so + // I'm removing the check that only allows this on the MC68020 + //Some(Size::Long) if self.cputype >= M68kType::MC68020 => Size::Long, + Some(Size::Long) => Size::Long, _ => return Err(Error::processor(Exceptions::IllegalInstruction as u32)), }; @@ -698,11 +702,11 @@ impl M68kDecoder { match reg { 0b000 => { let value = sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word) as u32; - Target::IndirectMemory(value) + Target::IndirectMemory(value, Size::Word) }, 0b001 => { let value = self.read_instruction_long(memory)?; - Target::IndirectMemory(value) + Target::IndirectMemory(value, Size::Long) }, 0b010 => { let displacement = sign_extend_to_long(self.read_instruction_word(memory)? as u32, Size::Word); @@ -754,7 +758,7 @@ impl M68kDecoder { (0..((self.end - self.start) / 2)).map(|offset| Ok(format!("{:04x} ", memory.read_beu16((self.start + (offset * 2)) as Address).unwrap())) ).collect(); - println!("{:#010x}: {}\n\t{:?}\n", self.start, ins_data.unwrap(), self.instruction); + println!("{:#010x}: {}\n\t{}\n", self.start, ins_data.unwrap(), self.instruction); } } @@ -807,7 +811,6 @@ fn get_condition(ins: u16) -> Condition { 0b1101 => Condition::LessThan, 0b1110 => Condition::GreaterThan, 0b1111 => Condition::LessThanOrEqual, - _ => Condition::True, } } diff --git a/src/cpus/m68k/execute.rs b/src/cpus/m68k/execute.rs index 071a0be..430566d 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) + Ok((1_000_000_000 / self.frequency as u64) * 4 as ClockElapsed) } fn on_error(&mut self, system: &System) { @@ -756,11 +756,11 @@ impl M68k { let addr = self.get_a_reg_mut(reg); *addr = new_value; }, - Instruction::UnimplementedA(ins) => { + Instruction::UnimplementedA(_) => { self.state.pc -= 2; self.exception(Exceptions::LineAEmulator as u8, false)?; }, - Instruction::UnimplementedF(ins) => { + Instruction::UnimplementedF(_) => { self.state.pc -= 2; self.exception(Exceptions::LineFEmulator as u8, false)?; }, @@ -771,8 +771,8 @@ impl M68k { Ok(()) } - fn execute_movem(&mut self, target: Target, size: Size, dir: Direction, mut mask: u16) -> Result<(), Error> { - let mut addr = self.get_target_address(target)?; + fn execute_movem(&mut self, target: Target, size: Size, dir: Direction, mask: u16) -> Result<(), Error> { + let addr = self.get_target_address(target)?; // If we're using a MC68020 or higher, and it was Post-Inc/Pre-Dec target, then update the value before it's stored if self.cputype >= M68kType::MC68020 { @@ -786,13 +786,13 @@ impl M68k { } let post_addr = match target { - Target::IndirectARegInc(reg) => { + Target::IndirectARegInc(_) => { if dir != Direction::FromTarget { return Err(Error::new(&format!("Cannot use {:?} with {:?}", target, dir))); } self.move_memory_to_registers(addr, size, mask)? }, - Target::IndirectARegDec(reg) => { + Target::IndirectARegDec(_) => { if dir != Direction::ToTarget { return Err(Error::new(&format!("Cannot use {:?} with {:?}", target, dir))); } @@ -937,7 +937,7 @@ impl M68k { let intermediate = self.get_address_sized(base_value.wrapping_add(base_disp as u32) as Address, Size::Long)?; self.get_address_sized(intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32) as Address, size) }, - Target::IndirectMemory(addr) => { + Target::IndirectMemory(addr, _) => { self.get_address_sized(addr as Address, size) }, } @@ -984,7 +984,7 @@ impl M68k { let intermediate = self.get_address_sized(base_value.wrapping_add(base_disp as u32) as Address, Size::Long)?; self.set_address_sized(intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32) as Address, value, size)?; }, - Target::IndirectMemory(addr) => { + Target::IndirectMemory(addr, _) => { self.set_address_sized(addr as Address, value, size)?; }, _ => return Err(Error::new(&format!("Unimplemented addressing target: {:?}", target))), @@ -1012,7 +1012,7 @@ impl M68k { let intermediate = self.get_address_sized(base_value.wrapping_add(base_disp as u32) as Address, Size::Long)?; intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32) }, - Target::IndirectMemory(addr) => { + Target::IndirectMemory(addr, _) => { addr }, _ => return Err(Error::new(&format!("Invalid addressing target: {:?}", target))), diff --git a/src/cpus/m68k/instructions.rs b/src/cpus/m68k/instructions.rs index aea72ec..e53793d 100644 --- a/src/cpus/m68k/instructions.rs +++ b/src/cpus/m68k/instructions.rs @@ -91,7 +91,7 @@ pub enum Target { IndirectRegOffset(BaseRegister, Option, i32), IndirectMemoryPreindexed(BaseRegister, Option, i32, i32), IndirectMemoryPostindexed(BaseRegister, Option, i32, i32), - IndirectMemory(u32), + IndirectMemory(u32, Size), } #[derive(Clone, Debug, PartialEq)] @@ -285,6 +285,15 @@ impl fmt::Display for ControlRegister { } } +impl fmt::Display for RegOrImmediate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RegOrImmediate::DReg(reg) => write!(f, "%d{}", reg), + RegOrImmediate::Immediate(value) => write!(f, "#{:#02x}", value), + } + } +} + impl fmt::Display for XRegister { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -339,13 +348,50 @@ impl fmt::Display for Target { let index_str = fmt_index_disp(index_reg); write!(f, "([{}#{:08x}]{} + #{:08x})", base_reg, base_disp, index_str, outer_disp) }, - Target::IndirectMemory(value) => write!(f, "(#{:08x})", value), + Target::IndirectMemory(value, size) => if *size == Size::Word { + write!(f, "(#{:04x})", value) + } else { + write!(f, "(#{:08x})", value) + }, } } } -fn fmt_movem_mask(mask: u16) -> String { - format!("something") +fn fmt_movem_mask(mut mask: u16, target: &Target) -> String { + let mut output = vec![]; + + match target { + Target::IndirectARegDec(_) => { + for i in (0..8).rev() { + if (mask & 0x01) != 0 { + output.push(format!("%a{}", i)); + } + mask >>= 1; + } + for i in (0..8).rev() { + if (mask & 0x01) != 0 { + output.push(format!("%d{}", i)); + } + mask >>= 1; + } + }, + _ => { + for i in 0..8 { + if (mask & 0x01) != 0 { + output.push(format!("%d{}", i)); + } + mask >>= 1; + } + for i in 0..8 { + if (mask & 0x01) != 0 { + output.push(format!("%a{}", i)); + } + mask >>= 1; + } + }, + } + + output.join("/") } impl fmt::Display for Instruction { @@ -353,6 +399,8 @@ impl fmt::Display for Instruction { match self { Instruction::ABCD(src, dest) => write!(f, "abcd\t{}, {}", src, dest), Instruction::ADD(src, dest, size) => write!(f, "add{}\t{}, {}", size, src, dest), + Instruction::ADDA(target, reg, size) => write!(f, "adda{}\t{}, %a{}", size, target, reg), + Instruction::ADDX(src, dest, size) => write!(f, "addx{}\t{}, {}", size, src, dest), Instruction::AND(src, dest, size) => write!(f, "and{}\t{}, {}", size, src, dest), Instruction::ANDtoCCR(value) => write!(f, "andb\t{:02x}, %ccr", value), Instruction::ANDtoSR(value) => write!(f, "andw\t{:04x}, %sr", value), @@ -365,7 +413,15 @@ impl fmt::Display for Instruction { Instruction::BCLR(src, dest, size) => write!(f, "bclr{}\t{}, {}", size, src, dest), Instruction::BSET(src, dest, size) => write!(f, "bset{}\t{}, {}", size, src, dest), Instruction::BTST(src, dest, size) => write!(f, "btst{}\t{}, {}", size, src, dest), - //Instruction::BKPT(value), + Instruction::BFCHG(target, offset, width) => write!(f, "bfchg\t{}, {}, {}", target, offset, width), + Instruction::BFCLR(target, offset, width) => write!(f, "bfclr\t{}, {}, {}", target, offset, width), + Instruction::BFEXTS(target, offset, width, reg) => write!(f, "bfexts\t{}, {}, {}, %d{}", target, offset, width, reg), + Instruction::BFEXTU(target, offset, width, reg) => write!(f, "bfextu\t{}, {}, {}, %d{}", target, offset, width, reg), + Instruction::BFFFO(target, offset, width, reg) => write!(f, "bfffo\t{}, {}, {}, %d{}", target, offset, width, reg), + Instruction::BFINS(reg, target, offset, width) => write!(f, "bfins\t%d{}, {}, {}, {}", reg, target, offset, width), + Instruction::BFSET(target, offset, width) => write!(f, "bfset\t{}, {}, {}", target, offset, width), + Instruction::BFTST(target, offset, width) => write!(f, "bftst\t{}, {}, {}", target, offset, width), + Instruction::BKPT(value) => write!(f, "bkpt\t{}", value), Instruction::CHK(target, reg, size) => write!(f, "chk{}\t{}, %d{}", size, target, reg), Instruction::CLR(target, size) => write!(f, "clr{}\t{}", size, target), @@ -405,8 +461,8 @@ impl fmt::Display for Instruction { Direction::FromTarget => write!(f, "movec\t{}, {}", target, reg), }, Instruction::MOVEM(target, size, dir, mask) => match dir { - Direction::ToTarget => write!(f, "movem{}\t{}, {}", size, fmt_movem_mask(*mask), target), - Direction::FromTarget => write!(f, "movem{}\t{}, {}", size, target, fmt_movem_mask(*mask)), + Direction::ToTarget => write!(f, "movem{}\t{}, {}", size, fmt_movem_mask(*mask, target), target), + Direction::FromTarget => write!(f, "movem{}\t{}, {}", size, target, fmt_movem_mask(*mask, target)), }, Instruction::MOVEP(dreg, areg, offset, size, dir) => match dir { Direction::ToTarget => write!(f, "movep{}\t%d{}, ({}, %a{})", size, dreg, areg, offset), @@ -448,6 +504,8 @@ impl fmt::Display for Instruction { Instruction::Scc(cond, target) => write!(f, "s{}\t{}", cond, target), Instruction::STOP(value) => write!(f, "stop\t#{:04x}", value), Instruction::SUB(src, dest, size) => write!(f, "sub{}\t{}, {}", size, src, dest), + Instruction::SUBA(target, reg, size) => write!(f, "suba{}\t{}, %a{}", size, target, reg), + Instruction::SUBX(src, dest, size) => write!(f, "subx{}\t{}, {}", size, src, dest), Instruction::SWAP(reg) => write!(f, "swap\t%d{}", reg), Instruction::TAS(target) => write!(f, "tas\t{}", target), @@ -456,7 +514,8 @@ impl fmt::Display for Instruction { Instruction::TRAPV => write!(f, "trapv"), Instruction::UNLK(reg) => write!(f, "unlk\t%a{}", reg), - _ => write!(f, "UNIMPL"), + Instruction::UnimplementedA(ins) => write!(f, "coproc_a\t{:#06x}", ins), + Instruction::UnimplementedF(ins) => write!(f, "coproc_f\t{:#06x}", ins), } } } diff --git a/src/cpus/z80/debugger.rs b/src/cpus/z80/debugger.rs index 86becbf..045ab6a 100644 --- a/src/cpus/z80/debugger.rs +++ b/src/cpus/z80/debugger.rs @@ -1,7 +1,7 @@ use crate::error::Error; use crate::system::System; -use crate::devices::{Address, Addressable, Debuggable}; +use crate::devices::{Address, Debuggable}; use super::state::Z80; use super::decode::Z80Decoder; diff --git a/src/memory.rs b/src/memory.rs index 98eaf70..1539838 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -217,6 +217,10 @@ impl BusPort { pub fn dump_memory(&mut self, addr: Address, count: Address) { self.subdevice.borrow_mut().dump_memory(self.offset + (addr & self.address_mask), count) } + + pub fn data_width(&self) -> u8 { + self.data_width + } } impl Addressable for BusPort { diff --git a/todo.txt b/todo.txt index 6d88a4f..8d4de69 100644 --- a/todo.txt +++ b/todo.txt @@ -1,20 +1,7 @@ -* for the mixer, it might be easier to have a buffer for each source, but then you'd need to have a list of all sources, even though - each source has a copy of the mixer as well... Likely there'd be a sub object in Source which is the buffer and anything else needed - by the mixer +* when a MOVE's targets are both ARegDec, the 2 extra clocks can be combined (-2 clocks in this one case) -* I'm leaning towards having an object that data is written to by the device. The device can decide how often to update. The issue is - knowing what data to exclude or insert when mixing the incoming buffers -* Removing at a sample-level granularity would compress or lengthen the waveforms, so it would be better to mix/drop a whole chunk at - once (either predetermined by the audio system or determined by each device by the amount of samples it writes at once). The chunk - size could either be specified by the device in microseconds or something, or can be inferred by the sample_rate and the size of the - chunk. -* how do you know how big an audio frame should be? How do other emulators do audio without stretching or compressing the waveforms, and - can/should I do mixing as well, given that I have 2 sources, and at least for those two, they should be connected to the same output -* you could make the sound device be an object that is passed back to the simulation section like SimplePty. You need to either register - a callback with the frontend sound system that is called when it needs data, or you write to a shared buffer which is passed back to the - frontend when it needs it, or it has a copy it can use directly * should you rename devices.rs traits.rs? * add command line arguments to speed up or slow down either the frame rate limiter or the simulated time per frame @@ -32,6 +19,25 @@ * you could modify read()/write() in Addressable to return the number of bytes read or written for dynamic bus sizing used by the MC68020+ +Audio: + * for the mixer, it might be easier to have a buffer for each source, but then you'd need to have a list of all sources, even though + each source has a copy of the mixer as well... Likely there'd be a sub object in Source which is the buffer and anything else needed + by the mixer + + * I'm leaning towards having an object that data is written to by the device. The device can decide how often to update. The issue is + knowing what data to exclude or insert when mixing the incoming buffers + * Removing at a sample-level granularity would compress or lengthen the waveforms, so it would be better to mix/drop a whole chunk at + once (either predetermined by the audio system or determined by each device by the amount of samples it writes at once). The chunk + size could either be specified by the device in microseconds or something, or can be inferred by the sample_rate and the size of the + chunk. + + * how do you know how big an audio frame should be? How do other emulators do audio without stretching or compressing the waveforms, and + can/should I do mixing as well, given that I have 2 sources, and at least for those two, they should be connected to the same output + * you could make the sound device be an object that is passed back to the simulation section like SimplePty. You need to either register + a callback with the frontend sound system that is called when it needs data, or you write to a shared buffer which is passed back to the + frontend when it needs it, or it has a copy it can use directly + + Debugger: * how can you improve the debugger?