Compare commits
3 Commits
d89fc1cd2b
...
8ed93fc90e
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ed93fc90e | |||
| 0c475127f6 | |||
| e5c2319803 |
5
.bad/.gitignore
vendored
Normal file
5
.bad/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
8
.bad/modules.xml
Normal file
8
.bad/modules.xml
Normal 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
13
.bad/mos6502.iml
Normal 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
6
.bad/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -1,5 +1,2 @@
|
||||
[alias]
|
||||
coverage = "tarpaulin --out Html --skip-clean --output-dir coverage"
|
||||
|
||||
[build]
|
||||
rustc-wrapper = "sccache"
|
||||
|
||||
2
.idea/mos6502.iml
generated
2
.idea/mos6502.iml
generated
@ -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
63
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
||||
@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
9
beneater/src/parts/address_bus.rs
Normal file
9
beneater/src/parts/address_bus.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub struct AddressBus {
|
||||
pub address: u16
|
||||
}
|
||||
|
||||
impl AddressBus {
|
||||
pub fn new() -> Self {
|
||||
AddressBus { address: 0x0000 }
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
beneater/src/parts/cpu_display.rs
Normal file
28
beneater/src/parts/cpu_display.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
9
beneater/src/parts/data_bus.rs
Normal file
9
beneater/src/parts/data_bus.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub struct DataBus {
|
||||
pub data: u8
|
||||
}
|
||||
|
||||
impl DataBus {
|
||||
pub fn new() -> Self {
|
||||
DataBus { data: 0x00 }
|
||||
}
|
||||
}
|
||||
79
beneater/src/parts/display_matrix.rs
Normal file
79
beneater/src/parts/display_matrix.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
20
beneater/src/parts/ram_display.rs
Normal file
20
beneater/src/parts/ram_display.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -4,3 +4,4 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
rand = "0.9.0"
|
||||
@ -1,4 +1,3 @@
|
||||
use std::fmt::{Display};
|
||||
|
||||
#[derive(PartialEq, Debug, Copy, Clone)]
|
||||
pub enum AddressMode {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
88
core/src/periph/hm62256.rs
Normal file
88
core/src/periph/hm62256.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
pub mod rom_chip;
|
||||
|
||||
pub mod at28c256;
|
||||
pub mod ram_chip;
|
||||
mod hm62256;
|
||||
|
||||
|
||||
5
core/src/periph/ram_chip.rs
Normal file
5
core/src/periph/ram_chip.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use crate::periph::rom_chip::RomChip;
|
||||
|
||||
pub trait RamChip: RomChip {
|
||||
fn write(&mut self, offset: &u16, value: &u8);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user