Compare commits
3 Commits
102c08569a
...
2068cf08e1
Author | SHA1 | Date | |
---|---|---|---|
2068cf08e1 | |||
132cc7fb9d | |||
a1386a289f |
10
src/main.rs
10
src/main.rs
@ -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");
|
||||||
|
104
src/sim6502.rs
104
src/sim6502.rs
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user