This commit is contained in:
Kevin Hamacher 2018-02-11 00:01:57 +01:00
parent dcbc514485
commit 4009845fce
4 changed files with 326 additions and 19 deletions

View File

@ -2,15 +2,22 @@
#![allow(unused_variables)] #![allow(unused_variables)]
use decoder; use decoder;
use decoder::Instruction; use decoder::{IncrementMode, Instruction};
use regs::{StatusFlag, GeneralPurposeRegister, GeneralPurposeRegisterPair};
#[derive(Debug)] #[derive(Debug)]
pub enum CPUError { pub enum CPUError {
UnimplementedInstruction, UnimplementedInstruction,
OutOfBoundsException, OutOfBoundsException,
UnsupportedAddress,
DecodingError(decoder::DecodingError), DecodingError(decoder::DecodingError),
} }
enum FlagUpdateMode {
HSVNZC,
HSVNZC_KeepZero,
}
// CPU // CPU
#[derive(Debug)] #[derive(Debug)]
pub struct CPU { 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 // Returns # of ticks the executed instruction took
pub fn step(&mut self, rom: &mut [u8], ram: &mut [u8]) -> Result<usize, CPUError> { pub fn step(&mut self, rom: &mut [u8], ram: &mut [u8]) -> Result<usize, CPUError> {
// Instruction fetch // Instruction fetch
@ -56,12 +145,215 @@ impl CPU {
// Instruction execute // Instruction execute
match ins { match ins {
Instruction::JMP(v) => self.pc = v, 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) => Instruction::EOR(ref d, ref r) =>
self.registers[d.as_usize()] ^= self.registers[r.as_usize()], self.registers[d.as_usize()] ^= self.registers[r.as_usize()],
Instruction::LDI(ref r, v) => self.registers[r.as_usize()] = v, Instruction::LDI(ref r, v) => self.set_register(r, v),
Instruction::SER(ref r) => self.registers[r.as_usize()] = 0xFF, Instruction::SER(ref r) => self.set_register(r, 0xFF),
Instruction::RJMP(v) => self.pc = self.pc.wrapping_add(v as _), 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) _ => return Err(CPUError::UnimplementedInstruction)
} }

View File

@ -82,7 +82,7 @@ pub enum Instruction {
SBI(IORegister, u8), SBI(IORegister, u8),
SBIC(IORegister, u8), SBIC(IORegister, u8),
SBIS(IORegister, u8), SBIS(IORegister, u8),
SBIW(GeneralPurposeRegisterPair, u8), SBIW(GeneralPurposeRegisterPair, u16),
SBR(GeneralPurposeRegister, u8), SBR(GeneralPurposeRegister, u8),
SBRC(GeneralPurposeRegister, u8), SBRC(GeneralPurposeRegister, u8),
SBRS(GeneralPurposeRegister, u8), SBRS(GeneralPurposeRegister, u8),
@ -203,7 +203,7 @@ pub fn decode(data: &[u8]) -> Result<Instruction, DecodingError> {
*/ */
{ {
let K = (((v & 0b1100_0000) >> 2) | (v & 0b1111)) as u8; 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 r = (v & 0b1111) as u8;
let A = ((v & 0b1111_1000) >> 3) as u16; let A = ((v & 0b1111_1000) >> 3) as u16;
let b = (v & 0b0111) as u8; let b = (v & 0b0111) as u8;
@ -211,7 +211,7 @@ pub fn decode(data: &[u8]) -> Result<Instruction, DecodingError> {
0b1001_0110_0000_0000 => return Ok(Instruction::ADIW(((d & 0b11) * 2 + 24).into(), K as u16)), 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_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())), 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())), 0b1110_1111_0000_0000 => return Ok(Instruction::SER((d + 16).into())),
0b1001_1010_0000_0000 => return Ok(Instruction::SBI(A, b)), 0b1001_1010_0000_0000 => return Ok(Instruction::SBI(A, b)),
0b1001_1011_0000_0000 => return Ok(Instruction::SBIS(A, b)), 0b1001_1011_0000_0000 => return Ok(Instruction::SBIS(A, b)),
@ -436,8 +436,8 @@ pub fn decode(data: &[u8]) -> Result<Instruction, DecodingError> {
let flag_should_be_set = ((v >> 10) & 1) == 1; let flag_should_be_set = ((v >> 10) & 1) == 1;
let flag = StatusFlag::try_from_idx((v & 0b111) as u8).unwrap(); let flag = StatusFlag::try_from_idx((v & 0b111) as u8).unwrap();
let mut k = (v >> 3) as i8; let mut k = (v >> 3) as i8;
k = if k > (1 << 7) { k = if k > (1 << 6) {
(1 << 7) - k (k as i16 - 1 << 8) as i8
} else { } else {
k k
}; };
@ -468,7 +468,7 @@ pub fn decode(data: &[u8]) -> Result<Instruction, DecodingError> {
{ {
// Rcall/jmp: 1101 kkkk kkkk kkkk // Rcall/jmp: 1101 kkkk kkkk kkkk
// SBR 0110 KKKK dddd KKKK <- seems invalid!!! // SBR 0110 KKKK dddd KKKK
// ORI: 0110 KKKK dddd KKKK // ORI: 0110 KKKK dddd KKKK
// ANDI 0111 KKKK dddd KKKK // ANDI 0111 KKKK dddd KKKK
// CPI 0011 KKKK dddd KKKK // CPI 0011 KKKK dddd KKKK

View File

@ -1,22 +1,31 @@
#![allow(non_snake_case)]
use std::path::Path; use std::path::Path;
use std::io; use std::io;
use std::io::Read; use std::io::{Read, Write};
use std::fs; use std::fs;
mod cpu; mod cpu;
mod regs; mod regs;
mod decoder; mod decoder;
mod chip;
fn main() { fn main() {
println!("Hello, world!"); println!("Hello, world!");
let mut rom = read_file("rom.bin").unwrap(); 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(); let mut cpu = cpu::CPU::new();
for _ in 0..40 { for _ in 0..28000 {
println!("{:?}", cpu.step(&mut rom, &mut ram)); let r = cpu.step(&mut rom, &mut ram);
println!("{:?}", r);
match r {
Err(cpu::CPUError::OutOfBoundsException) => break,
_ => {}
}
} }
println!("{:#?}", cpu); println!("{:#?}", cpu);
write_file("/tmp/endstate.ram", &ram).unwrap();
/* /*
let mut off = 0; let mut off = 0;
while off < data.len() { while off < data.len() {
@ -35,4 +44,10 @@ pub fn read_file<P: AsRef<Path>>(rom_path: P) -> Result<Box<[u8]>, io::Error> {
let mut buf = Vec::new(); let mut buf = Vec::new();
try!(file.read_to_end(&mut buf)); try!(file.read_to_end(&mut buf));
Ok(buf.into_boxed_slice()) Ok(buf.into_boxed_slice())
} }
pub fn write_file<P: AsRef<Path>>(path: P, data: &[u8]) -> Result<(), io::Error> {
let mut file = try!(fs::File::create(path));
try!(file.write_all(&data));
Ok(())
}

View File

@ -133,12 +133,12 @@ impl PartialEq for GeneralPurposeRegisterPair {
} }
impl GeneralPurposeRegisterPair { impl GeneralPurposeRegisterPair {
fn first(&self) -> u8 { pub fn low(&self) -> usize {
self.0 self.0 as usize
} }
fn second(&self) -> u8 { pub fn high(&self) -> usize {
self.0 + 1 self.0 as usize + 1
} }
} }