Compare commits

...

3 Commits

Author SHA1 Message Date
8ed93fc90e closer to being able to run a cycle? 2025-06-30 07:17:17 -04:00
0c475127f6 BUGFIX: Decoder now decodes instructions without parameters 2025-06-29 10:55:47 -04:00
e5c2319803 removes sccache
updates display of ben eater pc
doesnt blow up when creating a rom chip anymore
doesnt blow up when creating a pc anymore
2025-06-28 12:37:01 -04:00
27 changed files with 530 additions and 86 deletions

5
.bad/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

8
.bad/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/mos6502.iml" filepath="$PROJECT_DIR$/.idea/mos6502.iml" />
</modules>
</component>
</project>

13
.bad/mos6502.iml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/cli/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/core/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/macroquad/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.bad/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -1,5 +1,2 @@
[alias]
coverage = "tarpaulin --out Html --skip-clean --output-dir coverage"
[build]
rustc-wrapper = "sccache"

2
.idea/mos6502.iml generated
View File

@ -6,6 +6,8 @@
<sourceFolder url="file://$MODULE_DIR$/core/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/macroquad/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/beneater/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/core/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />

63
Cargo.lock generated
View File

@ -75,9 +75,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "beneater"
@ -185,6 +185,7 @@ name = "core"
version = "0.1.0"
dependencies = [
"log",
"rand",
]
[[package]]
@ -474,6 +475,15 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "pretty_env_logger"
version = "0.5.0"
@ -514,6 +524,35 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "regex"
version = "1.11.1"
@ -823,3 +862,23 @@ checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.1",
]
[[package]]
name = "zerocopy"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@ -1,5 +1,10 @@
// This is the GUI for the BenEater PC
use beneater::parts::cpu_display::CpuDisplay;
use macroquad::prelude::*;
use macroquad::telemetry::frame;
use beneater::parts::ben_eater_pc::BenEaterPC;
use beneater::parts::display_matrix::DisplayMatrix;
#[macroquad::main("Ben Eaters PC")]
async fn main() {
@ -7,10 +12,32 @@ async fn main() {
let computer = BenEaterPC::new();
let mut dm = DisplayMatrix::new(200.0, 50.0);
let message_to_show = "Taxation is theft";
let mut message_index = 0;
dm.push_letter('T');
let mut frame_number: u32 = 0x00;
loop {
clear_background(BLUE);
draw_text("Ben Eater", 20.0, 20.0, 30.0, BLACK);
dm.render(20.0, 40.0);
CpuDisplay::render(&computer.cpu, 20.0, 120.0);
frame_number += 1;
if frame_number.is_multiple_of(60) {
dm.push_letter('X');
}
if frame_number.is_multiple_of(60 * 6) {
dm.clear_display()
}
next_frame().await
}
}

View File

@ -0,0 +1,9 @@
pub struct AddressBus {
pub address: u16
}
impl AddressBus {
pub fn new() -> Self {
AddressBus { address: 0x0000 }
}
}

View File

@ -1,13 +1,17 @@
use crate::parts::clock::Clock;
use core::mos6502cpu::Mos6502Cpu;
struct BenEaterPC {
pub struct BenEaterPC {
clock: Clock,
cpu: Mos6502Cpu,
// pub because i am rendering it.
// there should be a proper interface to get the data
// for ui out of this.
pub cpu: Mos6502Cpu,
}
impl BenEaterPC {
pub fn new() -> Self {
println!("New BENEATERPC");
BenEaterPC {
clock: Clock::new(),
cpu: Mos6502Cpu::default()
@ -20,6 +24,8 @@ impl BenEaterPC {
// tick the clock.
// tick the memory
// tick the VIA
} else {
self.cpu.tick();
}
}
}

View File

@ -0,0 +1,28 @@
use macroquad::color::{BLACK, Color};
use macroquad::prelude::*;
use macroquad::text::draw_text;
use core::mos6502cpu::Mos6502Cpu;
pub struct CpuDisplay {}
impl CpuDisplay {
pub fn render(cpu: &Mos6502Cpu, x_offset: f32, y_offset: f32) {
// get the data to display...
let (pc, a, x, y, address_bus, data_bus) = cpu.dump_data();
// ...build the interface
Self::draw_square(x_offset, y_offset, x_offset + 300.0, y_offset + 85.0, BLACK);
draw_text(format!("PC: 0x{:04x} / {}", pc, pc).as_str(), x_offset + 5.0, y_offset + 18.0, 15.0, BLACK);
draw_text(format!("A: 0x{:02x} X: 0x{:02x} Y: 0x{:02x}", a, x, y).as_str(), x_offset + 5.0, y_offset + 35.0, 15.0, BLACK);
draw_text(format!("Address: {:016b} | {:04x}", address_bus, address_bus).as_str(), x_offset + 5.0, y_offset + 55.0, 15.0, BLACK);
draw_text(format!("Data: {:08b} | {:02x}", data_bus, data_bus).as_str(), x_offset + 5.0, y_offset + 75.0, 15.0, BLACK);
}
fn draw_square(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
// println!("Square from {x1:2.0}x{y1:2.0} to {x2:2.0}x{y2:2.0} with {:?}", color);
draw_line(x1, y1, x2, y1, 1.0, color);
draw_line(x1, y1, x1, y2, 1.0, color);
draw_line(x1, y2, x2, y2, 1.0, color);
draw_line(x2, y1, x2, y2, 1.0, color);
}
}

View File

@ -0,0 +1,9 @@
pub struct DataBus {
pub data: u8
}
impl DataBus {
pub fn new() -> Self {
DataBus { data: 0x00 }
}
}

View File

@ -0,0 +1,79 @@
use macroquad::prelude::*;
use crate::parts::address_bus::AddressBus;
use crate::parts::data_bus::DataBus;
pub struct DisplayMatrix {
width: f32,
height: f32,
text_buffer: String,
data_bus: DataBus,
rs: bool,
rw: bool,
cursor_position: u8
}
impl DisplayMatrix {
pub fn new(width: f32, height: f32) -> DisplayMatrix {
DisplayMatrix {
width,
height,
text_buffer: String::from(""),
data_bus: DataBus::new(),
rs: false,
rw: false,
cursor_position: 0x00
}
}
/// Tick
///
/// Checks the data bus and sees what the setup is.
///
/// 0 0 0 0 0 0 0 1 -> Clear Display
/// 0 0 0 0 0 0 1 - -> Return Home
/// 0 0 0 0 0 0 A B -> Sets cursor move direction and shift
/// A -> 0 = Insert, 1 = Delete
/// B -> Scroll bool
/// 0 0 0 0 1 D C B -> Sets display mode
/// D -> Display On/Off
/// C -> Cursor On/Off
/// B -> Blink
pub fn tick(&mut self) {
// parse whats on the data bus.
}
pub fn set_busses(&mut self, address_bus: &mut AddressBus, data_bus: &mut DataBus) {
}
pub fn push_letter(&mut self, letter_to_push: char) {
self.cursor_position += 1;
self.text_buffer.push(letter_to_push)
}
pub fn clear_display(&mut self) {
self.cursor_position = 0;
self.text_buffer.clear()
}
pub fn render(&self, x_offset: f32, y_offset: f32) {
DisplayMatrix::draw_square(x_offset,
y_offset,
x_offset + self.width ,
y_offset + self.height,
BLACK);
let mut tmp = self.text_buffer.clone();
tmp.push('#');
draw_text(&*tmp, x_offset + 5.0, y_offset + 20.0, 20.0, BLACK);
}
fn draw_square(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
// println!("Square from {x1:2.0}x{y1:2.0} to {x2:2.0}x{y2:2.0} with {:?}", color);
draw_line(x1, y1, x2, y1, 1.0, color);
draw_line(x1, y1, x1, y2, 1.0, color);
draw_line(x1, y2, x2, y2, 1.0, color);
draw_line(x2, y1, x2, y2, 1.0, color);
}
}

View File

@ -1,3 +1,8 @@
pub mod clock;
mod ben_eater_pc;
mod via6522;
pub mod ben_eater_pc;
pub mod via6522;
pub mod display_matrix;
pub mod address_bus;
pub mod data_bus;
pub mod cpu_display;
mod ram_display;

View File

@ -0,0 +1,20 @@
use macroquad::prelude::*;
pub struct RamDisplay {}
impl RamDisplay {
pub fn render(ram: &Box<[u8]>, x_offset: f32, y_offset: f32) {
Self::draw_square(x_offset, y_offset, x_offset + 200.0, y_offset + 300.0, BLACK);
draw_text("RAM", x_offset + 5.0, y_offset + 5.0, 15.0, BLACK);
}
fn draw_square(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
// println!("Square from {x1:2.0}x{y1:2.0} to {x2:2.0}x{y2:2.0} with {:?}", color);
draw_line(x1, y1, x2, y1, 1.0, color);
draw_line(x1, y1, x1, y2, 1.0, color);
draw_line(x1, y2, x2, y2, 1.0, color);
draw_line(x2, y1, x2, y2, 1.0, color);
}
}

View File

@ -1,5 +1,12 @@
pub enum Via6522Port {
A(u8),
B(u8)
}
#[derive(Default)]
pub struct Via6522 {
data_bus: u8,
address_bus: u16,
port_a_state: u8,
port_b_data: u8,
port_a_direction: u8,
@ -15,16 +22,23 @@ impl Via6522 {
}
}
pub fn set_a_direction(&mut self, new_direction: u8) {
pub fn set_port_direction(&mut self, port: Via6522Port) {
match port {
Via6522Port::A(x) => {
self.port_a_direction = x;
},
Via6522Port::B(x) => {
self.port_b_direction = x;
}
}
}
// check for output pins and see what they say
/// check for output pins and see what they say
pub fn update_pins(&mut self) {
}
// check for input mode pins and see what they say
/// check for input mode pins and see what they say
pub fn read_pins(&self) {
}

View File

@ -1,11 +1,22 @@
use core::instruction::Instruction;
use core::address_mode::AddressMode;
use core::operand::Operand;
use core::operation::Operation;
fn main() {
println!("Taxation is Theft");
// Instruction::from_bytes(vec![0b11100011]);
let instructions = vec![(
Instruction {
op: Operation::NOP,
mode: AddressMode::Implied,
operand: Operand::None,
}, &[0xea]
)];
// let instruction = Instruction::ADC(AddressMode::Immediate);
// println!("Instruction = {:?}", instruction.to_string());
for (op, bytes) in instructions {
assert_eq!(Instruction::decode(bytes), Some(op));
}
// let instruction = Instruction::decode(&[0xea]);
// println!("NOP Decoded -> {:?}", instruction);
}

View File

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
log = "0.4"
rand = "0.9.0"

View File

@ -1,4 +1,3 @@
use std::fmt::{Display};
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AddressMode {

View File

@ -15,6 +15,7 @@ pub struct Instruction {
impl Instruction {
pub fn decode(bytes: &[u8]) -> Option<Instruction> {
println!("DECODING : {bytes:?}");
let opcode = bytes.get(0).copied()?;
let info = INSTRUCTION_TABLE[opcode as usize]?;
@ -25,14 +26,18 @@ impl Instruction {
let hi = *bytes.get(2)?;
Operand::Word(u16::from_le_bytes([lo, hi]))
}
_ => return None,
_ => Operand::None,
};
Some(Instruction {
let return_value = Some(Instruction {
op: info.operation,
mode: info.mode,
operand,
})
});
println!("RETURNING: {:?}", return_value);
return_value
}
}
@ -41,7 +46,7 @@ impl Instruction {
mod test {
use crate::address_mode::AddressMode::*;
use crate::instruction::Instruction;
use crate::operation::Operation::ADC;
use crate::operation::Operation::{ADC, INY, LSR};
use super::*;
#[test]
@ -75,14 +80,14 @@ mod test {
(vec![0x24, 0xab], Instruction { op: BIT, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0x2c, 0xcd, 0xab], Instruction { op: BIT, mode: Absolute, operand: Operand::Word(0xabcd) }),
// BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS
(vec![0x10, 0xab], Instruction { op: BPL, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0x30, 0xab], Instruction { op: BMI, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0x50, 0xab], Instruction { op: BVC, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0x70, 0xab], Instruction { op: BVS, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0x90, 0xab], Instruction { op: BCC, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0xb0, 0xab], Instruction { op: BCS, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0xd0, 0xab], Instruction { op: BNE, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0xf0, 0xab], Instruction { op: BEQ, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0x10, 0xab], Instruction { op: BPL, mode: Implied, operand: Operand::Byte(0xab) }),
(vec![0x30, 0xab], Instruction { op: BMI, mode: Implied, operand: Operand::Byte(0xab) }),
(vec![0x50, 0xab], Instruction { op: BVC, mode: Implied, operand: Operand::Byte(0xab) }),
(vec![0x70, 0xab], Instruction { op: BVS, mode: Implied, operand: Operand::Byte(0xab) }),
(vec![0x90, 0xab], Instruction { op: BCC, mode: Implied, operand: Operand::Byte(0xab) }),
(vec![0xb0, 0xab], Instruction { op: BCS, mode: Implied, operand: Operand::Byte(0xab) }),
(vec![0xd0, 0xab], Instruction { op: BNE, mode: Implied, operand: Operand::Byte(0xab) }),
(vec![0xf0, 0xab], Instruction { op: BEQ, mode: Implied, operand: Operand::Byte(0xab) }),
// BRK
(vec![0x00], Instruction { op: BRK, mode: Implied, operand: Operand::None }),
// CLC, CLD, CLI, CLV
@ -129,7 +134,7 @@ mod test {
(vec![0xee, 0xcd, 0xab], Instruction { op: INC, mode: Absolute, operand: Operand::Word(0xabcd) }),
(vec![0xfe, 0xcd, 0xab], Instruction { op: INC, mode: AbsoluteX, operand: Operand::Word(0xabcd) }),
(vec![0xe8], Instruction { op: INX, mode: Implied, operand: Operand::None }),
(vec![0xc8], Instruction { op: INX, mode: Implied, operand: Operand::None }),
(vec![0xc8], Instruction { op: INY, mode: Implied, operand: Operand::None }),
// JMP, JSR
(vec![0x4c, 0xcd, 0xab], Instruction { op: JMP, mode: Absolute, operand: Operand::Word(0xabcd) }),
(vec![0x6c, 0xcd, 0xab], Instruction { op: JMP, mode: Indirect, operand: Operand::Word(0xabcd) }),
@ -146,21 +151,21 @@ mod test {
// LDX
(vec![0xa2, 0xab], Instruction { op: LDX, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0xa6, 0xab], Instruction { op: LDX, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0xb6, 0xab], Instruction { op: LDX, mode: ZeroPageX, operand: Operand::Byte(0xab) }),
(vec![0xb6, 0xab], Instruction { op: LDX, mode: ZeroPageY, operand: Operand::Byte(0xab) }),
(vec![0xae, 0xcd, 0xab], Instruction { op: LDX, mode: Absolute, operand: Operand::Word(0xabcd) }),
(vec![0xbe, 0xcd, 0xab], Instruction { op: LDX, mode: AbsoluteX, operand: Operand::Word(0xabcd) }),
(vec![0xbe, 0xcd, 0xab], Instruction { op: LDX, mode: AbsoluteY, operand: Operand::Word(0xabcd) }),
// LDY
(vec![0xa2, 0xab], Instruction { op: LDY, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0xa6, 0xab], Instruction { op: LDY, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0xb6, 0xab], Instruction { op: LDY, mode: ZeroPageX, operand: Operand::Byte(0xab) }),
(vec![0xae, 0xcd, 0xab], Instruction { op: LDY, mode: Absolute, operand: Operand::Word(0xabcd) }),
(vec![0xbe, 0xcd, 0xab], Instruction { op: LDY, mode: AbsoluteX, operand: Operand::Word(0xabcd) }),
(vec![0xa0, 0xab], Instruction { op: LDY, mode: Immediate, operand: Operand::Byte(0xab) }),
(vec![0xa4, 0xab], Instruction { op: LDY, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0xb4, 0xab], Instruction { op: LDY, mode: ZeroPageX, operand: Operand::Byte(0xab) }),
(vec![0xac, 0xcd, 0xab], Instruction { op: LDY, mode: Absolute, operand: Operand::Word(0xabcd) }),
(vec![0xbc, 0xcd, 0xab], Instruction { op: LDY, mode: AbsoluteX, operand: Operand::Word(0xabcd) }),
// LSR
(vec![0x4a, 0xab], Instruction { op: LDY, mode: Accumulator, operand: Operand::Byte(0xab) }),
(vec![0x46, 0xab], Instruction { op: LDY, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0x56, 0xab], Instruction { op: LDY, mode: ZeroPageX, operand: Operand::Byte(0xab) }),
(vec![0x4e, 0xcd, 0xab], Instruction { op: LDY, mode: Absolute, operand: Operand::Word(0xabcd) }),
(vec![0x5e, 0xcd, 0xab], Instruction { op: LDY, mode: AbsoluteX, operand: Operand::Word(0xabcd) }),
(vec![0x4a], Instruction { op: LSR, mode: Accumulator, operand: Operand::None }),
(vec![0x46, 0xab], Instruction { op: LSR, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0x56, 0xab], Instruction { op: LSR, mode: ZeroPageX, operand: Operand::Byte(0xab) }),
(vec![0x4e, 0xcd, 0xab], Instruction { op: LSR, mode: Absolute, operand: Operand::Word(0xabcd) }),
(vec![0x5e, 0xcd, 0xab], Instruction { op: LSR, mode: AbsoluteX, operand: Operand::Word(0xabcd) }),
// NOP
(vec![0xea], Instruction { op: NOP, mode: Implied, operand: Operand::None }),
// ORA
@ -178,13 +183,13 @@ mod test {
(vec![0x68], Instruction { op: PLA, mode: Implied, operand: Operand::None }),
(vec![0x28], Instruction { op: PLP, mode: Implied, operand: Operand::None }),
// ROL
(vec![0x2a, 0xab], Instruction { op: ROL, mode: Accumulator, operand: Operand::Byte(0xab) }),
(vec![0x2a], Instruction { op: ROL, mode: Accumulator, operand: Operand::None }),
(vec![0x26, 0xab], Instruction { op: ROL, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0x36, 0xab], Instruction { op: ROL, mode: ZeroPageX, operand: Operand::Byte(0xab) }),
(vec![0x2e, 0xcd, 0xab], Instruction { op: ROL, mode: Absolute, operand: Operand::Word(0xabcd) }),
(vec![0x3e, 0xcd, 0xab], Instruction { op: ROL, mode: AbsoluteX, operand: Operand::Word(0xabcd) }),
// ROR
(vec![0x6a, 0xab], Instruction { op: ROR, mode: Accumulator, operand: Operand::Byte(0xab) }),
(vec![0x6a], Instruction { op: ROR, mode: Accumulator, operand: Operand::None }),
(vec![0x66, 0xab], Instruction { op: ROR, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0x76, 0xab], Instruction { op: ROR, mode: ZeroPageX, operand: Operand::Byte(0xab) }),
(vec![0x6e, 0xcd, 0xab], Instruction { op: ROR, mode: Absolute, operand: Operand::Word(0xabcd) }),
@ -215,7 +220,7 @@ mod test {
(vec![0x91, 0xab], Instruction { op: STA, mode: IndirectY, operand: Operand::Byte(0xab) }),
// STX
(vec![0x86, 0xab], Instruction { op: STX, mode: ZeroPage, operand: Operand::Byte(0xab) }),
(vec![0x96, 0xab], Instruction { op: STX, mode: ZeroPageX, operand: Operand::Byte(0xab) }),
(vec![0x96, 0xab], Instruction { op: STX, mode: ZeroPageY, operand: Operand::Byte(0xab) }),
(vec![0x8e, 0xcd, 0xab], Instruction { op: STX, mode: Absolute, operand: Operand::Word(0xabcd) }),
// STY
(vec![0x84, 0xab], Instruction { op: STY, mode: ZeroPage, operand: Operand::Byte(0xab) }),
@ -230,13 +235,12 @@ mod test {
(vec![0x98], Instruction { op: TYA, mode: Implied, operand: Operand::None })
];
for (bytes, instruction) in params {
let result = Instruction::decode(&bytes);
if let Some(instruction) = result {
assert_eq!(Instruction::decode(&bytes).unwrap(), instruction)
let result1 = Instruction::decode(&bytes);
if let Some(instruction1) = result1 {
assert_eq!(instruction, instruction1)
} else {
println!("Failed to decode {:?}", bytes);
}
}
}
}

View File

@ -256,13 +256,13 @@ pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
table[ISA_OP_CMP_ABSX as usize] = Some(OpInfo {
operation: CMP,
mode: AddressMode::AbsoluteX,
length: 2,
length: 3,
cycles: 4,
});
table[ISA_OP_CMP_ABSY as usize] = Some(OpInfo {
operation: CMP,
mode: AddressMode::AbsoluteY,
length: 2,
length: 3,
cycles: 4,
});
table[ISA_OP_CMP_INDX as usize] = Some(OpInfo {

View File

@ -1,11 +1,17 @@
use crate::address_mode::AddressMode;
use crate::instruction::Instruction;
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
use crate::operand::Operand;
use crate::operation::Operation;
use crate::operation::Operation::NOP;
pub const SIZE_1KB: usize = 1024 * 1024;
pub const SIZE_32KB: usize = SIZE_1KB * 32;
pub const SIZE_64KB: usize = SIZE_1KB * 64;
pub struct Mos6502Cpu {
memory: [u8; SIZE_64KB],
// this is public for rendering quickly.
pub memory: Box<[u8]>,
a: u8,
x: u8,
y: u8,
@ -13,33 +19,44 @@ pub struct Mos6502Cpu {
pc: u16,
s: u8,
pub microcode_step: u8,
address_bus: u16,
data_bus: u8,
ir: u8 // Instruction Register
pub address_bus: u16,
pub data_bus: u8,
ir: Instruction // Instruction Register
}
impl Default for Mos6502Cpu {
fn default() -> Self {
let vec = vec![0x00; SIZE_64KB];
let boxed_slize: Box<[u8]> = vec.into_boxed_slice();
let boxed_array: Box<[u8; SIZE_64KB]> = boxed_slize.try_into().expect("Failed to allocate system memory");
Mos6502Cpu {
memory: [0x00; SIZE_64KB],
memory: boxed_array,
a: 0,
x: 0,
y: 0,
flags: Default::default(),
pc: 0,
pc: 0xfffd,
s: 0,
microcode_step: 0,
address_bus: 0,
data_bus: 0,
ir: 0x00
ir: Instruction {
op: Operation::NOP,
mode: AddressMode::Implied,
operand: Operand::None,
}
}
}
}
impl Mos6502Cpu {
pub fn new() -> Mos6502Cpu {
let vec = vec![0x00; SIZE_64KB];
let boxed_slize: Box<[u8]> = vec.into_boxed_slice();
let boxed_array: Box<[u8; SIZE_64KB]> = boxed_slize.try_into().expect("Failed to allocate system memory");
Mos6502Cpu {
memory: [0; SIZE_64KB],
memory: boxed_array,
a: 0,
x: 0,
y: 0,
@ -49,7 +66,11 @@ impl Mos6502Cpu {
microcode_step: 0,
address_bus: 0x0000,
data_bus: 0x00,
ir: 0x00
ir: Instruction {
op: Operation::NOP,
mode: AddressMode::Implied,
operand: Operand::None,
}
}
}
@ -95,6 +116,21 @@ impl Mos6502Cpu {
/// Returns
/// AddressBus, DataBus, RW flag
pub fn tick(&mut self) -> (u16, u8, bool) {
println!("PREPARiNG TO TICK CPU AT {:04x}", self.pc);
let mut num_microsteps_left = 0;
if num_microsteps_left == 0 {
println!("OUT OF MICROSTEPS. Time To do something that isnt microstep.");
// load the microstep buffer with what steps to run
// set the counter to the number of steps left
} else {
// run 1 microcode step
println!("Microstep {num_microsteps_left}");
num_microsteps_left -= 1;
}
(0,0,false)
}
@ -103,5 +139,11 @@ impl Mos6502Cpu {
self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus);
}
pub fn dump_data(&self) -> ( u16, u8, u8, u8, u16, u8) {
(self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus)
}
fn run_microstep(&self, instruction: Instruction, step: u8) {
}
}

View File

@ -1,4 +1,4 @@
use crate::mos6502cpu::SIZE_32KB;
use crate::mos6502cpu::{SIZE_32KB};
use crate::periph::rom_chip::RomChip;
/// At28C256
@ -8,24 +8,37 @@ use crate::periph::rom_chip::RomChip;
/// 256kbit storage
/// 32kbyte storage
pub struct At28C256 {
data: [u8; SIZE_32KB]
data: Box<[u8; SIZE_32KB]>,
}
impl Default for At28C256 {
fn default() -> Self {
let vec = vec![0x00; SIZE_32KB];
let boxed_slice: Box<[u8]> = vec.into_boxed_slice();
let boxed_array: Box<[u8; SIZE_32KB]> = boxed_slice.try_into().expect("Failed to convert Vec to boxed array");
At28C256 {
data: boxed_array,
}
}
}
impl RomChip for At28C256 {
fn read(&self, offset: &u16) -> u8 {
self.data[*offset as usize]
}
fn program(new_data: &[u8; SIZE_32KB]) -> Self {
fn program(new_data: Box<[u8; 33554432]>) -> Box<At28C256> {
println!("Writing new chip.");
At28C256 {
data: *new_data
}
let mut working = At28C256::default();
working.data = Box::new(*new_data);
working.into()
}
}
#[cfg(test)]
mod test {
use crate::mos6502cpu::SIZE_1KB;
use super::*;
#[test]
@ -35,18 +48,14 @@ mod test {
#[test]
fn programmed_data_reads_back_same() {
print!("Starting test...");
let data_to_write = [0xea; SIZE_32KB];
print!("allocated data for rom...");
let chip: At28C256 = At28C256::program(&data_to_write);
println!("programmed chip...");
print!("testing");
for offset in 0..SIZE_32KB {
if offset.is_multiple_of(1000) {
print!(".");
};
assert_eq!(0xea, chip.read(&(offset as u16)));
let mut data = At28C256::default();
for i in 0..SIZE_32KB {
data.data[i] = 0xea;
}
for offset in 0..SIZE_32KB {
if offset.is_multiple_of(SIZE_1KB) {
};
assert_eq!(0xea, data.read(&(offset as u16)));
}
println!("passed!");
}
}

View File

@ -0,0 +1,88 @@
// HM62256 Static Ram
use log::debug;
use crate::mos6502cpu::SIZE_32KB;
use crate::periph::ram_chip::RamChip;
use crate::periph::rom_chip::RomChip;
struct Hm62256 {
base_offset: u16,
data: Box<[u8]>
}
impl Default for Hm62256 {
fn default() -> Self {
let vec = vec![0x00; SIZE_32KB];
let boxed_slice: Box<[u8]> = vec.into_boxed_slice();
let boxed_array: Box<[u8; SIZE_32KB]> = boxed_slice.try_into().expect("Unable to box the ram");
Hm62256 {
base_offset: 0x0000,
data: boxed_array
}
}
}
impl RomChip for Hm62256 {
fn read(&self, offset: &u16) -> u8 {
//println!("READING FROM RAM AT [{offset:04x}]");
self.data[*offset as usize]
}
fn program(_: Box<[u8; SIZE_32KB]>) -> Box<Self> {
debug!("Dont program ram.");
Hm62256::default().into()
}
}
impl RamChip for Hm62256 {
fn write(&mut self, offset: &u16, value: &u8) {
let effective = *offset as i32 % SIZE_32KB as i32;
println!("Writing at E[{effective:04x}] / O[{offset:04x}]");
self.data[effective as usize] = *value;
}
}
#[cfg(test)]
mod test {
use rand::random;
use super::*;
#[test]
fn smoke() { assert!(true) }
#[test]
fn written_data_comes_back() {
let mut ram = Hm62256::default();
// 100,000 random read/writes to ram that all read back right
for _ in 0..100_000 {
let mut offset: u16 = random();
println!("SIze = {SIZE_32KB}");
let value: u8 =random();
println!("Wrote [{value:02x}] to [{offset:04x}]");
ram.write(&offset, &value);
assert_eq!(ram.read(&offset), value)
}
}
#[test]
fn address_space_is_round() {
// addresses written past the last address 'loop' back to 0+(offset - MAX_SIZE)
let max_offset = SIZE_32KB;
let test_offset = max_offset;
// all zero
let mut ram = Hm62256::default();
// write FF to the addresss after the last
ram.write(&(test_offset as u16), &0xff);
// check all the ram for anything that isn't 0x00
assert_eq!(ram.read(&(0x0000)), 0xff);
for offset in 1..SIZE_32KB {
println!("Testing offset {offset:04x} for 0x00");
assert_eq!(ram.read(&(offset as u16)), 0x00);
}
}
}

View File

@ -1,3 +1,6 @@
pub mod rom_chip;
pub mod at28c256;
pub mod ram_chip;
mod hm62256;

View File

@ -0,0 +1,5 @@
use crate::periph::rom_chip::RomChip;
pub trait RamChip: RomChip {
fn write(&mut self, offset: &u16, value: &u8);
}

View File

@ -8,10 +8,5 @@ pub trait RomChip {
/// Program
///
/// Replaces all data in the ROM chip
fn program(new_data: &[u8; SIZE_32KB]) -> Self;
fn program(new_data: Box<[u8; SIZE_32KB]>) -> Box<Self>;
}
pub trait RamChip: RomChip {
fn write(&mut self, offset: &u16, value: &u8);
}