Fixed some decoder bugs in m68k

This commit is contained in:
transistor 2021-12-14 16:06:34 -08:00
parent 148b4dcf2c
commit 1b6cbfa831
6 changed files with 112 additions and 40 deletions

View File

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

View File

@ -29,7 +29,7 @@ use super::state::{M68k, M68kType, Status, Flags, Exceptions, InterruptPriority}
impl Steppable for M68k {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
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))),

View File

@ -91,7 +91,7 @@ pub enum Target {
IndirectRegOffset(BaseRegister, Option<IndexRegister>, i32),
IndirectMemoryPreindexed(BaseRegister, Option<IndexRegister>, i32, i32),
IndirectMemoryPostindexed(BaseRegister, Option<IndexRegister>, 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),
}
}
}

View File

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

View File

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

View File

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