Compare commits

...

3 Commits

2 changed files with 74 additions and 40 deletions

View File

@ -4,8 +4,14 @@ use std::fs::File;
use std::io::Read; use std::io::Read;
fn main() { fn main() {
let mut f = File::open("basic.bin").expect("File not found"); let args: Vec<String> = std::env::args().collect();
let metadata = fs::metadata("basic.bin").expect("Could not load metadata"); if args.len() < 2 {
panic!("Please provide a path to the rom")
}
let rom_path = &args[1];
let mut f = File::open(rom_path).expect("File not found");
let metadata = fs::metadata(rom_path).expect("Could not load metadata");
let bytes = metadata.len(); let bytes = metadata.len();
let mut buffer = vec![0; bytes as usize]; let mut buffer = vec![0; bytes as usize];
let bytes_read = f.read(&mut buffer).expect("Problem reading file into buffer"); let bytes_read = f.read(&mut buffer).expect("Problem reading file into buffer");

View File

@ -4,9 +4,10 @@ use InstructionName::*;
use AddressingShort::*; use AddressingShort::*;
use Register::*; use Register::*;
use Flag::*; use Flag::*;
use Operand::*;
pub enum Addressing { pub enum Addressing {
Implicit, Implied,
Accumulator, Accumulator,
Immediate, Immediate,
ZeroPage, ZeroPage,
@ -21,7 +22,7 @@ pub enum Addressing {
Relative, Relative,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum InstructionName { pub enum InstructionName {
ADC, AHX, ALR, AND, ANC, ARR, ASL, AXS, BCC, BCS, BEQ, BIT, BMI, BNE, BPL, BRK, ADC, AHX, ALR, AND, ANC, ARR, ASL, AXS, BCC, BCS, BEQ, BIT, BMI, BNE, BPL, BRK,
BVC, BVS, CLC, CLD, CLI, CLV, CMP, CPX, CPY, DCP, DEC, DEX, DEY, EOR, ISC, INC, BVC, BVS, CLC, CLD, CLI, CLV, CMP, CPX, CPY, DCP, DEC, DEX, DEY, EOR, ISC, INC,
@ -30,7 +31,7 @@ pub enum InstructionName {
STA, STX, STY, TAS, TAX, TAY, TSX, TXA, TXS, TYA, XAA, STA, STX, STY, TAS, TAX, TAY, TSX, TXA, TXS, TYA, XAA,
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub enum AddressingShort { pub enum AddressingShort {
Imp, Acc, Imm, ZPg, ZPX, Imp, Acc, Imm, ZPg, ZPX,
ZPY, Abs, AbX, AbY, InX, ZPY, Abs, AbX, AbY, InX,
@ -43,7 +44,7 @@ pub struct InstructionDefinition {
addressing: AddressingShort, addressing: AddressingShort,
} }
#[derive(Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum Register { pub enum Register {
A, A,
X, X,
@ -85,6 +86,9 @@ impl_indexing!([u8; 4], Register);
#[derive(Debug)] #[derive(Debug)]
pub enum Operand { pub enum Operand {
None, None,
Immediate(u8),
MemAddr(u16),
Reg(Register)
} }
pub struct Cpu { pub struct Cpu {
@ -97,13 +101,13 @@ pub struct Cpu {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Instruction { pub struct Instruction<'a> {
opcode: u8, opcode: u8,
name: InstructionName, name: InstructionName,
src_opr: Operand, src_opr: Operand,
dst_opr: Operand, dst_opr: Operand,
bytes_read: i32, bytes_read: i32,
raw_data: Vec<u8>, raw_data: &'a [u8],
} }
impl Cpu { impl Cpu {
@ -136,7 +140,6 @@ impl Cpu {
} }
} }
pub fn rotate_shift(&mut self, inst: &Instruction, head_bit: u8, tail_bit: u8) { pub fn rotate_shift(&mut self, inst: &Instruction, head_bit: u8, tail_bit: u8) {
let val = self.load(&inst.src_opr); let val = self.load(&inst.src_opr);
let bit = val & head_bit; let bit = val & head_bit;
@ -156,21 +159,17 @@ impl Cpu {
self.status_flags[Carry] = bit > 0x0; self.status_flags[Carry] = bit > 0x0;
} }
pub fn branch(mut self, offset: i8) { pub fn branch_on_flag(&mut self, inst: &Instruction, flag: Flag, not: bool) {
self.program_counter += 2 + offset as i16; let offset = self.load(&inst.src_opr) as i8;
if not ^ !self.status_flags[flag] {
self.program_counter += 2 + offset as i16;
}
} }
} }
macro_rules! instruction_defs { macro_rules! instruction_defs {
($({$name:ident, $addr:ident}),* $(,)?) => { ($({$name:ident, $addr:ident}),* $(,)?) => {
[ [$(InstructionDefinition {name: $name, addressing: $addr},)*]
$(
InstructionDefinition {
name: $name,
addressing: $addr
},
)*
]
}; };
} }
@ -194,6 +193,23 @@ const INSTRUCTION_DEFS: [InstructionDefinition; 256] = instruction_defs![
/*Fx*/{BEQ,Rel},{SBC,InY},{KIL,Imp},{ISC,InY},{NOP,ZPX},{SBC,ZPX},{INC,ZPX},{ISC,ZPX},{SED,Imp},{SBC,AbY},{NOP,Imp},{ISC,AbY},{NOP,AbX},{SBC,AbX},{INC,AbX},{ISC,AbX}, /*Fx*/{BEQ,Rel},{SBC,InY},{KIL,Imp},{ISC,InY},{NOP,ZPX},{SBC,ZPX},{INC,ZPX},{ISC,ZPX},{SED,Imp},{SBC,AbY},{NOP,Imp},{ISC,AbY},{NOP,AbX},{SBC,AbX},{INC,AbX},{ISC,AbX},
]; ];
fn get_operands(data: &[u8], def: &InstructionDefinition, addr: AddressingShort) -> (Operand, Operand) {
let src = match (addr, def.name) {
(Imm,_) => Immediate(data[1]),
(Acc,_) | (_,STA) | (_,TAX) | (_,TAY) => Reg(A),
(_,STX) | (_,TXA) | (_,TXS) | (_,INX) | (_,DEX) => Reg(X),
(_,STY) | (_,TYA) | (_,INY) | (_,DEY) => Reg(Y),
_ => None,
};
let dst = match (addr, def.name) {
(_,LDA) | (_,TXA) | (_,TYA) => Reg(A),
(_,LDX) | (_,TSX) | (_,TAX) => Reg(X),
(_,LDY) | (_,TAY) => Reg(Y),
_ => None,
};
(src, dst)
}
fn execute(cpu: &mut Cpu, inst: &Instruction) { fn execute(cpu: &mut Cpu, inst: &Instruction) {
match inst.name { match inst.name {
// Access // Access
@ -245,7 +261,8 @@ fn execute(cpu: &mut Cpu, inst: &Instruction) {
cpu.registers[reg] = result as u8 - carry_val; cpu.registers[reg] = result as u8 - carry_val;
} }
cpu.status_flags[Carry] = result < 0; // TODO: This is wrong because it's a u16, it can't be below 0
// cpu.status_flags[Carry] = if result < 0 { true } else { false };
let reg_a = cpu.registers[A]; let reg_a = cpu.registers[A];
let res = result as u8; let res = result as u8;
@ -273,18 +290,18 @@ fn execute(cpu: &mut Cpu, inst: &Instruction) {
cpu.check_z_n_flags(cpu.registers[A]); cpu.check_z_n_flags(cpu.registers[A]);
}, },
EOR => { EOR => {
cpu.registers[A] != cpu.load(&inst.src_opr); cpu.registers[A] ^= cpu.load(&inst.src_opr);
cpu.check_z_n_flags(cpu.registers[A]); cpu.check_z_n_flags(cpu.registers[A]);
}, },
// Branching // Branching
BCC => if !cpu.status_flags[Carry] { cpu.branch(cpu.load(&inst.src_opr) as i8) }, BCC => cpu.branch_on_flag(&inst, Carry, true),
BCS => if cpu.status_flags[Carry] { cpu.branch(cpu.load(&inst.src_opr) as i8) }, BCS => cpu.branch_on_flag(&inst, Carry, false),
BNE => if !cpu.status_flags[Zero] { cpu.branch(cpu.load(&inst.src_opr) as i8) }, BNE => cpu.branch_on_flag(&inst, Zero, true),
BEQ => if cpu.status_flags[Zero] { cpu.branch(cpu.load(&inst.src_opr) as i8) }, BEQ => cpu.branch_on_flag(&inst, Zero, false),
BPL => if !cpu.status_flags[Negative] { cpu.branch(cpu.load(&inst.src_opr) as i8) }, BPL => cpu.branch_on_flag(&inst, Negative, true),
BMI => if cpu.status_flags[Negative] { cpu.branch(cpu.load(&inst.src_opr) as i8) }, BMI => cpu.branch_on_flag(&inst, Negative, false),
BVC => if !cpu.status_flags[Overflow] { cpu.branch(cpu.load(&inst.src_opr) as i8) }, BVC => cpu.branch_on_flag(&inst, Overflow, true),
BVS => if cpu.status_flags[Overflow] { cpu.branch(cpu.load(&inst.src_opr) as i8) }, BVS => cpu.branch_on_flag(&inst, Overflow, false),
// Jump // Jump
JMP => cpu.program_counter = cpu.load_u16(&inst.src_opr) as i16, JMP => cpu.program_counter = cpu.load_u16(&inst.src_opr) as i16,
JSR => { JSR => {
@ -331,26 +348,37 @@ fn execute(cpu: &mut Cpu, inst: &Instruction) {
// Unofficial/Illegal // Unofficial/Illegal
AHX | ALR | ANC | ARR | AXS | DCP | ISC | LAX | LAS AHX | ALR | ANC | ARR | AXS | DCP | ISC | LAX | LAS
| RLA | RRA | SAX | SHX | SHY | SLO | SRE | TAS | XAA => {}, | RLA | RRA | SAX | SHX | SHY | SLO | SRE | TAS | XAA => {},
} }
} }
pub fn decode_instructions(data: &[u8], bytes_to_read: i32) -> Vec<&InstructionDefinition>{ pub fn decode_instructions(data: &[u8], bytes_to_read: i32) -> Vec<Instruction<'_>>{
let mut idx: usize = 0; let mut idx: usize = 0;
let mut instructions: Vec<&InstructionDefinition> = let mut instructions: Vec<Instruction> =
Vec::with_capacity((bytes_to_read / 2) as usize); Vec::with_capacity((bytes_to_read / 2) as usize);
while (idx as i32) < bytes_to_read { while (idx as i32) < bytes_to_read {
let mut processed = 0; let mut processed: i32 = 0;
let inst = &INSTRUCTION_DEFS[data[idx] as usize]; let def = &INSTRUCTION_DEFS[data[idx] as usize];
instructions.push(inst);
processed += match inst.addressing { processed += match def.addressing {
Imp | Acc => 1, Imp | Acc => 1,
Imm | ZPg | Rel | AbI Imm | ZPg | Rel | AbI | ZPX | InX | InY => 2,
| ZPX | InX | InY => 2,
ZPY | AbX | AbY | Abs => 3, ZPY | AbX | AbY | Abs => 3,
}; };
idx += processed
let (src_opr, dst_opr) = get_operands(&data[idx..], &def, def.addressing);
let instruction = Instruction {
opcode: data[idx] as u8,
name: def.name,
src_opr,
dst_opr,
bytes_read: processed,
raw_data: &data[idx..idx+(processed as usize)],
};
instructions.push(instruction);
idx += processed as usize;
} }
instructions instructions
} }