Initial commit
This commit is contained in:
commit
dcbc514485
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target/
|
||||
**/*.rs.bk
|
||||
6
Cargo.toml
Normal file
6
Cargo.toml
Normal 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
70
src/cpu.rs
Normal 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
524
src/decoder.rs
Normal 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
38
src/main.rs
Normal 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
148
src/regs.rs
Normal 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;
|
||||
Loading…
Reference in New Issue
Block a user