iter
This commit is contained in:
parent
dcbc514485
commit
4009845fce
300
src/cpu.rs
300
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<usize, CPUError> {
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
@ -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<Instruction, DecodingError> {
|
||||
*/
|
||||
{
|
||||
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<Instruction, DecodingError> {
|
||||
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<Instruction, DecodingError> {
|
||||
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<Instruction, DecodingError> {
|
||||
|
||||
{
|
||||
// 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
|
||||
|
||||
23
src/main.rs
23
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() {
|
||||
@ -36,3 +45,9 @@ pub fn read_file<P: AsRef<Path>>(rom_path: P) -> Result<Box<[u8]>, io::Error> {
|
||||
try!(file.read_to_end(&mut buf));
|
||||
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(())
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user