mirror of
https://github.com/transistorfet/moa.git
synced 2024-11-25 15:33:08 +00:00
Fixed some decoder bugs in m68k
This commit is contained in:
parent
148b4dcf2c
commit
1b6cbfa831
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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))),
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
34
todo.txt
34
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?
|
||||
|
Loading…
Reference in New Issue
Block a user