From 4009845fceafbdc265040120402f8111a89a82ab Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Sun, 11 Feb 2018 00:01:57 +0100 Subject: [PATCH] iter --- src/cpu.rs | 300 ++++++++++++++++++++++++++++++++++++++++++++++++- src/decoder.rs | 12 +- src/main.rs | 25 ++++- src/regs.rs | 8 +- 4 files changed, 326 insertions(+), 19 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index d1304ee..c1b3e77 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -2,15 +2,22 @@ #![allow(unused_variables)] use decoder; -use decoder::Instruction; +use decoder::{IncrementMode, Instruction}; +use regs::{StatusFlag, GeneralPurposeRegister, GeneralPurposeRegisterPair}; #[derive(Debug)] pub enum CPUError { UnimplementedInstruction, OutOfBoundsException, + UnsupportedAddress, DecodingError(decoder::DecodingError), } +enum FlagUpdateMode { + HSVNZC, + HSVNZC_KeepZero, +} + // CPU #[derive(Debug)] pub struct CPU { @@ -38,6 +45,88 @@ impl CPU { } } + fn test_flag(&self, flag: StatusFlag) -> bool { + self.sreg & flag as u8 > 0 + } + + fn set_flag(&mut self, flag: StatusFlag) { + self.sreg |= flag as u8; + } + + fn clear_flag(&mut self, flag: StatusFlag) { + self.sreg &= !(flag as u8); + } + + fn set_clear_flag(&mut self, flag: StatusFlag, test: bool) { + if test { + self.set_flag(flag); + } else { + self.clear_flag(flag); + } + } + + fn get_register_pair(&self, r: &GeneralPurposeRegisterPair) -> u16 { + ((self.registers[r.high()] as u16) << 8) | self.registers[r.low()] as u16 + } + + fn set_register_pair(&mut self, r: &GeneralPurposeRegisterPair, v: u16) { + self.registers[r.high()] = (v >> 8) as u8; + self.registers[r.low()] = (v & 0xFF) as u8; + } + + fn get_register(&self, r: &GeneralPurposeRegister) -> u8 { + self.registers[r.as_usize()] + } + + fn set_register(&mut self, r: &GeneralPurposeRegister, v: u8) { + self.registers[r.as_usize()] = v; + } + + fn push(&mut self, ram: &mut [u8], val: u8) { + ram[self.sp as usize] = val; + self.sp -= 1; + } + + fn pop(&mut self, ram: &mut [u8]) -> u8 { + self.sp += 1; + ram[self.sp as usize] + } + + fn update_flags(&mut self, mode: FlagUpdateMode, rd: u8, rr: u8, s: u8) { + match mode { + FlagUpdateMode::HSVNZC => { + // H: Set if there was a borrow from bit 3; cleared otherwise + // S: N ⊕ V, For signed tests. + // V: Set if two's complement overflow resulted from the operation; cleared otherwise. + // N: R7 Set if MSB of the result is set; cleared otherwise. + // Z: Set if the result is $00; cleared otherwise. + // C: Set if the absolute value of K is larger than the absolute value of Rd; cleared otherwise. + // cpu_.sreg_.C = ((~Rd & Rr) | (Rr & R) | (R & ~Rd)) & 0x80; + self.set_clear_flag(StatusFlag::Carry, ((!rd & rr) | (rr & s) | (s & !rd)) & 0x80 == 0x80); + self.set_clear_flag(StatusFlag::Zero, s == 0); + self.set_clear_flag(StatusFlag::Negative, (s & 0x80) == 0x80); + self.set_clear_flag(StatusFlag::TwosComplementOverflow, ((rd & !rr & !s) | (!rd & rr & s)) & 0x80 == 0x80); + let t = self.test_flag(StatusFlag::Negative) ^ self.test_flag(StatusFlag::TwosComplementOverflow); + self.set_clear_flag(StatusFlag::SignBit, t); + self.set_clear_flag(StatusFlag::HalfCarry, ((!rd & rr) | (rr & s) | (s & !rd)) & 0x08 == 0x08); + //cpu_.sreg_.H = ((~Rd & Rr) | (Rr & R) | (R & ~Rd)) & 0x08; + + // self.set_clear_flag(StatusFlag::Zero, r.wrapping_sub(v) == 0); + // self.set_clear_flag(StatusFlag::Carry, v > r); + } + FlagUpdateMode::HSVNZC_KeepZero => { + self.set_clear_flag(StatusFlag::Carry, ((!rd & rr) | (rr & s) | (s & !rd)) & 0x80 == 0x80); + let t = self.test_flag(StatusFlag::Zero); + self.set_clear_flag(StatusFlag::Zero, s == 0 && t); + self.set_clear_flag(StatusFlag::Negative, (s & 0x80) == 0x80); + self.set_clear_flag(StatusFlag::TwosComplementOverflow, ((rd & !rr & !s) | (!rd & rr & s)) & 0x80 == 0x80); + let t = self.test_flag(StatusFlag::Negative) ^ self.test_flag(StatusFlag::TwosComplementOverflow); + self.set_clear_flag(StatusFlag::SignBit, t); + self.set_clear_flag(StatusFlag::HalfCarry, ((!rd & rr) | (rr & s) | (s & !rd)) & 0x08 == 0x08); + } + } + } + // Returns # of ticks the executed instruction took pub fn step(&mut self, rom: &mut [u8], ram: &mut [u8]) -> Result { // Instruction fetch @@ -56,12 +145,215 @@ impl CPU { // Instruction execute match ins { Instruction::JMP(v) => self.pc = v, - Instruction::CLR(ref r) => self.registers[r.as_usize()] = 0, + Instruction::CLR(ref r) => self.set_register(r, 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::LDI(ref r, v) => self.set_register(r, v), + Instruction::SER(ref r) => self.set_register(r, 0xFF), Instruction::RJMP(v) => self.pc = self.pc.wrapping_add(v as _), + Instruction::CLR_FLAG(v) => self.clear_flag(v), + Instruction::SET_FLAG(v) => self.set_flag(v), + Instruction::CPI(ref r, v) => { + let rv = self.get_register(r); + self.update_flags(FlagUpdateMode::HSVNZC, rv, v, rv.wrapping_sub(v)); + }, + Instruction::CP(ref r, ref i) => { + let rv = self.get_register(r); + let iv = self.get_register(i); + self.update_flags(FlagUpdateMode::HSVNZC, rv, iv, rv.wrapping_sub(iv)); + }, + Instruction::CPC(ref rd, ref rr) => { + let rd: u8 = self.get_register(rd); + let rr: u8 = self.get_register(rr); + + let s = if self.test_flag(StatusFlag::Carry) { + rd.wrapping_sub(rr).wrapping_sub(1) + } else { + rd.wrapping_sub(rr) + }; + self.update_flags(FlagUpdateMode::HSVNZC, rd, rr, s); + /* + self.set_clear_flag(StatusFlag::Carry, ((!r.into() & i.into()) | (r.into() & i.into()) | (r.into() & !i.into())) & 0x80 == 0x80); + cpu_.sreg_.C = ((~Rd & Rr) | (Rr & R) | (R & ~Rd)) & 0x80; + self.set_clear_flag(StatusFlag::Zero, s == 0 && self.test_flag(StatusFlag::Zero)); + cpu_.sreg_.Z = R == 0; + cpu_.sreg_.N = R & 0x80; + cpu_.sreg_.V = ((Rd & ~Rr & ~R) | (~Rd & Rr & R)) & 0x80; + cpu_.sreg_.S = cpu_.sreg_.N ^ cpu_.sreg_.V; + cpu_.sreg_.H = ((~Rd & Rr) | (Rr & R) | (R & ~Rd)) & 0x08; + */}, + Instruction::BR_IF(offset, flag, test) => { + if self.test_flag(flag) == test { + self.pc = self.pc.wrapping_add(offset as _); + } + }, + // ST(GeneralPurposeRegisterPair, GeneralPurposeRegister, IncrementMode), + Instruction::ST(ref ptr, ref src_reg, ref inc_mode) => { + let base = self.get_register_pair(ptr); + let addr = match *inc_mode { + IncrementMode::None => base, + IncrementMode::PreDecrement => { + self.set_register_pair(ptr, base.wrapping_sub(1)); + base.wrapping_sub(1) + }, + IncrementMode::PostIncrement => { + self.set_register_pair(ptr, base.wrapping_add(1)); + base + }, + IncrementMode::ConstantOffset(o) => base.wrapping_add(o as _), + }; + ram[addr as usize] = self.get_register(src_reg); + }, + Instruction::LD(ref ptr, ref dst_reg, ref inc_mode) => { + let base = self.get_register_pair(ptr); + let addr = match *inc_mode { + IncrementMode::None => base, + IncrementMode::PreDecrement => { + self.set_register_pair(ptr, base.wrapping_sub(1)); + base.wrapping_sub(1) + }, + IncrementMode::PostIncrement => { + self.set_register_pair(ptr, base.wrapping_add(1)); + base + }, + IncrementMode::ConstantOffset(o) => base.wrapping_add(o as _), + }; + let v = ram[addr as usize]; + self.set_register(dst_reg, v); + }, + Instruction::ELPM(ref dst_reg, ref inc_mode) => { + // TODO: RAMPZ + let Z = self.get_register_pair(&30u8.into()); + let d = rom[Z as usize]; + self.set_register(dst_reg, d); + match *inc_mode { + IncrementMode::PostIncrement => { + self.set_register_pair(&30u8.into(), Z.wrapping_add(1)); + }, + _ => { + // This instruction does only support PostIncrement + }, + } + }, + Instruction::LPM(ref dst_reg, ref inc_mode) => { + let Z = self.get_register_pair(&30u8.into()); + let d = rom[Z as usize]; + self.set_register(dst_reg, d); + match *inc_mode { + IncrementMode::PostIncrement => { + self.set_register_pair(&30u8.into(), Z.wrapping_add(1)); + }, + _ => { + // This instruction does only support PostIncrement + }, + } + }, + Instruction::OUT(ref addr, ref val) => { + /* + 0x03D: 'SPL', + 0x03E: 'SPH', + 0x03F: 'SREG', + */ + let val = self.get_register(val) as u16; + match *addr { + 0x3D => self.sp = self.sp & 0xFF00 | val, + 0x3E => self.sp = self.sp & 0x00FF | (val << 8), + 0x3F => self.sreg = val as u8, + _ => return Err(CPUError::UnsupportedAddress) + } + }, + Instruction::IN(ref reg, ref addr) => { + let _sp = self.sp; // Non-lexical lifetimes plz + match *addr { + 0x3D => self.set_register(reg, (_sp & 0xFF) as u8), + 0x3E => self.set_register(reg, (_sp >> 8) as u8), + _ => return Err(CPUError::UnsupportedAddress) + } + } + Instruction::CALL(ref addr) => { + let ret_to = self.pc + 2; + self.push(ram, ((ret_to >> 16) & 0xFF) as u8); + self.push(ram, ((ret_to >> 8) & 0xFF) as u8); + self.push(ram, (ret_to & 0xFF) as u8); + self.pc = *addr; + }, + Instruction::RCALL(ref addr) => { + let ret_to = self.pc + 2; + self.push(ram, ((ret_to >> 16) & 0xFF) as u8); + self.push(ram, ((ret_to >> 8) & 0xFF) as u8); + self.push(ram, (ret_to & 0xFF) as u8); + self.pc = (self.pc as i32 + *addr as i32) as u32; + }, + Instruction::RET => { + let mut ret_to = self.pop(ram) as u32; + ret_to += (self.pop(ram) as u32) << 8; + ret_to += (self.pop(ram) as u32) << 16; + self.pc = ret_to as _; + }, + Instruction::POP(ref reg) => { + let v = self.pop(ram); + self.registers[reg.as_usize()] = v; + }, + Instruction::PUSH(ref reg) => { + let v = self.registers[reg.as_usize()]; + self.push(ram, v); + }, + Instruction::SUBI(ref reg, ref v) => { + let v = self.registers[reg.as_usize()].wrapping_sub(*v); + self.registers[reg.as_usize()] = v; + // TODO: flags + }, + Instruction::SBC(ref d, ref r) => { + let v = self.registers[d.as_usize()].wrapping_sub(self.registers[r.as_usize()]); + // TODO: - carry + self.registers[d.as_usize()] = v; + }, + Instruction::SBCI(ref d, ref r) => { + let v = self.registers[d.as_usize()].wrapping_sub(*r); + // TODO: - carry + self.registers[d.as_usize()] = v; + }, + Instruction::MOVW(ref d, ref r) => { + let t = self.get_register_pair(r); + self.set_register_pair(d, t); + }, + Instruction::OR(ref d, ref r) => { + let t = self.get_register(d) | self.get_register(r); + self.set_register(d, t); + } + Instruction::ORI(ref d, ref v) => { + let t = self.get_register(d) | *v; + self.set_register(d, t); + }, + Instruction::AND(ref d, ref r) => { + let t = self.get_register(d) & self.get_register(r); + self.set_register(d, t); + } + Instruction::ANDI(ref d, ref v) => { + let t = self.get_register(d) & *v; + self.set_register(d, t); + }, + Instruction::ADD(ref d, ref r) => { + let t = self.get_register(d) + self.get_register(r); + self.set_register(d, t); + }, + Instruction::SUB(ref d, ref r) => { + let t = self.get_register(d) - self.get_register(r); + self.set_register(d, t); + }, + Instruction::MOV(ref d, ref r) => { + let v = self.get_register(r); + self.set_register(d, v); + }, + Instruction::ADIW(ref d, ref v) => { + let v = self.get_register_pair(d).wrapping_add(*v); + self.set_register_pair(d, v); + }, + Instruction::SBIW(ref d, ref v) => { + let v = self.get_register_pair(d).wrapping_sub(*v as _); + self.set_register_pair(d, v); + } + Instruction::NOP => {}, _ => return Err(CPUError::UnimplementedInstruction) } diff --git a/src/decoder.rs b/src/decoder.rs index 8c0e8c7..9cda401 100644 --- a/src/decoder.rs +++ b/src/decoder.rs @@ -82,7 +82,7 @@ pub enum Instruction { SBI(IORegister, u8), SBIC(IORegister, u8), SBIS(IORegister, u8), - SBIW(GeneralPurposeRegisterPair, u8), + SBIW(GeneralPurposeRegisterPair, u16), SBR(GeneralPurposeRegister, u8), SBRC(GeneralPurposeRegister, u8), SBRS(GeneralPurposeRegister, u8), @@ -203,7 +203,7 @@ pub fn decode(data: &[u8]) -> Result { */ { let K = (((v & 0b1100_0000) >> 2) | (v & 0b1111)) as u8; - let d = (v & 0b1111_0000 >> 4) 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; @@ -211,7 +211,7 @@ pub fn decode(data: &[u8]) -> Result { 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)), + 0b1001_0111_0000_0000 => return Ok(Instruction::SBIW(((d & 0b11) * 2 + 24).into(), K as u16)), 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)), @@ -436,8 +436,8 @@ pub fn decode(data: &[u8]) -> Result { 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 + k = if k > (1 << 6) { + (k as i16 - 1 << 8) as i8 } else { k }; @@ -468,7 +468,7 @@ pub fn decode(data: &[u8]) -> Result { { // Rcall/jmp: 1101 kkkk kkkk kkkk - // SBR 0110 KKKK dddd KKKK <- seems invalid!!! + // SBR 0110 KKKK dddd KKKK // ORI: 0110 KKKK dddd KKKK // ANDI 0111 KKKK dddd KKKK // CPI 0011 KKKK dddd KKKK diff --git a/src/main.rs b/src/main.rs index a009511..5498195 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,31 @@ +#![allow(non_snake_case)] use std::path::Path; use std::io; -use std::io::Read; +use std::io::{Read, Write}; use std::fs; mod cpu; mod regs; mod decoder; +mod chip; fn main() { println!("Hello, world!"); let mut rom = read_file("rom.bin").unwrap(); - let mut ram = [0u8; 1024]; + let mut ram = [0u8; 8 * 1024 + 8 * 1024]; let mut cpu = cpu::CPU::new(); - for _ in 0..40 { - println!("{:?}", cpu.step(&mut rom, &mut ram)); + for _ in 0..28000 { + let r = cpu.step(&mut rom, &mut ram); + println!("{:?}", r); + match r { + Err(cpu::CPUError::OutOfBoundsException) => break, + _ => {} + } } println!("{:#?}", cpu); + write_file("/tmp/endstate.ram", &ram).unwrap(); + /* let mut off = 0; while off < data.len() { @@ -35,4 +44,10 @@ pub fn read_file>(rom_path: P) -> Result, io::Error> { let mut buf = Vec::new(); try!(file.read_to_end(&mut buf)); Ok(buf.into_boxed_slice()) -} \ No newline at end of file +} + +pub fn write_file>(path: P, data: &[u8]) -> Result<(), io::Error> { + let mut file = try!(fs::File::create(path)); + try!(file.write_all(&data)); + Ok(()) +} diff --git a/src/regs.rs b/src/regs.rs index 1417d26..2588002 100644 --- a/src/regs.rs +++ b/src/regs.rs @@ -133,12 +133,12 @@ impl PartialEq for GeneralPurposeRegisterPair { } impl GeneralPurposeRegisterPair { - fn first(&self) -> u8 { - self.0 + pub fn low(&self) -> usize { + self.0 as usize } - fn second(&self) -> u8 { - self.0 + 1 + pub fn high(&self) -> usize { + self.0 as usize + 1 } }