Initial commit

This commit is contained in:
Kevin Hamacher 2018-02-04 19:44:10 +01:00
commit dcbc514485
6 changed files with 788 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target/
**/*.rs.bk

6
Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "avremu"
version = "0.1.0"
authors = ["Kevin Hamacher <kevin.hamacher@ruhr-uni-bochum.de>"]
[dependencies]

70
src/cpu.rs Normal file
View File

@ -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<usize, CPUError> {
// 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())
}
}

524
src/decoder.rs Normal file
View File

@ -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<Instruction, DecodingError> {
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<u16> = 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: <class 'FMULS'> <class 'FMULSU'>
0000_0011_0: <class 'FMUL'> <class 'MULSU'>
*/
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: <class 'ADIW'> '1001 0110 KKdd KKKK'
00000001: <class 'MOVW'> '0000 0001 dddd rrrr'
00000010: <class 'MULS'> '0000 0010 dddd rrrr'
10010111: <class 'SBIW'> '1001 0111 KKdd KKKK'
11101111: <class 'SER'> '1110 1111 dddd 1111'
10011010: <class 'SBI'> '1001 1010 AAAA Abbb'
10011011: <class 'SBIS'> '1001 1011 AAAA Abbb'
10011001: <class 'SBIC'> '1001 1001 AAAA Abbb'
10011000: <class 'CBI'> '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: <class 'STS_32'>
1001000: <class 'LDS_32'>
*/
// 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: <class 'SBRC'>
// 1111111: <class 'SBRS'>
// 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: <class 'ASR'> <class 'CALL'> <class 'COM'> <class 'DEC'>
<class 'INC'> <class 'JMP'> <class 'LSR'> <class 'NEG'>
<class 'ROR'> <class 'SWAP'>
*/
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 => <class 'BRCS'> <class 'BREQ'> <class 'BRHS'> <class 'BRIE'> <class 'BRLO'> <class 'BRLT'> <class 'BRMI'> <class 'BRTS'> <class 'BRVS'>
// 0b1111_0100_0000_0000 => <class 'BRCC'> <class 'BRGE'> <class 'BRHC'> <class 'BRID'> <class 'BRNE'> <class 'BRPL'> <class 'BRSH'> <class 'BRTC'> <class 'BRVC'>
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: <class 'ORI'> <class 'SBR'>
// 0111: <class 'ANDI'>
// 0011: <class 'CPI'>
// 0101: <class 'SUBI'>
// 0100: <class 'SBCI'>
// 1110: <class 'LDI'>
// 1100: <class 'RJMP'>
// 1101: <class 'RCALL'>
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))
}

38
src/main.rs Normal file
View File

@ -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<P: AsRef<Path>>(rom_path: P) -> Result<Box<[u8]>, 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())
}

148
src/regs.rs Normal file
View File

@ -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<u8> 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<Self, ()> {
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<Self, ()> {
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<u8> for GeneralPurposeRegister {
fn from(v: u8) -> Self {
if v > 31 {
unreachable!();
}
Self{0: v}
}
}
impl Into<u8> for GeneralPurposeRegister {
fn into(self) -> u8 {
self.0
}
}
impl Into<usize> 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<u8> for GeneralPurposeRegisterPair {
fn from(v: u8) -> Self {
if v > 30 {
println!("v={}", v);
unreachable!();
}
Self{0: v}
}
}
impl Into<u8> 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;