Added bit field instructions, and fixed some bugs

This commit is contained in:
transistor 2021-10-17 21:18:59 -07:00
parent 758621c410
commit 32d2d591ce
3 changed files with 188 additions and 41 deletions

View File

@ -29,6 +29,8 @@ const OPCG_RESERVED1: u8 = 0xA;
const OPCG_RESERVED2: u8 = 0xF; const OPCG_RESERVED2: u8 = 0xF;
pub type Register = u8;
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum Sign { pub enum Sign {
Signed, Signed,
@ -53,6 +55,13 @@ pub enum XRegister {
Address(u8), Address(u8),
} }
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RegOrImmediate {
DReg(u8),
Immediate(u8),
}
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum ControlRegister { pub enum ControlRegister {
VBR, VBR,
@ -88,13 +97,13 @@ pub enum Condition {
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum Target { pub enum Target {
Immediate(u32), Immediate(u32),
DirectDReg(u8), DirectDReg(Register),
DirectAReg(u8), DirectAReg(Register),
IndirectAReg(u8), IndirectAReg(Register),
IndirectARegInc(u8), IndirectARegInc(Register),
IndirectARegDec(u8), IndirectARegDec(Register),
IndirectARegOffset(u8, i32), IndirectARegOffset(Register, i32),
IndirectARegXRegOffset(u8, XRegister, i32, u8, Size), IndirectARegXRegOffset(Register, XRegister, i32, u8, Size),
IndirectMemory(u32), IndirectMemory(u32),
IndirectPCOffset(i32), IndirectPCOffset(i32),
IndirectPCXRegOffset(XRegister, i32, u8, Size), IndirectPCXRegOffset(XRegister, i32, u8, Size),
@ -116,41 +125,49 @@ pub enum Instruction {
BCLR(Target, Target, Size), BCLR(Target, Target, Size),
BSET(Target, Target, Size), BSET(Target, Target, Size),
BTST(Target, Target, Size), BTST(Target, Target, Size),
BFCHG(Target, RegOrImmediate, RegOrImmediate),
BFCLR(Target, RegOrImmediate, RegOrImmediate),
BFEXTS(Target, RegOrImmediate, RegOrImmediate, Register),
BFEXTU(Target, RegOrImmediate, RegOrImmediate, Register),
BFFFO(Target, RegOrImmediate, RegOrImmediate, Register),
BFINS(Register, Target, RegOrImmediate, RegOrImmediate),
BFSET(Target, RegOrImmediate, RegOrImmediate),
BFTST(Target, RegOrImmediate, RegOrImmediate),
BKPT(u8), BKPT(u8),
CHK(Target, u8, Size), CHK(Target, Register, Size),
CLR(Target, Size), CLR(Target, Size),
CMP(Target, Target, Size), CMP(Target, Target, Size),
CMPA(Target, u8, Size), CMPA(Target, Register, Size),
DBcc(Condition, u8, i16), DBcc(Condition, Register, i16),
DIV(Target, Target, Size, Sign), DIV(Target, Target, Size, Sign),
EOR(Target, Target, Size), EOR(Target, Target, Size),
EORtoCCR(u8), EORtoCCR(u8),
EORtoSR(u16), EORtoSR(u16),
EXG(Target, Target), EXG(Target, Target),
EXT(u8, Size, Size), EXT(Register, Size, Size),
ILLEGAL, ILLEGAL,
JMP(Target), JMP(Target),
JSR(Target), JSR(Target),
LEA(Target, u8), LEA(Target, Register),
LINK(u8, i16), LINK(Register, i16),
LSd(Target, Target, Size, ShiftDirection), LSd(Target, Target, Size, ShiftDirection),
MOVE(Target, Target, Size), MOVE(Target, Target, Size),
MOVEA(Target, u8, Size), MOVEA(Target, Register, Size),
MOVEfromSR(Target), MOVEfromSR(Target),
MOVEtoSR(Target), MOVEtoSR(Target),
MOVEfromCCR(Target), MOVEfromCCR(Target),
MOVEtoCCR(Target), MOVEtoCCR(Target),
MOVEC(Target, ControlRegister, Direction), MOVEC(Target, ControlRegister, Direction),
MOVEM(Target, Size, Direction, u16), MOVEM(Target, Size, Direction, u16),
MOVEP(u8, Target, Size, Direction), MOVEP(Register, Target, Size, Direction),
MOVEQ(u8, u8), MOVEQ(u8, Register),
MOVEUSP(Target, Direction), MOVEUSP(Target, Direction),
MUL(Target, Target, Size, Sign), MUL(Target, Target, Size, Sign),
@ -179,14 +196,14 @@ pub enum Instruction {
Scc(Condition, Target), Scc(Condition, Target),
STOP(u16), STOP(u16),
SUB(Target, Target, Size), SUB(Target, Target, Size),
SWAP(u8), SWAP(Register),
TAS(Target), TAS(Target),
TST(Target, Size), TST(Target, Size),
TRAP(u8), TRAP(u8),
TRAPV, TRAPV,
UNLK(u8), UNLK(Register),
} }
@ -686,14 +703,44 @@ impl M68kDecoder {
}, },
None => { None => {
let target = self.decode_lower_effective_address(memory, ins, Some(Size::Word))?; let target = self.decode_lower_effective_address(memory, ins, Some(Size::Word))?;
let count = Target::Immediate(1);
match (ins & 0x0600) >> 9 { let count = Target::Immediate(1);
0b00 => Ok(Instruction::ASd(count, target, Size::Word, dir)), if (ins & 0x800) == 0 {
0b01 => Ok(Instruction::LSd(count, target, Size::Word, dir)), match (ins & 0x0600) >> 9 {
0b10 => Ok(Instruction::ROXd(count, target, Size::Word, dir)), 0b00 => Ok(Instruction::ASd(count, target, Size::Word, dir)),
0b11 => Ok(Instruction::ROd(count, target, Size::Word, dir)), 0b01 => Ok(Instruction::LSd(count, target, Size::Word, dir)),
_ => return Err(Error::processor(Exceptions::IllegalInstruction as u32)), 0b10 => Ok(Instruction::ROXd(count, target, Size::Word, dir)),
0b11 => Ok(Instruction::ROd(count, target, Size::Word, dir)),
_ => return Err(Error::processor(Exceptions::IllegalInstruction as u32)),
}
} else if self.cputype > M68kType::MC68020 {
// Bitfield instructions (MC68020+)
let ext = self.read_instruction_word(memory)?;
let reg = ((ext & 0x7000) >> 12) as u8;
let offset = match (ext & 0x0800) == 0 {
true => RegOrImmediate::Immediate(((ext & 0x07C0) >> 6) as u8),
false => RegOrImmediate::DReg(((ext & 0x01C0) >> 6) as u8),
};
let width = match (ext & 0x0020) == 0 {
true => RegOrImmediate::Immediate((ext & 0x001F) as u8),
false => RegOrImmediate::DReg((ext & 0x0007) as u8),
};
match (ins & 0x0700) >> 8 {
0b010 => Ok(Instruction::BFCHG(target, offset, width)),
0b100 => Ok(Instruction::BFCLR(target, offset, width)),
0b011 => Ok(Instruction::BFEXTS(target, offset, width, reg)),
0b001 => Ok(Instruction::BFEXTU(target, offset, width, reg)),
0b101 => Ok(Instruction::BFFFO(target, offset, width, reg)),
0b111 => Ok(Instruction::BFINS(reg, target, offset, width)),
0b110 => Ok(Instruction::BFSET(target, offset, width)),
0b000 => Ok(Instruction::BFTST(target, offset, width)),
_ => return Err(Error::processor(Exceptions::IllegalInstruction as u32)),
}
} else {
return Err(Error::processor(Exceptions::IllegalInstruction as u32));
} }
}, },
} }
@ -732,7 +779,7 @@ impl M68kDecoder {
let xreg = if (brief_extension & 0x8000) == 0 { XRegister::Data(xreg_num) } else { XRegister::Address(xreg_num) }; let xreg = if (brief_extension & 0x8000) == 0 { XRegister::Data(xreg_num) } else { XRegister::Address(xreg_num) };
let size = if (brief_extension & 0x0800) == 0 { Size::Word } else { Size::Long }; let size = if (brief_extension & 0x0800) == 0 { Size::Word } else { Size::Long };
let scale = ((brief_extension & 0x0600) >> 9) as u8; let scale = ((brief_extension & 0x0600) >> 9) as u8;
let use_full = (brief_extension & 0x0100) == 1; let use_full = (brief_extension & 0x0100) != 0;
if !use_full { if !use_full {
let displacement = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte); let displacement = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte);
@ -1062,7 +1109,10 @@ impl fmt::Display for Instruction {
Direction::ToTarget => write!(f, "movem{}\t{}, {}", size, fmt_movem_mask(*mask), target), 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::FromTarget => write!(f, "movem{}\t{}, {}", size, target, fmt_movem_mask(*mask)),
}, },
//Instruction::MOVEP(reg, target, size, dir), Instruction::MOVEP(reg, target, size, dir) => match dir {
Direction::ToTarget => write!(f, "movep{}\t%d{}, {}", size, reg, target),
Direction::FromTarget => write!(f, "movep{}\t{}, %d{}", size, target, reg),
},
Instruction::MOVEQ(value, reg) => write!(f, "moveq\t#{:02x}, %d{}", value, reg), Instruction::MOVEQ(value, reg) => write!(f, "moveq\t#{:02x}, %d{}", value, reg),
Instruction::MOVEUSP(target, dir) => match dir { Instruction::MOVEUSP(target, dir) => match dir {
Direction::ToTarget => write!(f, "movel\t%usp, {}", target), Direction::ToTarget => write!(f, "movel\t%usp, {}", target),

View File

@ -12,7 +12,9 @@ use super::decode::{
Condition, Condition,
ShiftDirection, ShiftDirection,
ControlRegister, ControlRegister,
Register,
XRegister, XRegister,
RegOrImmediate,
sign_extend_to_long sign_extend_to_long
}; };
@ -78,7 +80,9 @@ impl M68k {
Status::Running => { Status::Running => {
match self.cycle_one(system) { match self.cycle_one(system) {
Ok(()) => Ok(()), Ok(()) => Ok(()),
Err(Error { err: ErrorType::Processor, native, .. }) => { //Err(Error { err: ErrorType::Processor, native, .. }) => {
// TODO temporary: we are passing illegal instructions upward in order to fix them
Err(Error { err: ErrorType::Processor, native, .. }) if native != Exceptions::IllegalInstruction as u32 => {
self.exception(system, native as u8)?; self.exception(system, native as u8)?;
Ok(()) Ok(())
}, },
@ -152,8 +156,6 @@ impl M68k {
if self.debugger.use_tracing { if self.debugger.use_tracing {
self.decoder.dump_decoded(system); self.decoder.dump_decoded(system);
// TODO for debugging temporarily
self.dump_state(system);
} }
if self.debugger.use_debugger { if self.debugger.use_debugger {
@ -249,6 +251,65 @@ impl M68k {
value = value | mask; value = value | mask;
self.set_target_value(system, target, value, size)?; self.set_target_value(system, target, value, size)?;
}, },
Instruction::BFCHG(target, offset, width) => {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(system, target, Size::Long)?;
let field = value & mask;
self.set_bit_field_test_flags(field, get_bit_field_msb(offset));
self.set_target_value(system, target, (value & !mask) | (!field & mask), Size::Long)?;
},
Instruction::BFCLR(target, offset, width) => {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(system, target, Size::Long)?;
let field = value & mask;
self.set_bit_field_test_flags(field, get_bit_field_msb(offset));
self.set_target_value(system, target, value & !mask, Size::Long)?;
},
Instruction::BFEXTS(target, offset, width, reg) => {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(system, target, Size::Long)?;
let field = value & mask;
self.set_bit_field_test_flags(field, get_bit_field_msb(offset));
let right_offset = 32 - offset - width;
let mut ext = 0;
for i in 0..(offset + right_offset) {
ext = (ext >> 1) | 0x80000000;
}
self.state.d_reg[reg as usize] = (field >> right_offset) | ext;
},
Instruction::BFEXTU(target, offset, width, reg) => {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(system, target, Size::Long)?;
let field = value & mask;
self.set_bit_field_test_flags(field, get_bit_field_msb(offset));
self.state.d_reg[reg as usize] = field >> (32 - offset - width);
},
//Instruction::BFFFO(target, offset, width, reg) => {
//},
//Instruction::BFINS(reg, target, offset, width) => {
//},
Instruction::BFSET(target, offset, width) => {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(system, target, Size::Long)?;
let field = value & mask;
self.set_bit_field_test_flags(field, get_bit_field_msb(offset));
self.set_target_value(system, target, value | mask, Size::Long)?;
},
Instruction::BFTST(target, offset, width) => {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(system, target, Size::Long)?;
let field = value & mask;
self.set_bit_field_test_flags(field, get_bit_field_msb(offset));
},
//Instruction::BKPT(u8) => {
//},
Instruction::CLR(target, size) => { Instruction::CLR(target, size) => {
self.set_target_value(system, target, 0, size)?; self.set_target_value(system, target, 0, size)?;
// Clear flags except Zero flag // Clear flags except Zero flag
@ -722,6 +783,22 @@ impl M68k {
Ok(addr) Ok(addr)
} }
pub fn get_bit_field_args(&self, offset: RegOrImmediate, width: RegOrImmediate) -> (u32, u32) {
let offset = self.get_reg_or_immediate(offset);
let mut width = self.get_reg_or_immediate(width) % 32;
if width == 0 {
width = 32;
}
(offset, width)
}
fn get_reg_or_immediate(&self, value: RegOrImmediate) -> u32 {
match value {
RegOrImmediate::DReg(reg) => self.state.d_reg[reg as usize],
RegOrImmediate::Immediate(value) => value as u32,
}
}
fn get_control_reg_mut(&mut self, control_reg: ControlRegister) -> &mut u32 { fn get_control_reg_mut(&mut self, control_reg: ControlRegister) -> &mut u32 {
match control_reg { match control_reg {
ControlRegister::VBR => &mut self.state.vbr, ControlRegister::VBR => &mut self.state.vbr,
@ -734,7 +811,7 @@ impl M68k {
} }
#[inline(always)] #[inline(always)]
fn get_a_reg_mut(&mut self, reg: u8) -> &mut u32 { fn get_a_reg_mut(&mut self, reg: Register) -> &mut u32 {
if reg == 7 { if reg == 7 {
if self.is_supervisor() { &mut self.state.msp } else { &mut self.state.usp } if self.is_supervisor() { &mut self.state.msp } else { &mut self.state.usp }
} else { } else {
@ -809,6 +886,16 @@ impl M68k {
mask mask
} }
fn set_bit_field_test_flags(&mut self, field: u32, msb_mask: u32) {
let mut flags = 0x0000;
if (field & msb_mask) != 0 {
flags |= Flags::Negative as u16;
}
if field == 0 {
flags |= Flags::Zero as u16;
}
self.state.sr = (self.state.sr & 0xFFF0) | flags;
}
fn get_current_condition(&self, cond: Condition) -> bool { fn get_current_condition(&self, cond: Condition) -> bool {
match cond { match cond {
@ -959,3 +1046,16 @@ fn get_msb_mask(value: u32, size: Size) -> u32 {
} }
} }
fn get_bit_field_mask(offset: u32, width: u32) -> u32 {
let mut mask = 0;
for i in 0..width {
mask = (mask >> 1) | 0x80000000;
}
mask >> offset
}
fn get_bit_field_msb(offset: u32) -> u32 {
0x80000000 >> offset
}

View File

@ -1,22 +1,19 @@
* can you make Bus be an AddressableDeviceBox so that the CPU can have a ref to it instead of calling system.get_bus() * can you eventually make the system connections all configurable via a config file?
the alternative is having system.get_bus() take an argument or something to figure out what bus to return of multiple, for multibus systems
* can you eventually make this all configurable via a config file?
* test using mpsc to pass messages with the tty IO thread, and test if it's slower than what you have now * test using mpsc to pass messages with the tty IO thread, and test if it's slower than what you have now
* make it possible to break out of the current execution, into the debugger, by using a certain keystroke * make it possible to break out of the current execution, into the debugger, by using a certain keystroke
* add support for MC68020+ indexing modes
* add support for MMU * add support for MC68020+ indexing modes, implement the full extension word for MC68020+
* add support for FPU
* make tests for each instruction * make tests for each instruction
* unimplemented: ABCD, ADDX, BKPT, CHK, EXG, ILLEGAL, MOVEfromCCR, MOVEP, RTR, RTD, SBCD, SUBX * unimplemented: ABCD, ADDX, BFFFO, BFINS, BKPT, CHK, EXG, ILLEGAL, MOVEfromCCR, MOVEP, RTR, RTD, SBCD, SUBX
* undecoded: ADDX, SUBX * undecoded: ADDX, SUBX
* modify execution for >=MC68020: DIVSL, DIVUL, LINK, MOVEM, MULSL, MULUL, RTM, TRAPcc, UNPK * modify execution for >=MC68020: DIVSL, DIVUL, LINK, MOVEM, MULSL, MULUL, RTM, TRAPcc, UNPK
* implement the full extension word for MC68020+ * >=MC68020 undecoded & unimplemented: CALLM, CAS, CAS2, CHK2, CMP2, PACK
* >=MC68020 instructions: BFCHG, BFCLR, BFEXTS, BFEXTU, BFFFO, BFINS, BFSET, BFTST, CALLM, CAS, CAS2, CHK2, CMP2, PACK * add support for MMU
* add support for FPU
* Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc * Coprocessor instructions: cpBcc, cpDBcc, cpGEN, cpScc, cpTRAPcc