commit 6a4f53ca2b0abf49d71c7b3ad6f56e239878e0bb Author: transistor Date: Tue Sep 28 16:09:38 2021 -0700 Initial start with some structure for instruction decoding diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f9f79b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +Cargo.lock +.*.sw? +/target +*.vim diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e22f99c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "moa" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..69bc4f4 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ + +Moa +=== + +###### *Started September 26, 2021* + +An emulator for m68k CPUs and devices + diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..fcb4ac0 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,39 @@ + +#[derive(Debug)] +pub enum ErrorType { + Emulator, + Processor, + Internal, +} + +#[derive(Debug)] +pub struct Error { + pub err: ErrorType, + pub native: u32, + pub msg: String, +} + +impl Error { + pub fn new(msg: &str) -> Error { + Error { + err: ErrorType::Emulator, + native: 0, + msg: msg.to_string(), + } + } + + pub fn processor(native: u32) -> Error { + Error { + err: ErrorType::Processor, + native, + msg: "".to_string(), + } + } +} + +macro_rules! debug { + ($($arg:tt)*) => ({ + println!($($arg)*); + }) +} + diff --git a/src/m68k.rs b/src/m68k.rs new file mode 100644 index 0000000..3f4f749 --- /dev/null +++ b/src/m68k.rs @@ -0,0 +1,365 @@ + +use crate::error::Error; +use crate::memory::{Address, AddressSpace}; + +pub trait Processor { + fn reset(); + fn step(); +} + + + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum State { + Init, + Running, + Halted, +} + +pub struct MC68010 { + pub state: State, + + pub pc: u32, + pub msp: u32, + pub usp: u32, + pub flags: u16, + pub d_reg: [u32; 8], + pub a_reg: [u32; 8], + + pub vbr: u32, +} + +const FLAGS_ON_RESET: u16 = 0x2700; + +const FLAGS_SUPERVISOR: u16 = 0x2000; + +const ERR_BUS_ERROR: u32 = 2; +const ERR_ADDRESS_ERROR: u32 = 3; +const ERR_ILLEGAL_INSTRUCTION: u32 = 4; + +#[derive(Copy, Clone, Debug, PartialEq)] +enum Sign { + Signed, + Unsigned, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum Size { + Byte, + Word, + Long, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum Condition { + CarryClear, + CarrySet, + Equal, + NotEqual, + GreaterThanOrEqual, + GreaterThan, + LessThanOrEqual, + LessThan, + Minus, + Plus, + OverflowClear, + OverflowSet, +} + +#[derive(Clone, Debug, PartialEq)] +enum Target { + Immediate(u32), + DirectDReg(u8), + DirectAReg(u8), + IndirectAReg(u8), + IndirectARegInc(u8), + IndirectARegDec(u8), + IndirectARegOffset(u8, u16), + IndirectARegDRegOffset(u8, u8, u16), + IndirectMemory(u32), + IndirectPCOffset(u16), + IndirectPCRegOffset(u8, u16), +} + +#[derive(Clone, Debug, PartialEq)] +enum Instruction { + ADD(Target, Target, Size), + AND(Target, Target, Size), + ANDCCR(u8), + ANDSR(u16), + + Bcc(Condition, u16), + BRA(u16), + BSR(u16), + + CLR(Target, Size), + CMP(Target, Target, Size), + + DBcc(Condition, u16), + DIV(Target, Target, Size, Sign), + + LEA(Target, u8), + JSR(Target), + JMP(Target), +} + + +const OPCG_BIT_OPS: u8 = 0x0; +const OPCG_MOVE_BYTE: u8 = 0x1; +const OPCG_MOVE_WORD: u8 = 0x2; +const OPCG_MOVE_LONG: u8 = 0x3; +const OPCG_MISC: u8 = 0x04; +const OPCG_ADDQ_SUBQ: u8 = 0x5; +const OPCG_BRANCH: u8 = 0x6; +const OPCG_MOVEQ: u8 = 0x7; +const OPCG_DIV_OR: u8 = 0x8; +const OPCG_SUB: u8 = 0x9; +const OPCG_RESERVED1: u8 = 0xA; +const OPCG_CMP_EOR: u8 = 0xB; +const OPCG_MUL_EXCH: u8 = 0xC; +const OPCG_ADD: u8 = 0xD; +const OPCG_SHIFT: u8 = 0xE; +const OPCG_RESERVED2: u8 = 0xF; + + +impl MC68010 { + pub fn new() -> MC68010 { + MC68010 { + state: State::Init, + pc: 0, + msp: 0, + usp: 0, + flags: FLAGS_ON_RESET, + d_reg: [0; 8], + a_reg: [0; 8], + vbr: 0, + } + } + + pub fn reset(&mut self) { + self.state = State::Init; + self.pc = 0; + self.msp = 0; + self.usp = 0; + self.flags = FLAGS_ON_RESET; + self.d_reg = [0; 8]; + self.a_reg = [0; 8]; + self.vbr = 0; + } + + pub fn is_running(&self) -> bool { + self.state != State::Halted + } + + + pub fn init(&mut self, space: &mut AddressSpace) -> Result<(), Error> { + println!("Initializing CPU"); + + self.msp = space.read_beu32(0)?; + self.pc = space.read_beu32(4)?; + self.state = State::Running; + + Ok(()) + } + + pub fn step(&mut self, space: &mut AddressSpace) -> Result<(), Error> { + match self.state { + State::Init => self.init(space), + State::Halted => Err(Error::new("CPU halted")), + State::Running => self.execute_one(space), + } + } + + fn is_supervisor(&self) -> bool { + self.flags & FLAGS_SUPERVISOR != 0 + } + + fn read_instruction_word(&mut self, space: &mut AddressSpace) -> Result { + let word = space.read_beu16(self.pc as Address)?; + println!("{:08x} {:04x?}", self.pc, word); + self.pc += 2; + Ok(word) + } + + fn read_instruction_long(&mut self, space: &mut AddressSpace) -> Result { + let word = space.read_beu32(self.pc as Address)?; + println!("{:08x} {:08x?}", self.pc, word); + self.pc += 4; + Ok(word) + } + + fn push_long(&mut self, space: &mut AddressSpace, value: u32) -> Result<(), Error> { + let reg = if self.is_supervisor() { &mut self.msp } else { &mut self.usp }; + *reg -= 4; + space.write_beu32(*reg as Address, value) + } + + fn execute_one(&mut self, space: &mut AddressSpace) -> Result<(), Error> { + let ins = self.decode_one(space)?; + + match ins { + Instruction::JSR(target) => { + self.push_long(space, self.pc)?; + self.pc = self.get_target_value(space, target)?; + }, + _ => panic!(""), + } + + Ok(()) + } + + fn get_target_value(&mut self, space: &mut AddressSpace, target: Target) -> Result { + match target { + Target::Immediate(value) => Ok(value), + Target::DirectDReg(reg) => Ok(self.d_reg[reg as usize]), + _ => Err(Error::new(&format!("Unimplemented addressing target: {:?}", target))), + } + } + + fn decode_one(&mut self, space: &mut AddressSpace) -> Result { + let ins = self.read_instruction_word(space)?; + + match ((ins & 0xF000) >> 12) as u8 { + OPCG_BIT_OPS => { +panic!(""); + }, + OPCG_MOVE_BYTE => { + let data = self.read_instruction_word(space)?; + +panic!(""); + }, + OPCG_MOVE_WORD => { + let data = self.read_instruction_word(space)?; + +panic!(""); + }, + OPCG_MOVE_LONG => { + let data = self.read_instruction_long(space)?; + +panic!(""); + }, + OPCG_MISC => { + if (ins & 0b111000000) == 0b111000000 { + // LEA Instruction + + debug!("LEA"); + let src = self.decode_lower_effective_address(space, ins)?; + let dest = get_high_reg(ins); + Ok(Instruction::LEA(src, dest)) + + } else if (ins & 0b101000000) == 0b100000000 { + // CHK Instruction +panic!(""); + } else if (ins & 0b101110000000) == 0b100010000000 { + // MOVEM Instruction +panic!(""); + } else if (ins & 0b111110000000) == 0b111010000000 { + // JMP/JSR Instruction + let target = self.decode_lower_effective_address(space, ins)?; + if (ins & 0b01000000) == 0 { + Ok(Instruction::JSR(target)) + } else { + Ok(Instruction::JMP(target)) + } + + } else { + +panic!(""); + } + }, + OPCG_ADDQ_SUBQ => { + +panic!(""); + }, + OPCG_BRANCH => { + +panic!(""); + }, + OPCG_MOVEQ => { + +panic!(""); + }, + OPCG_DIV_OR => { + +panic!(""); + }, + OPCG_SUB => { + +panic!(""); + }, + OPCG_CMP_EOR => { + +panic!(""); + }, + OPCG_MUL_EXCH => { + +panic!(""); + }, + OPCG_ADD => { + +panic!(""); + }, + OPCG_SHIFT => { + +panic!(""); + }, + _ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)), + } + } + + fn decode_lower_effective_address(&mut self, space: &mut AddressSpace, ins: u16) -> Result { + let reg = get_low_reg(ins); + let mode = get_mode(ins); + self.get_mode_as_target(space, mode, reg) + } + + fn get_mode_as_target(&mut self, space: &mut AddressSpace, mode: u8, reg: u8) -> Result { + let value = match mode { + 0b010 => Target::IndirectAReg(reg), + 0b101 => { + let d16 = self.read_instruction_word(space)?; + Target::IndirectARegOffset(reg, d16) + }, + 0b111 => { + match reg { + 0b000 => { + let value = self.read_instruction_word(space)? as u32; + Target::IndirectMemory(value) + }, + 0b001 => { + let value = self.read_instruction_long(space)?; + Target::IndirectMemory(value) + }, + 0b010 => { + let d16 = self.read_instruction_word(space)?; + Target::IndirectPCOffset(d16) + }, + _ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)), + } + }, + _ => return Err(Error::processor(ERR_ILLEGAL_INSTRUCTION)), + }; + Ok(value) + } +} + +#[inline(always)] +fn get_high_reg(ins: u16) -> u8 { + ((ins & 0x0D00) >> 9) as u8 +} + +#[inline(always)] +fn get_low_reg(ins: u16) -> u8 { + (ins & 0x0007) as u8 +} + +#[inline(always)] +fn get_mode(ins: u16) -> u8 { + ((ins & 0x0038) >> 3) as u8 +} + +/* +impl Processor for MC68010 { + +} +*/ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4e08f22 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,24 @@ + +#[macro_use] +mod error; +mod memory; +mod m68k; + +use crate::memory::{AddressSpace, Segment}; +use crate::m68k::MC68010; + +fn main() { + let mut space = AddressSpace::new(); + let monitor = Segment::load(0x00000000, "monitor.bin").unwrap(); + for byte in monitor.contents.iter() { + print!("{:02x} ", byte); + } + space.insert(monitor); + + + let mut cpu = MC68010::new(); + while cpu.is_running() { + cpu.step(&mut space).unwrap(); + } +} + diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 0000000..5393bb6 --- /dev/null +++ b/src/memory.rs @@ -0,0 +1,133 @@ + +use std::fs; +use std::slice::Iter; + +use crate::error::Error; + + +pub type Address = u64; + +trait Addressable { + fn read(&self, addr: Address) -> Iter; + fn write(&mut self, addr: Address, data: &[u8]); +} + + +pub struct Segment { + pub base: Address, + pub contents: Vec, +} + +impl Segment { + pub fn new(base: Address, contents: Vec) -> Segment { + Segment { + base, + contents, + } + } + + pub fn load(base: Address, filename: &str) -> Result { + match fs::read(filename) { + Ok(contents) => Ok(Segment::new(base, contents)), + Err(_) => Err(Error::new(&format!("Error reading contents of {}", filename))), + } + } +} + +impl Addressable for Segment { + fn read(&self, addr: Address) -> Iter { + self.contents[(addr - self.base) as usize .. ].iter() + } + + fn write(&mut self, addr: Address, data: &[u8]) { + for byte in data { + self.contents[(addr - self.base) as usize] = *byte; + } + } +} + + + +pub struct AddressSpace { + pub segments: Vec, +} + +impl AddressSpace { + pub fn new() -> AddressSpace { + AddressSpace { + segments: vec!(), + } + } + + pub fn insert(&mut self, seg: Segment) { + for i in 0..self.segments.len() { + if self.segments[i].base > seg.base { + self.segments.insert(i, seg); + return; + } + } + self.segments.insert(0, seg); + } + + pub fn get_segment(&self, addr: Address) -> Result<&Segment, Error> { + for i in 0..self.segments.len() { + if addr >= self.segments[i].base && addr <= (self.segments[i].base + self.segments[i].contents.len() as Address) { + return Ok(&self.segments[i]); + } + } + return Err(Error::new("No segment found")); + } + + pub fn get_segment_mut(&mut self, addr: Address) -> Result<&mut Segment, Error> { + for i in 0..self.segments.len() { + if addr >= self.segments[i].base && addr <= (self.segments[i].base + self.segments[i].contents.len() as Address) { + return Ok(&mut self.segments[i]); + } + } + return Err(Error::new("No segment found")); + } + + + pub fn read_beu16(&self, addr: Address) -> Result { + let seg = self.get_segment(addr)?; + Ok(read_beu16(seg.read(addr))) + } + + pub fn read_beu32(&self, addr: Address) -> Result { + let seg = self.get_segment(addr)?; + Ok(read_beu32(seg.read(addr))) + } + + pub fn write_beu16(&mut self, addr: Address, value: u16) -> Result<(), Error> { + let seg = self.get_segment_mut(addr)?; + let data = [ + (value >> 8) as u8, + value as u8, + ]; + Ok(seg.write(addr, &data)) + } + + pub fn write_beu32(&mut self, addr: Address, value: u32) -> Result<(), Error> { + let seg = self.get_segment_mut(addr)?; + let data = [ + (value >> 24) as u8, + (value >> 16) as u8, + (value >> 8) as u8, + value as u8, + ]; + Ok(seg.write(addr, &data)) + } +} + +pub fn read_beu16(mut iter: Iter) -> u16 { + (*iter.next().unwrap() as u16) << 8 | + (*iter.next().unwrap() as u16) +} + +pub fn read_beu32(mut iter: Iter) -> u32 { + (*iter.next().unwrap() as u32) << 24 | + (*iter.next().unwrap() as u32) << 16 | + (*iter.next().unwrap() as u32) << 8 | + (*iter.next().unwrap() as u32) +} +