commit dcbc51448588f9b9b14241aa228115ed6678246b Author: Kevin Hamacher Date: Sun Feb 4 19:44:10 2018 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eccd7b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target/ +**/*.rs.bk diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f003cac --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "avremu" +version = "0.1.0" +authors = ["Kevin Hamacher "] + +[dependencies] diff --git a/src/cpu.rs b/src/cpu.rs new file mode 100644 index 0000000..d1304ee --- /dev/null +++ b/src/cpu.rs @@ -0,0 +1,70 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +use decoder; +use decoder::Instruction; + +#[derive(Debug)] +pub enum CPUError { + UnimplementedInstruction, + OutOfBoundsException, + DecodingError(decoder::DecodingError), +} + +// CPU +#[derive(Debug)] +pub struct CPU { + // General purpose registers + registers: [u8; 32], + + // PC + pc: u32, + + // For now have the stack pointer in here as well. In reality it is part of + // the IO registers + sp: u16, + + // The same is true for the status register + sreg: u8, +} + +impl CPU { + pub fn new() -> Self { + CPU { + registers: [0u8; 32], + pc: 0, // Reset vector + sp: 0xFFFF, // Random value, uninitialized + sreg: 0, // Uninitialized as well + } + } + + // Returns # of ticks the executed instruction took + pub fn step(&mut self, rom: &mut [u8], ram: &mut [u8]) -> Result { + // Instruction fetch + if (self.pc as usize) * 2 >= rom.len() { + return Err(CPUError::OutOfBoundsException); + } + + let ins = match decoder::decode(&rom[(self.pc as usize) * 2..]) { + Ok(v) => v, + Err(e) => return Err(CPUError::DecodingError(e)), + }; + print!("CPU: pc={:06X} Fetch: {:?} -> ", self.pc, ins); + + self.pc += (ins.size() / 2) as u32; + + // Instruction execute + match ins { + Instruction::JMP(v) => self.pc = v, + Instruction::CLR(ref r) => self.registers[r.as_usize()] = 0, + Instruction::EOR(ref d, ref r) => + self.registers[d.as_usize()] ^= self.registers[r.as_usize()], + Instruction::LDI(ref r, v) => self.registers[r.as_usize()] = v, + Instruction::SER(ref r) => self.registers[r.as_usize()] = 0xFF, + Instruction::RJMP(v) => self.pc = self.pc.wrapping_add(v as _), + _ => return Err(CPUError::UnimplementedInstruction) + } + + Ok(ins.cycles()) + } +} diff --git a/src/decoder.rs b/src/decoder.rs new file mode 100644 index 0000000..8c0e8c7 --- /dev/null +++ b/src/decoder.rs @@ -0,0 +1,524 @@ +use regs::{GeneralPurposeRegister, GeneralPurposeRegisterPair, IORegister, + StatusFlag, PC}; + +#[derive(Debug)] +pub enum DecodingError { + UndefinedInstruction(u16), + TruncatedInstruction, +} + +#[derive(Debug)] +pub enum IncrementMode { + None, // [Z] + PreDecrement, // [-Z] + PostIncrement, // [Z+] + ConstantOffset(u8), // e.g. [Z+q] +} + +#[derive(Debug)] +pub enum Instruction { + ADC(GeneralPurposeRegister, GeneralPurposeRegister), + ADD(GeneralPurposeRegister, GeneralPurposeRegister), + ADIW(GeneralPurposeRegisterPair, u16), + AND(GeneralPurposeRegister, GeneralPurposeRegister), + ANDI(GeneralPurposeRegister, u8), + ASR(GeneralPurposeRegister), + BLD(GeneralPurposeRegister, u8), + BREAK, + BR_IF(i8, StatusFlag, bool), + BST(GeneralPurposeRegister, u8), + CALL(PC), + CBI(IORegister, u8), + CLR_FLAG(StatusFlag), + CLR(GeneralPurposeRegister), // TODO: Find CLR flag thingy + COM(GeneralPurposeRegister), + CP(GeneralPurposeRegister, GeneralPurposeRegister), + CPC(GeneralPurposeRegister, GeneralPurposeRegister), + CPI(GeneralPurposeRegister, u8), + CPSE(GeneralPurposeRegister, GeneralPurposeRegister), + DEC(GeneralPurposeRegister), + EICALL, + EIJMP, + ELPM(GeneralPurposeRegister, IncrementMode), // Only supports PostIncrement + EOR(GeneralPurposeRegister, GeneralPurposeRegister), + FMUL(GeneralPurposeRegister, GeneralPurposeRegister), + FMULS(GeneralPurposeRegister, GeneralPurposeRegister), + FMULSU(GeneralPurposeRegister, GeneralPurposeRegister), + ICALL, + IJMP, + IN(GeneralPurposeRegister, IORegister), + INC(GeneralPurposeRegister), + JMP(PC), + LAC(GeneralPurposeRegister), + LAS(GeneralPurposeRegister), + LAT(GeneralPurposeRegister), + LD(GeneralPurposeRegisterPair, GeneralPurposeRegister, IncrementMode), + LDI(GeneralPurposeRegister, u8), + LDS8(GeneralPurposeRegister, u8), // Two variants, with u8 and u16 + LDS16(GeneralPurposeRegister, u16), + LPM(GeneralPurposeRegister, IncrementMode), // Only supports PostIncrement + LSL(GeneralPurposeRegister), + LSR(GeneralPurposeRegister), + MOV(GeneralPurposeRegister, GeneralPurposeRegister), + MOVW(GeneralPurposeRegisterPair, GeneralPurposeRegisterPair), + MUL(GeneralPurposeRegister, GeneralPurposeRegister), + MULS(GeneralPurposeRegister, GeneralPurposeRegister), + MULSU(GeneralPurposeRegister, GeneralPurposeRegister), + NEG(GeneralPurposeRegister), + NOP, + OR(GeneralPurposeRegister, GeneralPurposeRegister), + ORI(GeneralPurposeRegister, u8), + OUT(IORegister, GeneralPurposeRegister), + POP(GeneralPurposeRegister), + PUSH(GeneralPurposeRegister), + RCALL(i16), // These are relative and have only 12 bits. + RET, + RETI, + RJMP(i16), // These are relative and have only 12 bits. + ROL(GeneralPurposeRegister), + ROR(GeneralPurposeRegister), + SBC(GeneralPurposeRegister, GeneralPurposeRegister), + SBCI(GeneralPurposeRegister, u8), + SBI(IORegister, u8), + SBIC(IORegister, u8), + SBIS(IORegister, u8), + SBIW(GeneralPurposeRegisterPair, u8), + SBR(GeneralPurposeRegister, u8), + SBRC(GeneralPurposeRegister, u8), + SBRS(GeneralPurposeRegister, u8), + SER(GeneralPurposeRegister), + SET_FLAG(StatusFlag), // TODO how is it really called? (CLR) + SLEEP, + SPM(IncrementMode), // Only supports PostIncrement + ST(GeneralPurposeRegisterPair, GeneralPurposeRegister, IncrementMode), + STS8(u8, GeneralPurposeRegister), // Two variants, u8/u16 + STS16(u16, GeneralPurposeRegister), + SUB(GeneralPurposeRegister, GeneralPurposeRegister), + SUBI(GeneralPurposeRegister, u8), + SWAP(GeneralPurposeRegister), + TST(GeneralPurposeRegister), + WDR, + XCH(GeneralPurposeRegister), +} + +impl Instruction { + // TODO: There are more + // LDS32 STS32 + pub fn size(&self) -> usize { + match *self { + Instruction::JMP(_) | Instruction::CALL(_) => 4, + Instruction::STS16(_, _) | Instruction::LDS16(_, _) => 4, + _ => 2 + } + } + + pub fn cycles(&self) -> usize { + // TODO: Implement this correctly. + 2 + } +} + +pub fn decode(data: &[u8]) -> Result { + if data.len() < 2 { + return Err(DecodingError::TruncatedInstruction); + } + // Try to match 2b instructions without any parameters first. + let v: u16 = ((data[1] as u16) << 8) | (data[0] as u16); + + // Load second u16 as well if possible + let v2: Option = if data.len() >= 4 { + Some(((data[3] as u16) << 8) | (data[2] as u16)) + } else { + None + }; + + // Status reg operations (set and clear bits). + match v & 0b1111_1111_1000_1111 { + 0b1001_0100_1000_1000 => { + // CLR_FLAG TODO HOW IS IT CALLED + let idx = ((v & 0b0000_0000_0111_0000) >> 4) as u8; + return Ok(Instruction::CLR_FLAG(StatusFlag::try_from_idx(idx).unwrap())); + }, + 0b1001_0100_0000_1000 => { + // SET_FLAG TODO HOW IS IT CALLED + let idx = ((v & 0b0000_0000_0111_0000) >> 4) as u8; + return Ok(Instruction::SET_FLAG(StatusFlag::try_from_idx(idx).unwrap())); + } + _ => {} + } + + // Other instructions with no parameters. + match v { + 0b1001_0101_1001_1000 => return Ok(Instruction::BREAK), + 0b1001_0101_0001_1001 => return Ok(Instruction::EICALL), + 0b1001_0100_0001_1001 => return Ok(Instruction::EIJMP), + 0b1001_0101_1101_1000 => return Ok(Instruction::ELPM(0.into(), IncrementMode::None)), + 0b1001_0101_0000_1001 => return Ok(Instruction::ICALL), + 0b1001_0100_0000_1001 => return Ok(Instruction::IJMP), + 0b1001_0101_1100_1000 => return Ok(Instruction::LPM(0.into(), IncrementMode::None)), + 0b0000_0000_0000_0000 => return Ok(Instruction::NOP), + 0b1001_0101_0000_1000 => return Ok(Instruction::RET), + 0b1001_0101_0001_1000 => return Ok(Instruction::RETI), + 0b1001_0101_1000_1000 => return Ok(Instruction::SLEEP), + 0b1001_0101_1110_1000 => return Ok(Instruction::SPM(IncrementMode::None)), + 0b1001_0101_1111_1000 => return Ok(Instruction::SPM(IncrementMode::PostIncrement)), + 0b1001_0101_1010_1000 => return Ok(Instruction::WDR), + _ => {} + } + + // Now things will get ugly, we have a lot of different prefix sizes for + // different instructions. + + /* + 0000_0011_1: + 0000_0011_0: + */ + match v & 0b1111_1111_0000_0000 { + 0b0000_0011_0000_0000 => { + // FMUL/FMULS/FMULSU/MULSU + let d = ((v & 0b0111_0000) >> 4) as u8; + let r = ((v & 0b0111)) as u8; + let mode = v & 0b1000_1000; + match mode { + 0b0000_0000 => return Ok(Instruction::MULSU((d + 16).into(), (r + 16).into())), + 0b0000_1000 => return Ok(Instruction::FMUL((d + 16).into(), (r + 16).into())), + 0b1000_0000 => return Ok(Instruction::FMULS((d + 16).into(), (r + 16).into())), + 0b1000_1000 => return Ok(Instruction::FMULSU((d + 16).into(), (r + 16).into())), + _ => unreachable!(), + } + } + _ => {} + } + + /* + 10010110: '1001 0110 KKdd KKKK' + 00000001: '0000 0001 dddd rrrr' + 00000010: '0000 0010 dddd rrrr' + 10010111: '1001 0111 KKdd KKKK' + 11101111: '1110 1111 dddd 1111' + 10011010: '1001 1010 AAAA Abbb' + 10011011: '1001 1011 AAAA Abbb' + 10011001: '1001 1001 AAAA Abbb' + 10011000: '1001 1000 AAAA Abbb' + */ + { + let K = (((v & 0b1100_0000) >> 2) | (v & 0b1111)) as u8; + let d = (v & 0b1111_0000 >> 4) as u8; + let r = (v & 0b1111) as u8; + let A = ((v & 0b1111_1000) >> 3) as u16; + let b = (v & 0b0111) as u8; + match v & 0b1111_1111_0000_0000 { + 0b1001_0110_0000_0000 => return Ok(Instruction::ADIW(((d & 0b11) * 2 + 24).into(), K as u16)), + 0b0000_0001_0000_0000 => return Ok(Instruction::MOVW((d * 2).into(), (r * 2).into())), + 0b0000_0010_0000_0000 => return Ok(Instruction::MULS((d + 16).into(), (r + 16).into())), + 0b1001_0111_0000_0000 => return Ok(Instruction::SBIW(((d & 0b11) * 2 + 24).into(), K)), + 0b1110_1111_0000_0000 => return Ok(Instruction::SER((d + 16).into())), + 0b1001_1010_0000_0000 => return Ok(Instruction::SBI(A, b)), + 0b1001_1011_0000_0000 => return Ok(Instruction::SBIS(A, b)), + 0b1001_1001_0000_0000 => return Ok(Instruction::SBIC(A, b)), + 0b1001_1000_0000_0000 => return Ok(Instruction::CBI(A, b)), + _ => {}, + } + } + + /* + 1001001: + 1001000: + */ + // Handle LD{X,Y,Z}'s (none, incre and decre, just no offset) + { + if v & 0b1110_1100_0000_0000 == 0b1000_0000_0000_0000 { + let dr = (((v & 0b1_1111_0000) >> 4) as u8).into(); + // mode: 00 -> None, 01 -> PostIncrement, 10 -> PreDecrement + let mode = v & 0b11; + let do_store = v & 0b0010_0000_0000 != 0; + match (do_store, v & 0b0001_0000_0000_0000 > 0, (v >> 2) & 0b11, mode) { + // LD X + (false, true, 0b11, 0b00) => return Ok(Instruction::LD(26.into(), dr, IncrementMode::None)), + (false, true, 0b11, 0b01) => return Ok(Instruction::LD(26.into(), dr, IncrementMode::PostIncrement)), + (false, true, 0b11, 0b10) => return Ok(Instruction::LD(26.into(), dr, IncrementMode::PreDecrement)), + // LD Y + (false, false, 0b10, 0b00) => return Ok(Instruction::LD(28.into(), dr, IncrementMode::None)), + (false, true, 0b10, 0b01) => return Ok(Instruction::LD(28.into(), dr, IncrementMode::PostIncrement)), + (false, true, 0b10, 0b10) => return Ok(Instruction::LD(28.into(), dr, IncrementMode::PreDecrement)), + // LD Z + (false, false, 0b00, 0b00) => return Ok(Instruction::LD(30.into(), dr, IncrementMode::None)), + (false, true, 0b00, 0b01) => return Ok(Instruction::LD(30.into(), dr, IncrementMode::PostIncrement)), + (false, true, 0b00, 0b10) => return Ok(Instruction::LD(30.into(), dr, IncrementMode::PreDecrement)), + // ST X + (true, true, 0b11, 0b00) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::None)), + (true, true, 0b11, 0b01) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::PostIncrement)), + (true, true, 0b11, 0b10) => return Ok(Instruction::ST(26.into(), dr, IncrementMode::PreDecrement)), + // ST Y + (true, false, 0b10, 0b00) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::None)), + (true, true, 0b10, 0b01) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::PostIncrement)), + (true, true, 0b10, 0b10) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::PreDecrement)), + // ST Z + (true, false, 0b00, 0b00) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::None)), + (true, true, 0b00, 0b01) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::PostIncrement)), + (true, true, 0b00, 0b10) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::PreDecrement)), + + // POP: 1001 000d dddd 1111 + (false, true, 0b11, 0b11) => return Ok(Instruction::POP(dr)), + // PUSH: 1001 001d dddd 1111 + (true, true, 0b11, 0b11) => return Ok(Instruction::PUSH(dr)), + // LAC: 1001 001r rrrr 0110 + (true, true, 0b01, 0b10) => return Ok(Instruction::LAC(dr)), + // LAS: 1001 001r rrrr 0101 + (true, true, 0b01, 0b01) => return Ok(Instruction::LAS(dr)), + // LAT: 1001 001r rrrr 0111 + (true, true, 0b01, 0b11) => return Ok(Instruction::LAT(dr)), + // XCH: 1001 001r rrrr 0100 + (true, true, 0b01, 0b00) => return Ok(Instruction::XCH(dr)), + // ELPM (2) 1001 000d dddd 0110 + (false, true, 0b01, 0b10) => return Ok(Instruction::ELPM(dr, IncrementMode::None)), + // ELPM (3) 1001 000d dddd 0111 + (false, true, 0b01, 0b11) => return Ok(Instruction::ELPM(dr, IncrementMode::PostIncrement)), + // LPM (2): 1001 000d dddd 0100 + (false, true, 0b01, 0b00) => return Ok(Instruction::LPM(dr, IncrementMode::None)), + // LPM (3): 1001 000d dddd 0101 + (false, true, 0b01, 0b01) => return Ok(Instruction::LPM(dr, IncrementMode::PostIncrement)), + // TODO: LDS32 STS32 + // STS32 1001 001r rrrr 0000 kkkk kkkk kkkk kkkk + (true, true, 0, 0) => { + //STS32 + match v2 { + Some(k) => return Ok(Instruction::STS16(k, dr)), + None => return Err(DecodingError::TruncatedInstruction), + } + }, + // LDS32 1001 000d dddd 0000 kkkk kkkk kkkk kkkk + (false, true, 0, 0) => { + //LDS32 + match v2 { + Some(k) => return Ok(Instruction::LDS16(dr, k)), + None => return Err(DecodingError::TruncatedInstruction), + } + } + _ => {} + } + } + } + //LDX1 1001 000d dddd 1100 + //LDX2 1001 000d dddd 1101 + //LDX3 1001 000d dddd 1110 + + //LDY1 1000 000d dddd 1000 + //LDY2 1001 000d dddd 1001 + //LDY3 1001 000d dddd 1010 + + //LDZ1 1000 000d dddd 0000 + //LDZ2 1001 000d dddd 0001 + //LDZ3 1001 000d dddd 0010 + + //STY1 1000 001r rrrr 1000 + //STY2 1001 001r rrrr 1001 + //STY3 1001 001r rrrr 1010 + + match v & 0b1111_1110_0000_0000 { + 0b1111_1100_0000_0000 | 0b1111_1110_0000_0000 => { + // 1111110: + // 1111111: + // 1111 110r rrrr 0bbb + let r = (((v >> 4) & 0b1_1111) as u8).into(); + let b = (v & 0b111) as u8; + if (v & 0b1000) == 0 { + if ((v >> 9) & 1) == 0 { + return Ok(Instruction::SBRC(r, b)); + } else { + return Ok(Instruction::SBRS(r, b)); + } + } + }, + // BLD '1111 100d dddd 0bbb' + // BST '1111 101d dddd 0bbb' + 0b1111_1000_0000_0000 | 0b1111_1010_0000_0000 => { + let d = (((v >> 4) & 0b1_1111) as u8).into(); + let b = (v & 0b111) as u8; + if (v & 0b1000) == 0 { + if ((v >> 9) & 1) == 9 { + return Ok(Instruction::BLD(d, b)); + } else { + return Ok(Instruction::BST(d, b)); + } + } + } + 0b1001_0100_0000_0000 => { + /* 1001010: + + + */ + let d = (((v & 0b0000_0001_1111_0000) >> 4) as u8).into(); + // ASR '1001 010d dddd 0101 + // COM '1001 010d dddd 0000' + // DEC '1001 010d dddd 1010' + // INC '1001 010d dddd 0011' + // LSR '1001 010d dddd 0110' + // NEG '1001 010d dddd 0001' + // ROR '1001 010d dddd 0111' + // SWP '1001 010d dddd 0010' + match v & 0b1111 { + 0b0101 => return Ok(Instruction::ASR(d)), + 0b0000 => return Ok(Instruction::COM(d)), + 0b1010 => return Ok(Instruction::DEC(d)), + 0b0011 => return Ok(Instruction::INC(d)), + 0b0110 => return Ok(Instruction::LSR(d)), + 0b0001 => return Ok(Instruction::NEG(d)), + 0b0111 => return Ok(Instruction::ROR(d)), + 0b0010 => return Ok(Instruction::SWAP(d)), + // CALL 1001 010k kkkk 111k kkkk kkkk kkkk kkkk' + // JMP '1001 010k kkkk 110k kkkk kkkk kkkk kkkk' + 0b1100 | 0b1101 | 0b1110 | 0b1111 => { + // u32 instruction + match v2 { + None => return Err(DecodingError::TruncatedInstruction), + Some(kl) => { + let k = (((v & 0b1111_0000) as u32) << 12) | (kl as u32); + if v & 0b10 == 0 { + return Ok(Instruction::JMP(k)); + } else { + return Ok(Instruction::CALL(k)); + } + } + } + } + _ => {}, + } + } + _ => {}, + } + + { + let d5 = (((v >> 4) & 0b1_1111) as u8).into(); + let r5 = (((v & 0b1111) | ((v >> 5) & 0b1_0000)) as u8).into(); + match v & 0b1111_1100_0000_0000 { + 0b0001_1000_0000_0000 => return Ok(Instruction::SUB(d5, r5)), + 0b0001_1100_0000_0000 => { + if d5 == r5 { + return Ok(Instruction::ROL(d5)); + } else { + return Ok(Instruction::ADC(d5, r5)); + } + }, + 0b0000_1100_0000_0000 => { + if d5 == r5 { + return Ok(Instruction::LSL(d5)); + } else { + return Ok(Instruction::ADD(d5, r5)); + } + }, + 0b0000_1000_0000_0000 => return Ok(Instruction::SBC(d5, r5)), + 0b0000_0100_0000_0000 => return Ok(Instruction::CPC(d5, r5)), + 0b1001_1100_0000_0000 => return Ok(Instruction::MUL(d5, r5)), + 0b0001_0100_0000_0000 => return Ok(Instruction::CP(d5, r5)), + 0b0001_0000_0000_0000 => return Ok(Instruction::CPSE(d5, r5)), + 0b0010_0100_0000_0000 => { + if d5 == r5 { + return Ok(Instruction::CLR(d5)); + } else { + return Ok(Instruction::EOR(d5, r5)); + } + } + 0b0010_0000_0000_0000 => { + if d5 == r5 { + return Ok(Instruction::TST(d5)); + } else { + return Ok(Instruction::AND(d5, r5)); + } + } + 0b0010_1000_0000_0000 => return Ok(Instruction::OR(d5, r5)), + 0b0010_1100_0000_0000 => return Ok(Instruction::MOV(d5, r5)), + + // Branch instructions + // 0b1111_0000_0000_0000 => + // 0b1111_0100_0000_0000 => + 0b1111_0100_0000_0000 | 0b1111_0000_0000_0000 => { + let flag_should_be_set = ((v >> 10) & 1) == 1; + let flag = StatusFlag::try_from_idx((v & 0b111) as u8).unwrap(); + let mut k = (v >> 3) as i8; + k = if k > (1 << 7) { + (1 << 7) - k + } else { + k + }; + return Ok(Instruction::BR_IF(k, flag, flag_should_be_set)); + } + _ => {} + } + } + + { + // OUT 1011 1AAr rrrr AAAA + // IN '1011 0AAd dddd AAAA + let r = (((v >> 4) & 0b1_1111) as u8).into(); + let A = ((((v >> 5) & 0b11_0000) | (v & 0b1111)) as u8).into(); + + // STS return '1010 1kkk rrrr kkkk'.replace(' ', '') + // LDS return '1010 0kkk dddd kkkk'.replace(' ', '') + let r16 = (((v >> 4) & 0b1111) as u8).into(); + let k = (((v >> 4) & 0b111_0000) | (v & 0b1111)) as u8; + match v & 0b1111_1000_0000_0000 { + 0b1011_1000_0000_0000 => return Ok(Instruction::OUT(A, r)), + 0b1011_0000_0000_0000 => return Ok(Instruction::IN(r, A)), + 0b1010_1000_0000_0000 => return Ok(Instruction::STS8(k, r16)), + 0b1010_0000_0000_0000 => return Ok(Instruction::LDS8(r16, k)), + _ => {} + } + } + + { + // Rcall/jmp: 1101 kkkk kkkk kkkk + // SBR 0110 KKKK dddd KKKK <- seems invalid!!! + // ORI: 0110 KKKK dddd KKKK + // ANDI 0111 KKKK dddd KKKK + // CPI 0011 KKKK dddd KKKK + // SUBI 0101 KKKK dddd KKKK + // SBCI 0100 KKKK dddd KKKK + // LDI 1110 KKKK dddd KKKK + // 0110: + // 0111: + // 0011: + // 0101: + // 0100: + // 1110: + // 1100: + // 1101: + let k = (v & 0b0000_1111_1111_1111) as i16; + let k: i16 = if (k & 0b1000_0000_0000) == 0b1000_0000_0000 { + k - 0b1_0000_0000_0000 + } else { + k + }; + let K = (((v >> 4) & 0b1111_0000) | (v & 0b1111)) as u8; + let d = (((v >> 4) & 0b1111) as u8 + 16u8).into(); + match v & 0b1111_0000_0000_0000 { + 0b0110_0000_0000_0000 => return Ok(Instruction::ORI(d, K)), + 0b0111_0000_0000_0000 => return Ok(Instruction::ANDI(d, K)), + 0b0011_0000_0000_0000 => return Ok(Instruction::CPI(d, K)), + 0b0101_0000_0000_0000 => return Ok(Instruction::SUBI(d, K)), + 0b0100_0000_0000_0000 => return Ok(Instruction::SBCI(d, K)), + 0b1110_0000_0000_0000 => return Ok(Instruction::LDI(d, K)), + 0b1100_0000_0000_0000 => return Ok(Instruction::RJMP(k)), + 0b1101_0000_0000_0000 => return Ok(Instruction::RCALL(k)), + _ => {} + } + } + + // return '10q0 qq0d dddd 1qqq'.replace(' ', '') LD Y IV + // return '10q0 qq0d dddd 0qqq'.replace(' ', '') LD Z IV + // return "10q0 qq1r rrrr 1qqq".replace(' ', '') ST Y IV + // return "10q0 qq1r rrrr 0qqq".replace(' ', '') ST Z IV + if (v >> 12) & 0b1101 == 0b1000 { + let w = ((v >> 10) & 1) == 1; + let dr = (((v >> 4) & 0b11111) as u8).into(); + let q = (((v >> 8) & 0b10_0000) | ((v >> 7) & 0b1_1000) | (v & 0b111)) as u8; + match (v & 0b1000, w) { + (0b1000, false) => return Ok(Instruction::LD(28.into(), dr, IncrementMode::ConstantOffset(q))), + (0b1000, true) => return Ok(Instruction::ST(28.into(), dr, IncrementMode::ConstantOffset(q))), + (0b0000, false) => return Ok(Instruction::LD(30.into(), dr, IncrementMode::ConstantOffset(q))), + (0b0000, true) => return Ok(Instruction::ST(30.into(), dr, IncrementMode::ConstantOffset(q))), + _ => {}, + } + } + Err(DecodingError::UndefinedInstruction(v)) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a009511 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,38 @@ +use std::path::Path; +use std::io; +use std::io::Read; +use std::fs; + +mod cpu; +mod regs; +mod decoder; + +fn main() { + println!("Hello, world!"); + let mut rom = read_file("rom.bin").unwrap(); + let mut ram = [0u8; 1024]; + let mut cpu = cpu::CPU::new(); + for _ in 0..40 { + println!("{:?}", cpu.step(&mut rom, &mut ram)); + } + println!("{:#?}", cpu); + + /* + let mut off = 0; + while off < data.len() { + let instr = decoder::decode(&data[off..]); + println!("{:X}: {:?}", off, instr); + match instr { + Ok(o) => off += o.size(), + _ => off += 2, + } + } + */ +} + +pub fn read_file>(rom_path: P) -> Result, io::Error> { + let mut file = try!(fs::File::open(rom_path)); + let mut buf = Vec::new(); + try!(file.read_to_end(&mut buf)); + Ok(buf.into_boxed_slice()) +} \ No newline at end of file diff --git a/src/regs.rs b/src/regs.rs new file mode 100644 index 0000000..1417d26 --- /dev/null +++ b/src/regs.rs @@ -0,0 +1,148 @@ +use std::cmp::PartialEq; + +// Sreg flags, ordered from bit 7 to bit 0. +#[derive(Copy, Clone, Debug)] +pub enum StatusFlag { + // I + GlobalInterruptEnable, + // T + BitCopyStorage, + // H + HalfCarry, + // S + SignBit, + // V + TwosComplementOverflow, + // N + Negative, + // Z + Zero, + // C + Carry, +} + +impl Into for StatusFlag { + fn into(self) -> u8 { + 1 << match self { + StatusFlag::GlobalInterruptEnable => 7, + StatusFlag::BitCopyStorage => 6, + StatusFlag::HalfCarry => 5, + StatusFlag::SignBit => 4, + StatusFlag::TwosComplementOverflow => 3, + StatusFlag::Negative => 2, + StatusFlag::Zero => 1, + StatusFlag::Carry => 0, + } + } +} + +impl StatusFlag { + // the try_from is unstable, so use our own here. + pub fn try_from(i: u8) -> Result { + match i { + 0x80 => Ok(StatusFlag::GlobalInterruptEnable), + 0x40 => Ok(StatusFlag::BitCopyStorage), + 0x20 => Ok(StatusFlag::HalfCarry), + 0x10 => Ok(StatusFlag::SignBit), + 0x08 => Ok(StatusFlag::TwosComplementOverflow), + 0x04 => Ok(StatusFlag::Negative), + 0x02 => Ok(StatusFlag::Zero), + 0x01 => Ok(StatusFlag::Carry), + _ => Err(()), + } + } + + pub fn try_from_idx(i: u8) -> Result { + if i > 7 { + Err(()) + } else { + Ok([ + StatusFlag::Carry, + StatusFlag::Zero, + StatusFlag::Negative, + StatusFlag::TwosComplementOverflow, + StatusFlag::SignBit, + StatusFlag::HalfCarry, + StatusFlag::BitCopyStorage, + StatusFlag::GlobalInterruptEnable + ][i as usize]) + } + } +} + +// type GeneralPurposeRegister = struct(u8); +#[derive(Debug)] +pub struct GeneralPurposeRegister(u8); +impl From for GeneralPurposeRegister { + fn from(v: u8) -> Self { + if v > 31 { + unreachable!(); + } + Self{0: v} + } +} + +impl Into for GeneralPurposeRegister { + fn into(self) -> u8 { + self.0 + } +} + +impl Into for GeneralPurposeRegister { + fn into(self) -> usize { + self.0 as usize + } +} + +impl GeneralPurposeRegister { + pub fn as_usize(&self) -> usize { + self.0 as usize + } +} + +impl PartialEq for GeneralPurposeRegister { + fn eq(&self, other: &GeneralPurposeRegister) -> bool { + self.0 == other.0 + } +} + +// Mostly the same as above, but we want to make it a different type. +// type GeneralPurposeRegisterPair = struct(u8); +#[derive(Debug)] +pub struct GeneralPurposeRegisterPair(u8); +impl From for GeneralPurposeRegisterPair { + fn from(v: u8) -> Self { + if v > 30 { + println!("v={}", v); + unreachable!(); + } + Self{0: v} + } +} + +impl Into for GeneralPurposeRegisterPair { + fn into(self) -> u8 { + self.0 + } +} + +impl PartialEq for GeneralPurposeRegisterPair { + fn eq(&self, other: &GeneralPurposeRegisterPair) -> bool { + self.0 == other.0 + } +} + +impl GeneralPurposeRegisterPair { + fn first(&self) -> u8 { + self.0 + } + + fn second(&self) -> u8 { + self.0 + 1 + } +} + +pub type PC = u32; + +// TODO: Struct :) +pub type IORegister = u16; \ No newline at end of file