Compare commits
3 Commits
1a53f1d782
...
2cfd570789
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cfd570789 | |||
| e4405cc225 | |||
| 8509b20109 |
@ -3,13 +3,14 @@ use beneater::parts::cpu_display::CpuDisplay;
|
|||||||
use macroquad::prelude::*;
|
use macroquad::prelude::*;
|
||||||
use macroquad::telemetry::frame;
|
use macroquad::telemetry::frame;
|
||||||
use beneater::parts::ben_eater_pc::BenEaterPC;
|
use beneater::parts::ben_eater_pc::BenEaterPC;
|
||||||
use beneater::parts::display_matrix::DisplayMatrix;
|
use beneater::parts::backplane::Backplane;
|
||||||
#[macroquad::main("Ben Eaters PC")]
|
#[macroquad::main("Ben Eaters PC")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
println!("Taxation is Theft");
|
println!("Taxation is Theft");
|
||||||
|
|
||||||
let mut computer = BenEaterPC::new();
|
let mut backplane = Backplane::new();
|
||||||
computer.load_rom("resources/beneater/roms/ror.bin");
|
backplane.load_rom("resources/beneater/roms/ror.bin");
|
||||||
|
|
||||||
|
|
||||||
let mut dm = DisplayMatrix::new(200.0, 50.0);
|
let mut dm = DisplayMatrix::new(200.0, 50.0);
|
||||||
let message_to_show = "Taxation is theft";
|
let message_to_show = "Taxation is theft";
|
||||||
@ -23,7 +24,7 @@ async fn main() {
|
|||||||
|
|
||||||
draw_text("Ben Eater", 20.0, 20.0, 30.0, BLACK);
|
draw_text("Ben Eater", 20.0, 20.0, 30.0, BLACK);
|
||||||
dm.render(20.0, 40.0);
|
dm.render(20.0, 40.0);
|
||||||
CpuDisplay::render(&computer.cpu, 20.0, 120.0);
|
// CpuDisplay::render(&computer.cpu, 20.0, 120.0);
|
||||||
|
|
||||||
frame_number += 1;
|
frame_number += 1;
|
||||||
|
|
||||||
@ -37,6 +38,5 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
next_frame().await
|
next_frame().await
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
beneater/src/parts/backplane.rs
Normal file
58
beneater/src/parts/backplane.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use core::mos6502cpu::Mos6502Cpu;
|
||||||
|
use crate::parts::mos6522_peripheral::Mos6522Peripheral;
|
||||||
|
use crate::parts::via6522::VIA6522;
|
||||||
|
use core::constants::constants_system::*;
|
||||||
|
use core::periph::at28c256::At28C256;
|
||||||
|
pub struct Backplane {
|
||||||
|
cpu: Mos6502Cpu,
|
||||||
|
via: VIA6522,
|
||||||
|
memory: [u8; SIZE_64KB],
|
||||||
|
rom: At28C256
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Backplane {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Backplane {
|
||||||
|
cpu: Mos6502Cpu::default(),
|
||||||
|
via: VIA6522::default(),
|
||||||
|
memory: [0x00; SIZE_64KB],
|
||||||
|
rom: At28C256::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_rom(&mut self, to_load: &[u8; SIZE_32KB]) {
|
||||||
|
self.rom.program(to_load);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
// is the CPU in read or write state
|
||||||
|
let address = self.cpu.address_bus();
|
||||||
|
let data = self.cpu.data_bus();
|
||||||
|
match address {
|
||||||
|
/// VIA
|
||||||
|
0x6000..0x6010 => {
|
||||||
|
if self.cpu.read_signal {
|
||||||
|
self.cpu.set_data_bus(self.via.read((address - 0x6000) as u8));
|
||||||
|
} else {
|
||||||
|
self.via.write((address - 0x6000) as u8, self.cpu.data_bus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// RAM
|
||||||
|
0x0000..=0x3fff => {
|
||||||
|
if self.cpu.read_signal {
|
||||||
|
self.cpu.set_data_bus(self.memory[address as usize]);
|
||||||
|
} else {
|
||||||
|
self.memory[*address as usize] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// ROM
|
||||||
|
0x4000..=0x7fff => {
|
||||||
|
|
||||||
|
}
|
||||||
|
/// The ether. Scarrrrrrrryyyy......
|
||||||
|
_ => {
|
||||||
|
println!("Lost READ:?{} to {:04x}", self.cpu.read_signal, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -23,7 +23,7 @@ impl BenEaterPC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick_system(&mut self) {
|
pub fn tick_system(&mut self) {
|
||||||
let (address, data, rw) = self.cpu.tick();
|
self.cpu.tick();
|
||||||
if self.cpu.microcode_step == 0 {
|
if self.cpu.microcode_step == 0 {
|
||||||
// tick the clock.
|
// tick the clock.
|
||||||
// tick the memory
|
// tick the memory
|
||||||
|
|||||||
@ -1,79 +1,209 @@
|
|||||||
use macroquad::prelude::*;
|
use std::time::{Duration, Instant};
|
||||||
use crate::parts::address_bus::AddressBus;
|
|
||||||
use crate::parts::data_bus::DataBus;
|
|
||||||
|
|
||||||
pub struct DisplayMatrix {
|
#[derive(Debug)]
|
||||||
width: f32,
|
pub struct HD44780 {
|
||||||
height: f32,
|
// Bus inputs
|
||||||
text_buffer: String,
|
data_bus: u8,
|
||||||
data_bus: DataBus,
|
|
||||||
rs: bool,
|
rs: bool,
|
||||||
rw: bool,
|
rw: bool,
|
||||||
cursor_position: u8
|
enable: bool,
|
||||||
|
prev_enable: bool,
|
||||||
|
|
||||||
|
// Internal memory
|
||||||
|
ddram: [u8; 80],
|
||||||
|
cgram: [u8; 64],
|
||||||
|
|
||||||
|
// Cursor & display state
|
||||||
|
cursor_position: u8,
|
||||||
|
display_on: bool,
|
||||||
|
cursor_on: bool,
|
||||||
|
blink_on: bool,
|
||||||
|
entry_increment: bool,
|
||||||
|
entry_shift: bool,
|
||||||
|
|
||||||
|
// Function set flags
|
||||||
|
data_length_8bit: bool,
|
||||||
|
two_line_mode: bool,
|
||||||
|
font_5x10: bool,
|
||||||
|
|
||||||
|
// Busy flag
|
||||||
|
busy: bool,
|
||||||
|
last_command_time: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisplayMatrix {
|
impl HD44780 {
|
||||||
pub fn new(width: f32, height: f32) -> DisplayMatrix {
|
pub fn new() -> Self {
|
||||||
DisplayMatrix {
|
Self {
|
||||||
width,
|
data_bus: 0,
|
||||||
height,
|
|
||||||
text_buffer: String::from(""),
|
|
||||||
data_bus: DataBus::new(),
|
|
||||||
rs: false,
|
rs: false,
|
||||||
rw: false,
|
rw: false,
|
||||||
cursor_position: 0x00
|
enable: false,
|
||||||
|
prev_enable: false,
|
||||||
|
|
||||||
|
ddram: [b' '; 80],
|
||||||
|
cgram: [0; 64],
|
||||||
|
|
||||||
|
cursor_position: 0,
|
||||||
|
display_on: true,
|
||||||
|
cursor_on: false,
|
||||||
|
blink_on: false,
|
||||||
|
entry_increment: true,
|
||||||
|
entry_shift: false,
|
||||||
|
|
||||||
|
data_length_8bit: true,
|
||||||
|
two_line_mode: true,
|
||||||
|
font_5x10: false,
|
||||||
|
|
||||||
|
busy: false,
|
||||||
|
last_command_time: Instant::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_control_lines(&mut self, rs: bool, rw: bool, enable: bool) {
|
||||||
|
self.rs = rs;
|
||||||
|
self.rw = rw;
|
||||||
|
|
||||||
|
// On rising edge of Enable
|
||||||
|
if enable && !self.prev_enable {
|
||||||
|
self.evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.prev_enable = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_data_bus(&mut self, value: u8) {
|
||||||
|
self.data_bus = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_data_bus(&self) -> u8 {
|
||||||
|
if !self.rs && self.rw {
|
||||||
|
// Return busy flag + current address
|
||||||
|
let busy_flag = if self.busy { 0x80 } else { 0x00 };
|
||||||
|
busy_flag | (self.cursor_position & 0x7F)
|
||||||
|
} else {
|
||||||
|
// Not implemented: read from DDRAM/CGRAM
|
||||||
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) {
|
pub fn tick(&mut self) {
|
||||||
// parse whats on the data bus.
|
if self.busy && self.last_command_time.elapsed() > Duration::from_micros(50) {
|
||||||
|
self.busy = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_busses(&mut self, address_bus: &mut AddressBus, data_bus: &mut DataBus) {
|
fn evaluate(&mut self) {
|
||||||
|
if self.rw {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rs {
|
||||||
|
self.write_data(self.data_bus);
|
||||||
|
} else {
|
||||||
|
self.execute_command(self.data_bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.busy = true;
|
||||||
|
self.last_command_time = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_letter(&mut self, letter_to_push: char) {
|
fn write_data(&mut self, data: u8) {
|
||||||
self.cursor_position += 1;
|
if self.cursor_position < 0x50 {
|
||||||
self.text_buffer.push(letter_to_push)
|
self.ddram[self.cursor_position as usize] = data;
|
||||||
|
self.cursor_position = self.cursor_position.wrapping_add(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_display(&mut self) {
|
fn execute_command(&mut self, cmd: u8) {
|
||||||
self.cursor_position = 0;
|
match cmd {
|
||||||
self.text_buffer.clear()
|
0x01 => {
|
||||||
|
self.ddram.fill(b' ');
|
||||||
|
self.cursor_position = 0;
|
||||||
|
}
|
||||||
|
0x02 => {
|
||||||
|
self.cursor_position = 0;
|
||||||
|
}
|
||||||
|
0x04..=0x07 => {
|
||||||
|
self.entry_increment = (cmd & 0b10) != 0;
|
||||||
|
self.entry_shift = (cmd & 0b01) != 0;
|
||||||
|
}
|
||||||
|
0x08..=0x0F => {
|
||||||
|
self.display_on = (cmd & 0b100) != 0;
|
||||||
|
self.cursor_on = (cmd & 0b010) != 0;
|
||||||
|
self.blink_on = (cmd & 0b001) != 0;
|
||||||
|
}
|
||||||
|
0x10..=0x1F => {
|
||||||
|
// Cursor/display shift — not yet implemented
|
||||||
|
}
|
||||||
|
0x20..=0x3F => {
|
||||||
|
self.data_length_8bit = (cmd & 0b10000) != 0;
|
||||||
|
self.two_line_mode = (cmd & 0b1000) != 0;
|
||||||
|
self.font_5x10 = (cmd & 0b100) != 0;
|
||||||
|
}
|
||||||
|
0x40..=0x7F => {
|
||||||
|
// Set CGRAM address — stub
|
||||||
|
}
|
||||||
|
0x80..=0xFF => {
|
||||||
|
self.cursor_position = cmd & 0x7F;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self, x_offset: f32, y_offset: f32) {
|
pub fn get_display_lines(&self) -> (String, String) {
|
||||||
DisplayMatrix::draw_square(x_offset,
|
let row1: String = self.ddram[0x00..0x10].iter().map(|&b| b as char).collect();
|
||||||
y_offset,
|
let row2: String = self.ddram[0x40..0x50].iter().map(|&b| b as char).collect();
|
||||||
x_offset + self.width ,
|
(row1, row2)
|
||||||
y_offset + self.height,
|
}
|
||||||
BLACK);
|
}
|
||||||
|
|
||||||
let mut tmp = self.text_buffer.clone();
|
|
||||||
tmp.push('#');
|
#[cfg(test)]
|
||||||
draw_text(&*tmp, x_offset + 5.0, y_offset + 20.0, 20.0, BLACK);
|
mod tests {
|
||||||
}
|
use super::*;
|
||||||
|
|
||||||
fn draw_square(x1: f32, y1: f32, x2: f32, y2: f32, color: Color) {
|
fn pulse_enable(lcd: &mut HD44780) {
|
||||||
// println!("Square from {x1:2.0}x{y1:2.0} to {x2:2.0}x{y2:2.0} with {:?}", color);
|
lcd.write_control_lines(lcd.rs, lcd.rw, true);
|
||||||
draw_line(x1, y1, x2, y1, 1.0, color);
|
lcd.write_control_lines(lcd.rs, lcd.rw, false);
|
||||||
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);
|
#[test]
|
||||||
|
fn test_clear_display() {
|
||||||
|
let mut lcd = HD44780::new();
|
||||||
|
lcd.set_data_bus(0x01);
|
||||||
|
lcd.write_control_lines(false, false, true);
|
||||||
|
lcd.write_control_lines(false, false, false);
|
||||||
|
lcd.tick();
|
||||||
|
assert!(lcd.ddram.iter().all(|&b| b == b' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_data() {
|
||||||
|
let mut lcd = HD44780::new();
|
||||||
|
lcd.set_data_bus(0x41); // 'A'
|
||||||
|
lcd.write_control_lines(true, false, true);
|
||||||
|
lcd.write_control_lines(true, false, false);
|
||||||
|
lcd.tick();
|
||||||
|
assert_eq!(lcd.ddram[0], b'A');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_cursor() {
|
||||||
|
let mut lcd = HD44780::new();
|
||||||
|
lcd.set_data_bus(0x80 | 0x40);
|
||||||
|
lcd.write_control_lines(false, false, true);
|
||||||
|
lcd.write_control_lines(false, false, false);
|
||||||
|
lcd.tick();
|
||||||
|
assert_eq!(lcd.cursor_position, 0x40);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_busy_flag_and_address() {
|
||||||
|
let mut lcd = HD44780::new();
|
||||||
|
lcd.busy = true;
|
||||||
|
lcd.cursor_position = 0x15;
|
||||||
|
lcd.write_control_lines(false, true, true);
|
||||||
|
let data = lcd.read_data_bus();
|
||||||
|
assert_eq!(data, 0x80 | 0x15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,3 +6,5 @@ pub mod address_bus;
|
|||||||
pub mod data_bus;
|
pub mod data_bus;
|
||||||
pub mod cpu_display;
|
pub mod cpu_display;
|
||||||
pub mod ram_display;
|
pub mod ram_display;
|
||||||
|
pub mod mos6522_peripheral;
|
||||||
|
pub mod backplane;
|
||||||
|
|||||||
5
beneater/src/parts/mos6522_peripheral.rs
Normal file
5
beneater/src/parts/mos6522_peripheral.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub trait Mos6522Peripheral {
|
||||||
|
fn write(&mut self, port: u8);
|
||||||
|
fn control(&mut self, control: u8);
|
||||||
|
fn read(&mut self) -> u8;
|
||||||
|
}
|
||||||
@ -1,45 +1,127 @@
|
|||||||
pub enum Via6522Port {
|
use crate::parts::display_matrix::HD44780;
|
||||||
A(u8),
|
use crate::parts::mos6522_peripheral::Mos6522Peripheral;
|
||||||
B(u8)
|
|
||||||
|
pub struct VIA6522 {
|
||||||
|
// Data registers
|
||||||
|
ora: u8,
|
||||||
|
orb: u8,
|
||||||
|
|
||||||
|
// Data direction
|
||||||
|
ddra: u8,
|
||||||
|
ddrb: u8,
|
||||||
|
|
||||||
|
// Control lines (external pins)
|
||||||
|
ca1: bool,
|
||||||
|
ca2: bool,
|
||||||
|
cb1: bool,
|
||||||
|
cb2: bool,
|
||||||
|
|
||||||
|
// Timers
|
||||||
|
t1_counter: u16,
|
||||||
|
t1_latch: u16,
|
||||||
|
t1_enabled: bool,
|
||||||
|
|
||||||
|
t2_counter: u16,
|
||||||
|
t2_latch: u16,
|
||||||
|
t2_enabled: bool,
|
||||||
|
|
||||||
|
// Interrupt flags
|
||||||
|
ifr: u8,
|
||||||
|
ier: u8,
|
||||||
|
|
||||||
|
// Peripheral (e.g., LCD)
|
||||||
|
pub lcd: Option<Box<dyn Mos6522Peripheral>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
impl VIA6522 {
|
||||||
pub struct Via6522 {
|
pub fn new() -> Self {
|
||||||
data_bus: u8,
|
Self {
|
||||||
address_bus: u16,
|
ora: 0,
|
||||||
port_a_state: u8,
|
orb: 0,
|
||||||
port_b_data: u8,
|
ddra: 0,
|
||||||
port_a_direction: u8,
|
ddrb: 0,
|
||||||
port_b_direction: u8,
|
ca1: false,
|
||||||
memory_offset: u16,
|
ca2: false,
|
||||||
}
|
cb1: false,
|
||||||
|
cb2: false,
|
||||||
impl Via6522 {
|
t1_counter: 0,
|
||||||
pub fn new(offset: u16) -> Self {
|
t1_latch: 0,
|
||||||
Via6522 {
|
t1_enabled: false,
|
||||||
memory_offset: offset,
|
t2_counter: 0,
|
||||||
..Default::default()
|
t2_latch: 0,
|
||||||
|
t2_enabled: false,
|
||||||
|
ifr: 0,
|
||||||
|
ier: 0,
|
||||||
|
lcd: Some(HD44780::new().into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_port_direction(&mut self, port: Via6522Port) {
|
pub fn read(&self, addr: u8) -> u8 {
|
||||||
match port {
|
match addr & 0x0F {
|
||||||
Via6522Port::A(x) => {
|
0x0 => self.orb,
|
||||||
self.port_a_direction = x;
|
0x1 => self.ora,
|
||||||
},
|
0x2 => self.ddrb,
|
||||||
Via6522Port::B(x) => {
|
0x3 => self.ddra,
|
||||||
self.port_b_direction = x;
|
0x4 => (self.t1_counter & 0xFF) as u8,
|
||||||
|
0x5 => (self.t1_counter >> 8) as u8,
|
||||||
|
0x6 => (self.t1_latch & 0xFF) as u8,
|
||||||
|
0x7 => (self.t1_latch >> 8) as u8,
|
||||||
|
0x8 => (self.t2_counter & 0xFF) as u8,
|
||||||
|
0x9 => (self.t2_latch & 0xFF) as u8,
|
||||||
|
0xD => self.ifr,
|
||||||
|
0xE => self.ier | 0x80,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, addr: u8, value: u8) {
|
||||||
|
match addr & 0x0F {
|
||||||
|
0x0 => self.orb = value,
|
||||||
|
0x1 => self.ora = value,
|
||||||
|
0x2 => self.ddrb = value,
|
||||||
|
0x3 => self.ddra = value,
|
||||||
|
0x4 => {
|
||||||
|
self.t1_latch = (self.t1_latch & 0xFF00) | value as u16;
|
||||||
|
self.t1_counter = self.t1_latch;
|
||||||
|
}
|
||||||
|
0x5 => {
|
||||||
|
self.t1_latch = (value as u16) << 8 | (self.t1_latch & 0x00FF);
|
||||||
|
self.t1_counter = self.t1_latch;
|
||||||
|
self.t1_enabled = true;
|
||||||
|
}
|
||||||
|
0x6 => self.t1_latch = (self.t1_latch & 0xFF00) | value as u16,
|
||||||
|
0x7 => self.t1_latch = (value as u16) << 8 | (self.t1_latch & 0x00FF),
|
||||||
|
0x8 => self.t2_counter = value as u16,
|
||||||
|
0x9 => self.t2_latch = value as u16,
|
||||||
|
0xD => self.ifr &= !value, // Clear interrupt flags
|
||||||
|
0xE => {
|
||||||
|
if value & 0x80 != 0 {
|
||||||
|
self.ier |= value & 0x7F;
|
||||||
|
} else {
|
||||||
|
self.ier &= !(value & 0x7F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
if self.t1_enabled {
|
||||||
|
if self.t1_counter > 0 {
|
||||||
|
self.t1_counter -= 1;
|
||||||
|
if self.t1_counter == 0 {
|
||||||
|
self.ifr |= 0x40; // Set T1 interrupt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.t2_enabled {
|
||||||
|
if self.t2_counter > 0 {
|
||||||
|
self.t2_counter -= 1;
|
||||||
|
if self.t2_counter == 0 {
|
||||||
|
self.ifr |= 0x20; // Set T2 interrupt
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
|
||||||
pub fn read_pins(&self) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -2,6 +2,5 @@ pub const SIZE_1KB: usize = 1024;
|
|||||||
pub const SIZE_32KB: usize = SIZE_1KB * 32;
|
pub const SIZE_32KB: usize = SIZE_1KB * 32;
|
||||||
pub const SIZE_64KB: usize = SIZE_1KB * 64;
|
pub const SIZE_64KB: usize = SIZE_1KB * 64;
|
||||||
|
|
||||||
|
|
||||||
pub const OFFSET_RESET_VECTOR: u16 = 0xfffc;
|
pub const OFFSET_RESET_VECTOR: u16 = 0xfffc;
|
||||||
pub const OFFSET_INT_VECTOR: u16 = 0xfffe;
|
pub const OFFSET_INT_VECTOR: u16 = 0xfffe;
|
||||||
@ -4,6 +4,15 @@ use crate::op_info::OpInfo;
|
|||||||
use crate::operation::Operation;
|
use crate::operation::Operation;
|
||||||
use crate::operation::Operation::*;
|
use crate::operation::Operation::*;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn INSTRUCTION_CYCLES(instruction: u8) -> u8 {
|
||||||
|
INSTRUCTION_TABLE[instruction as usize].unwrap().cycles
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn INSTRUCTION_LENGTH(instruction: u8) -> u8 {
|
||||||
|
INSTRUCTION_TABLE[instruction as usize].unwrap().length
|
||||||
|
}
|
||||||
|
|
||||||
pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
|
pub const INSTRUCTION_TABLE: [Option<OpInfo>; 256] = {
|
||||||
let mut table: [Option<OpInfo>; 256] = [const { None }; 256];
|
let mut table: [Option<OpInfo>; 256] = [const { None }; 256];
|
||||||
|
|
||||||
|
|||||||
@ -1,60 +1,77 @@
|
|||||||
|
use log::trace;
|
||||||
use crate::address_mode::AddressMode;
|
use crate::address_mode::AddressMode;
|
||||||
use crate::constants::constants_system::{OFFSET_INT_VECTOR, OFFSET_RESET_VECTOR, SIZE_64KB};
|
use crate::constants::constants_isa_op::ISA_OP_NOP;
|
||||||
use crate::instruction::Instruction;
|
use crate::instruction::Instruction;
|
||||||
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
|
use crate::mos6502flags::{Mos6502Flag, Mos6502Flags};
|
||||||
use crate::mos6502flags::Mos6502Flag::{Carry, Decimal, Interrupt, Overflow};
|
use crate::mos6502flags::Mos6502Flag::*;
|
||||||
use crate::op_info::OpInfo;
|
use crate::op_info::OpInfo;
|
||||||
use crate::operand::Operand;
|
use crate::operand::Operand;
|
||||||
use crate::operation::Operation;
|
use crate::operation::Operation;
|
||||||
|
use crate::constants::constants_system::*;
|
||||||
|
use crate::instruction_table::INSTRUCTION_TABLE;
|
||||||
|
|
||||||
pub struct Mos6502Cpu {
|
pub struct Mos6502Cpu {
|
||||||
// this is public for rendering quickly.
|
memory: [u8; SIZE_64KB],
|
||||||
pub memory: Box<[u8]>,
|
/// accumulator
|
||||||
a: u8,
|
a: u8,
|
||||||
|
/// x register
|
||||||
x: u8,
|
x: u8,
|
||||||
|
/// y register
|
||||||
y: u8,
|
y: u8,
|
||||||
|
/// cpu flags
|
||||||
flags: Mos6502Flags,
|
flags: Mos6502Flags,
|
||||||
|
/// program counter
|
||||||
pc: u16,
|
pc: u16,
|
||||||
|
/// stack offset
|
||||||
s: u8,
|
s: u8,
|
||||||
pub microcode_step: u8,
|
pub microcode_step: u8,
|
||||||
pub address_bus: u16,
|
address_bus: u16,
|
||||||
pub data_bus: u8,
|
data_bus: u8,
|
||||||
ir: Instruction, // Instruction Register
|
ir: Instruction, // Instruction Register
|
||||||
oi: OpInfo,
|
oi: OpInfo,
|
||||||
has_reset: bool,
|
has_reset: bool,
|
||||||
iv: u16 // Interrupt Vector
|
iv: u16, // Interrupt Vector
|
||||||
|
cycle_carry: u16, // Value to hold between microsteps
|
||||||
|
ir_bytes: [u8; 4],
|
||||||
|
/// CPU Read signal
|
||||||
|
pub read_signal: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mos6502Cpu {
|
||||||
|
|
||||||
|
/// set_data_bus
|
||||||
|
///
|
||||||
|
/// Sets data on the data bus.
|
||||||
|
/// Used when CPU is in "R" mode
|
||||||
|
pub fn set_data_bus(&mut self, to_set: u8) {
|
||||||
|
self.data_bus = to_set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Mos6502Cpu {
|
impl Default for Mos6502Cpu {
|
||||||
fn default() -> Self {
|
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");
|
|
||||||
|
|
||||||
let mut working = Mos6502Cpu {
|
let mut working = Mos6502Cpu {
|
||||||
memory: boxed_array,
|
memory: [0x00; SIZE_64KB],
|
||||||
a: 0,
|
a: 0x00,
|
||||||
x: 0,
|
x: 0x00,
|
||||||
y: 0,
|
y: 0x00,
|
||||||
flags: Default::default(),
|
flags: Default::default(),
|
||||||
pc: 0xfffd,
|
pc: 0xfffd,
|
||||||
s: 0,
|
s: 0x00,
|
||||||
microcode_step: 0,
|
microcode_step: 0x00,
|
||||||
address_bus: 0,
|
address_bus: 0x00,
|
||||||
data_bus: 0,
|
data_bus: 0x00,
|
||||||
ir: Instruction {
|
ir: Instruction {
|
||||||
op: Operation::NOP,
|
op: Operation::NOP,
|
||||||
mode: AddressMode::Implied,
|
mode: AddressMode::Implied,
|
||||||
operand: Operand::None,
|
operand: Operand::None,
|
||||||
},
|
},
|
||||||
oi: OpInfo {
|
oi: INSTRUCTION_TABLE[ISA_OP_NOP as usize].unwrap(),
|
||||||
operation: Operation::NOP,
|
|
||||||
mode: AddressMode::Implied,
|
|
||||||
length: 1,
|
|
||||||
cycles: 2,
|
|
||||||
},
|
|
||||||
has_reset: false,
|
has_reset: false,
|
||||||
iv: 0xfffe
|
iv: 0xfffe,
|
||||||
|
cycle_carry: 0x0000,
|
||||||
|
ir_bytes: [0x00; 4],
|
||||||
|
read_signal: true
|
||||||
};
|
};
|
||||||
working.reset_cpu();
|
working.reset_cpu();
|
||||||
working
|
working
|
||||||
@ -62,34 +79,20 @@ impl Default for Mos6502Cpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Mos6502Cpu {
|
impl Mos6502Cpu {
|
||||||
|
pub fn address_bus(&self) -> u16 {
|
||||||
|
self.address_bus
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_bus(&self) -> u8 {
|
||||||
|
self.data_bus
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new() -> Mos6502Cpu {
|
pub fn new() -> Mos6502Cpu {
|
||||||
let vec = vec![0x00; SIZE_64KB];
|
let array = [0x00u8; 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");
|
|
||||||
let mut working = Mos6502Cpu {
|
let mut working = Mos6502Cpu {
|
||||||
memory: boxed_array,
|
memory: array,
|
||||||
a: 0,
|
ir_bytes: [0x00; 4],
|
||||||
x: 0,
|
..Default::default()
|
||||||
y: 0,
|
|
||||||
flags: Mos6502Flags::default(),
|
|
||||||
pc: 0,
|
|
||||||
s: 0xfd,
|
|
||||||
microcode_step: 0,
|
|
||||||
address_bus: 0x0000,
|
|
||||||
data_bus: 0x00,
|
|
||||||
ir: Instruction {
|
|
||||||
op: Operation::NOP,
|
|
||||||
mode: AddressMode::Implied,
|
|
||||||
operand: Operand::None,
|
|
||||||
},
|
|
||||||
oi: OpInfo {
|
|
||||||
operation: Operation::NOP,
|
|
||||||
mode: AddressMode::Implied,
|
|
||||||
length: 1,
|
|
||||||
cycles: 2,
|
|
||||||
},
|
|
||||||
has_reset: false,
|
|
||||||
iv: 0xfffe
|
|
||||||
};
|
};
|
||||||
working.reset_cpu();
|
working.reset_cpu();
|
||||||
working
|
working
|
||||||
@ -123,6 +126,7 @@ impl Mos6502Cpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn poke(&mut self, offset: u16, value: u8) {
|
pub fn poke(&mut self, offset: u16, value: u8) {
|
||||||
|
println!("Setting memory at {offset:04x} to {value:02x}");
|
||||||
self.memory[offset as usize] = value
|
self.memory[offset as usize] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +134,10 @@ impl Mos6502Cpu {
|
|||||||
self.a
|
self.a
|
||||||
}
|
}
|
||||||
pub fn poke_a(&mut self, new_a: u8) {
|
pub fn poke_a(&mut self, new_a: u8) {
|
||||||
self.a = new_a;
|
|
||||||
|
println!("Updating register A from [{}] to [{}]", self.a, new_a);
|
||||||
|
|
||||||
|
self.a = new_a;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peek_x(&self) -> u8 {
|
pub fn peek_x(&self) -> u8 {
|
||||||
@ -159,9 +166,8 @@ impl Mos6502Cpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ticks the CPU
|
/// Ticks the CPU
|
||||||
/// Returns
|
pub fn tick(&mut self) {
|
||||||
/// AddressBus, DataBus, RW flag
|
|
||||||
pub fn tick(&mut self) -> (u16, u8, bool) {
|
|
||||||
println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc);
|
println!("PREPARiNG TO TICK CPU AT PC 0x{:04x}", self.pc);
|
||||||
if self.microcode_step == 0 {
|
if self.microcode_step == 0 {
|
||||||
println!("OUT OF MICROSTEPS. Decoding the next instruction");
|
println!("OUT OF MICROSTEPS. Decoding the next instruction");
|
||||||
@ -176,9 +182,27 @@ impl Mos6502Cpu {
|
|||||||
// set the counter to the number of steps left
|
// set the counter to the number of steps left
|
||||||
} else {
|
} else {
|
||||||
// run 1 microcode step
|
// run 1 microcode step
|
||||||
println!("Microstep {} for {:?}", self.microcode_step, self.ir.op);
|
println!("Microstep {}/{} for {:?}", self.microcode_step, self.oi.cycles, self.ir.op);
|
||||||
match self.ir.op {
|
match self.ir.op {
|
||||||
Operation::ADC => {
|
Operation::ADC => {
|
||||||
|
match self.microcode_step {
|
||||||
|
1 => {
|
||||||
|
match self.ir.mode {
|
||||||
|
AddressMode::Immediate => {}
|
||||||
|
AddressMode::ZeroPage => {}
|
||||||
|
AddressMode::ZeroPageX => {}
|
||||||
|
AddressMode::Absolute => {}
|
||||||
|
AddressMode::AbsoluteX => {}
|
||||||
|
AddressMode::AbsoluteY => {}
|
||||||
|
AddressMode::Indirect => {}
|
||||||
|
AddressMode::IndirectX => {}
|
||||||
|
AddressMode::IndirectY => {}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2 => {},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Operation::AND => {}
|
Operation::AND => {}
|
||||||
Operation::ASL => {}
|
Operation::ASL => {}
|
||||||
@ -207,7 +231,65 @@ impl Mos6502Cpu {
|
|||||||
Operation::CMP => {}
|
Operation::CMP => {}
|
||||||
Operation::CPX => {}
|
Operation::CPX => {}
|
||||||
Operation::CPY => {}
|
Operation::CPY => {}
|
||||||
Operation::DEC => {}
|
Operation::DEC => {
|
||||||
|
match self.microcode_step {
|
||||||
|
// DEC Step 1
|
||||||
|
1 => {
|
||||||
|
let working_value = match self.oi.mode {
|
||||||
|
AddressMode::ZeroPage => {
|
||||||
|
// read from
|
||||||
|
let offset = match self.ir.operand {
|
||||||
|
Operand::Byte(z) => {
|
||||||
|
z
|
||||||
|
}
|
||||||
|
_ => { 0x00 }
|
||||||
|
};
|
||||||
|
trace!("READING FROM MEMORY AT 0x{offset:04x}");
|
||||||
|
self.memory[offset as usize]
|
||||||
|
// self.peek(offset);
|
||||||
|
}
|
||||||
|
AddressMode::ZeroPageX => {
|
||||||
|
let offset = match self.ir.operand {
|
||||||
|
Operand::Byte(z) => {
|
||||||
|
z
|
||||||
|
}
|
||||||
|
_ => { 0x00 }
|
||||||
|
};
|
||||||
|
// self.memory.peek(offset + self.x);
|
||||||
|
self.memory[offset as usize]
|
||||||
|
}
|
||||||
|
AddressMode::Absolute => {
|
||||||
|
let offset = match self.ir.operand {
|
||||||
|
Operand::Word(offset) => {
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
_ => { 0x00 }
|
||||||
|
};
|
||||||
|
// self.memory.peek(offset)
|
||||||
|
self.memory[offset as usize]
|
||||||
|
}
|
||||||
|
AddressMode::AbsoluteX => {
|
||||||
|
let offset = match self.ir.operand {
|
||||||
|
Operand::Word(offset) => {
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
_ => { 0x00 }
|
||||||
|
};
|
||||||
|
// self.memory.peek(offset + self.x);
|
||||||
|
self.memory[offset as usize]
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
0x00
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// DEC write memory
|
||||||
|
2 => {
|
||||||
|
self.a = self.cycle_carry as u8;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
Operation::DEX => {
|
Operation::DEX => {
|
||||||
if self.microcode_step == 1 {
|
if self.microcode_step == 1 {
|
||||||
let (new_x, new_carry) = self.x.overflowing_sub(1);
|
let (new_x, new_carry) = self.x.overflowing_sub(1);
|
||||||
@ -227,6 +309,8 @@ impl Mos6502Cpu {
|
|||||||
let (new_x, new_carry) = self.x.overflowing_add(1);
|
let (new_x, new_carry) = self.x.overflowing_add(1);
|
||||||
self.poke_x(new_x);
|
self.poke_x(new_x);
|
||||||
self.poke_flag(Carry, new_carry);
|
self.poke_flag(Carry, new_carry);
|
||||||
|
self.address_bus = self.pc;
|
||||||
|
self.data_bus = 0x00;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::INY => {
|
Operation::INY => {
|
||||||
@ -234,16 +318,23 @@ impl Mos6502Cpu {
|
|||||||
let (new_y, new_carry) = self.y.overflowing_add(1);
|
let (new_y, new_carry) = self.y.overflowing_add(1);
|
||||||
self.poke_y(new_y);
|
self.poke_y(new_y);
|
||||||
self.poke_flag(Carry, new_carry);
|
self.poke_flag(Carry, new_carry);
|
||||||
|
self.address_bus = self.pc;
|
||||||
|
self.data_bus = 0x00;
|
||||||
} }
|
} }
|
||||||
Operation::JMP => {
|
Operation::JMP => {
|
||||||
match self.ir.operand {
|
match self.ir.operand {
|
||||||
Operand::Word(offset) => {
|
Operand::Word(offset) => {
|
||||||
self.pc = offset;
|
self.pc = offset;
|
||||||
|
self.address_bus = self.pc;
|
||||||
|
self.data_bus = 0x00;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::JSR => {}
|
Operation::JSR => {
|
||||||
|
// push pc to stack.
|
||||||
|
// jump to the subroutine.
|
||||||
|
}
|
||||||
Operation::LDA => {
|
Operation::LDA => {
|
||||||
match self.oi.mode {
|
match self.oi.mode {
|
||||||
AddressMode::Immediate => {
|
AddressMode::Immediate => {
|
||||||
@ -264,11 +355,32 @@ impl Mos6502Cpu {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AddressMode::ZeroPageX => {}
|
AddressMode::ZeroPageX => {
|
||||||
AddressMode::ZeroPageY => {}
|
match self.ir.operand {
|
||||||
AddressMode::Absolute => {}
|
Operand::Byte(value) => {
|
||||||
AddressMode::AbsoluteX => {}
|
let x_offset = self.x;
|
||||||
AddressMode::AbsoluteY => {}
|
self.a = self.memory[(value + x_offset) as usize];
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddressMode::Absolute => {
|
||||||
|
if let Operand::Word(offset) = self.ir.operand {
|
||||||
|
println!("Loading from absolute address 0x{offset:04x}");
|
||||||
|
self.a = self.memory[offset as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
AddressMode::AbsoluteX => {
|
||||||
|
if let Operand::Word(offset) = self.ir.operand {
|
||||||
|
self.a = self.memory[(offset + self.x as u16) as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddressMode::AbsoluteY => {
|
||||||
|
if let Operand::Word(offset) = self.ir.operand {
|
||||||
|
self.a = self.memory[(offset + self.y as u16) as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
AddressMode::Indirect => {}
|
AddressMode::Indirect => {}
|
||||||
AddressMode::IndirectX => {}
|
AddressMode::IndirectX => {}
|
||||||
AddressMode::IndirectY => {}
|
AddressMode::IndirectY => {}
|
||||||
@ -356,7 +468,16 @@ impl Mos6502Cpu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AddressMode::AbsoluteY => {}
|
AddressMode::AbsoluteY => {
|
||||||
|
match self.ir.operand {
|
||||||
|
Operand::Word(offset) => {
|
||||||
|
self.memory[(offset + self.y as u16) as usize] = self.a;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Invalid Parameter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
AddressMode::IndirectX => {}
|
AddressMode::IndirectX => {}
|
||||||
AddressMode::IndirectY => {}
|
AddressMode::IndirectY => {}
|
||||||
_ => {
|
_ => {
|
||||||
@ -385,8 +506,6 @@ impl Mos6502Cpu {
|
|||||||
}
|
}
|
||||||
self.microcode_step -= 1;
|
self.microcode_step -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
(0,0,false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump(&self) {
|
pub fn dump(&self) {
|
||||||
@ -398,16 +517,12 @@ impl Mos6502Cpu {
|
|||||||
(self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step)
|
(self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_microstep(&self, instruction: Instruction, step: u8) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::constants::constants_isa_op::*;
|
use crate::constants::constants_isa_op::*;
|
||||||
use crate::instruction_table::INSTRUCTION_TABLE;
|
use crate::instruction_table::{INSTRUCTION_CYCLES, INSTRUCTION_TABLE};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -445,7 +560,7 @@ mod test {
|
|||||||
cpu.memory[0x6000] = ISA_OP_CLI;
|
cpu.memory[0x6000] = ISA_OP_CLI;
|
||||||
cpu.pc = 0x6000;
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLI as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_CLI) { cpu.tick(); }
|
||||||
|
|
||||||
assert!(!cpu.peek_flag(Interrupt));
|
assert!(!cpu.peek_flag(Interrupt));
|
||||||
|
|
||||||
@ -458,7 +573,7 @@ mod test {
|
|||||||
cpu.memory[0x6000] = ISA_OP_CLV;
|
cpu.memory[0x6000] = ISA_OP_CLV;
|
||||||
cpu.pc = 0x6000;
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLV as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_CLV) { cpu.tick(); }
|
||||||
|
|
||||||
assert!(!cpu.peek_flag(Overflow));
|
assert!(!cpu.peek_flag(Overflow));
|
||||||
}
|
}
|
||||||
@ -470,24 +585,88 @@ mod test {
|
|||||||
cpu.memory[0x6001] = 0xab;
|
cpu.memory[0x6001] = 0xab;
|
||||||
cpu.pc = 0x6000;
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_I as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_I) { cpu.tick(); }
|
||||||
|
|
||||||
assert_eq!(cpu.a, 0xab);
|
assert_eq!(cpu.a, 0xab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lda_zx() {
|
||||||
|
let mut cpu = Mos6502Cpu::default();
|
||||||
|
cpu.poke_x(1);
|
||||||
|
cpu.memory[0x6000] = ISA_OP_LDA_ZX;
|
||||||
|
cpu.memory[0x6001] = 0xab;
|
||||||
|
cpu.memory[0x00ac] = 0xbe;
|
||||||
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ZX) { cpu.tick(); };
|
||||||
|
|
||||||
|
// println!("MEMORY AT 0x00aa, ab, ac, ad, ae -> {:02x} {:02x} {:02x} {:02x} {:02x}", cpu.memory[0x00aa], cpu.memory[0x00ab], cpu.memory[0x00ac], cpu.memory[0x00ad], cpu.memory[0x00ae]);
|
||||||
|
// cpu.dump();
|
||||||
|
assert_eq!(cpu.peek_a(), 0xbe);
|
||||||
|
assert!(!cpu.peek_flag(Zero));
|
||||||
|
assert!(!cpu.peek_flag(Carry));
|
||||||
|
assert!(!cpu.peek_flag(Negative));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lda_zeropage() {
|
fn lda_zeropage() {
|
||||||
let mut cpu = Mos6502Cpu::default();
|
let mut cpu = Mos6502Cpu::default();
|
||||||
cpu.memory[0x6000] = ISA_OP_LDA_Z;
|
cpu.memory[0x6000] = ISA_OP_LDA_Z;
|
||||||
cpu.memory[0x6001] = 0xab;
|
cpu.memory[0x6001] = 0xab;
|
||||||
|
// Load ZeroPage
|
||||||
cpu.memory[0x00ab] = 0xbe;
|
cpu.memory[0x00ab] = 0xbe;
|
||||||
cpu.pc = 0x6000;
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_Z as usize].unwrap().cycles + 1 { cpu.tick(); }
|
for _ in 0..INSTRUCTION_CYCLES(ISA_OP_LDA_Z) { cpu.tick(); }
|
||||||
|
|
||||||
assert_eq!(cpu.a, 0xbe);
|
assert_eq!(cpu.a, 0xbe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lda_absolute() {
|
||||||
|
let mut cpu = Mos6502Cpu::default();
|
||||||
|
cpu.memory[0x6000] = ISA_OP_LDA_ABS;
|
||||||
|
cpu.memory[0x6001] = 0xef;
|
||||||
|
cpu.memory[0x6002] = 0x0e;
|
||||||
|
cpu.memory[0x0eef] = 0xab;
|
||||||
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ABS) { cpu.tick(); }
|
||||||
|
|
||||||
|
assert_eq!(cpu.a, 0xab);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lda_absolutex() {
|
||||||
|
let mut cpu = Mos6502Cpu::default();
|
||||||
|
cpu.memory[0x6000] = ISA_OP_LDA_ABSX;
|
||||||
|
cpu.memory[0x6001] = 0xef;
|
||||||
|
cpu.memory[0x6002] = 0x0e;
|
||||||
|
cpu.poke_x(0x01);
|
||||||
|
cpu.memory[0x0ef0] = 0xab;
|
||||||
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ABSX) { cpu.tick(); }
|
||||||
|
|
||||||
|
assert_eq!(cpu.a, 0xab);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lda_absolutey() {
|
||||||
|
let mut cpu = Mos6502Cpu::default();
|
||||||
|
cpu.memory[0x6000] = ISA_OP_LDA_ABSY;
|
||||||
|
cpu.memory[0x6001] = 0xef;
|
||||||
|
cpu.memory[0x6002] = 0xbe;
|
||||||
|
cpu.poke_y(0x01);
|
||||||
|
cpu.memory[0x0ef0] = 0xab;
|
||||||
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_LDA_ABSY) { cpu.tick(); }
|
||||||
|
|
||||||
|
assert_eq!(cpu.a, 0xab);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dex_inx() {
|
fn dex_inx() {
|
||||||
let mut cpu = Mos6502Cpu::default();
|
let mut cpu = Mos6502Cpu::default();
|
||||||
@ -496,9 +675,9 @@ mod test {
|
|||||||
cpu.memory[0x6001] = ISA_OP_INX;
|
cpu.memory[0x6001] = ISA_OP_INX;
|
||||||
cpu.pc = 0x6000;
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
for _ in 0..=INSTRUCTION_TABLE[ISA_OP_DEX as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_DEX) { cpu.tick(); }
|
||||||
assert_eq!(0xaa, cpu.x);
|
assert_eq!(0xaa, cpu.x);
|
||||||
for _ in 0..=INSTRUCTION_TABLE[ISA_OP_INX as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_INX) { cpu.tick(); }
|
||||||
assert_eq!(0xab, cpu.x);
|
assert_eq!(0xab, cpu.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,9 +689,9 @@ mod test {
|
|||||||
cpu.memory[0x6001] = ISA_OP_INY;
|
cpu.memory[0x6001] = ISA_OP_INY;
|
||||||
cpu.pc = 0x6000;
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
for _ in 0..=INSTRUCTION_TABLE[ISA_OP_DEY as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_DEY) { cpu.tick(); }
|
||||||
assert_eq!(0xaa, cpu.peek_y());
|
assert_eq!(0xaa, cpu.peek_y());
|
||||||
for _ in 0..=INSTRUCTION_TABLE[ISA_OP_INY as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_INY) { cpu.tick(); }
|
||||||
assert_eq!(0xab, cpu.peek_y());
|
assert_eq!(0xab, cpu.peek_y());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,9 +703,9 @@ mod test {
|
|||||||
cpu.memory[0x6001] = ISA_OP_ROR_A;
|
cpu.memory[0x6001] = ISA_OP_ROR_A;
|
||||||
cpu.pc = 0x6000;
|
cpu.pc = 0x6000;
|
||||||
|
|
||||||
for _ in 0..=INSTRUCTION_TABLE[ISA_OP_ROL_A as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_ROL_A) { cpu.tick(); }
|
||||||
assert_eq!(cpu.peek_a(), 0b0101_0101);
|
assert_eq!(cpu.peek_a(), 0b0101_0101);
|
||||||
for _ in 0..=INSTRUCTION_TABLE[ISA_OP_ROR_A as usize].unwrap().cycles { cpu.tick(); }
|
for _ in 0..=INSTRUCTION_CYCLES(ISA_OP_ROR_A) { cpu.tick(); }
|
||||||
assert_eq!(cpu.peek_a(), 0b1010_1010);
|
assert_eq!(cpu.peek_a(), 0b1010_1010);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,12 @@
|
|||||||
|
use crate::mos6502flags::Mos6502Flag::{Break, Carry, Decimal, Interrupt, Negative, Overflow, Zero};
|
||||||
|
|
||||||
|
pub const BIT_NEGATIVE: u8 = 7;
|
||||||
|
pub const BIT_OVERFLOW: u8 = 6;
|
||||||
|
pub const BIT_BREAK: u8 = 4;
|
||||||
|
pub const BIT_DECIMAL: u8 = 3;
|
||||||
|
pub const BIT_INTERRUPT: u8 = 2;
|
||||||
|
pub const BIT_ZERO: u8 = 1;
|
||||||
|
pub const BIT_CARRY: u8 = 0;
|
||||||
/// Represents the status flags in the 6502 processor's status register (P).
|
/// Represents the status flags in the 6502 processor's status register (P).
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum Mos6502Flag {
|
pub enum Mos6502Flag {
|
||||||
@ -42,7 +50,21 @@ pub enum Mos6502Flag {
|
|||||||
Negative,
|
Negative,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
impl Mos6502Flag {
|
||||||
|
pub fn index(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Carry => BIT_CARRY,
|
||||||
|
Zero => BIT_ZERO,
|
||||||
|
Interrupt => BIT_INTERRUPT,
|
||||||
|
Decimal => BIT_DECIMAL,
|
||||||
|
Break => BIT_BREAK,
|
||||||
|
Overflow => BIT_OVERFLOW,
|
||||||
|
Negative => BIT_NEGATIVE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, PartialEq, Debug)]
|
||||||
pub struct Mos6502Flags {
|
pub struct Mos6502Flags {
|
||||||
carry: bool,
|
carry: bool,
|
||||||
zero: bool,
|
zero: bool,
|
||||||
@ -56,78 +78,133 @@ pub struct Mos6502Flags {
|
|||||||
impl Mos6502Flags {
|
impl Mos6502Flags {
|
||||||
pub fn dump(&self) -> String {
|
pub fn dump(&self) -> String {
|
||||||
format!("{}{}{}{}{}{}{}",
|
format!("{}{}{}{}{}{}{}",
|
||||||
if self.carry { 'C' } else { 'c' },
|
if self.carry { 'C' } else { 'c' },
|
||||||
if self.zero { 'Z' } else { 'z' },
|
if self.zero { 'Z' } else { 'z' },
|
||||||
if self.interrupt { 'I' } else { 'i' },
|
if self.interrupt { 'I' } else { 'i' },
|
||||||
if self.decimal { 'D' } else { 'd' },
|
if self.decimal { 'D' } else { 'd' },
|
||||||
if self.break_flag { 'B' } else { 'b' },
|
if self.break_flag { 'B' } else { 'b' },
|
||||||
if self.overflow { 'O' } else { 'o' },
|
if self.overflow { 'O' } else { 'o' },
|
||||||
if self.negative { 'N' } else { 'n' }
|
if self.negative { 'N' } else { 'n' }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mos6502Flags {
|
impl Mos6502Flags {
|
||||||
|
|
||||||
pub fn set_flag(&mut self, flag_to_set: Mos6502Flag) {
|
pub fn set_flag(&mut self, flag_to_set: Mos6502Flag) {
|
||||||
self.change_flag(flag_to_set, true);
|
println!("Setting {flag_to_set:?} flag");
|
||||||
|
match flag_to_set {
|
||||||
|
Carry => self.carry = true,
|
||||||
|
Zero => self.zero = true,
|
||||||
|
Interrupt => self.interrupt = true,
|
||||||
|
Decimal => self.decimal = true,
|
||||||
|
Break => self.break_flag = true,
|
||||||
|
Overflow => self.overflow = true,
|
||||||
|
Negative => self.negative = true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_flag(&mut self, flag_to_clear: Mos6502Flag) {
|
pub fn clear_flag(&mut self, flag_to_clear: Mos6502Flag) {
|
||||||
self.change_flag(flag_to_clear, false);
|
println!("Clearing {flag_to_clear:?} flag");
|
||||||
}
|
match flag_to_clear {
|
||||||
|
Carry => self.carry = false,
|
||||||
|
Zero => self.zero = false,
|
||||||
|
Interrupt => self.interrupt = false,
|
||||||
|
Decimal => self.decimal = false,
|
||||||
|
Break => self.break_flag = false,
|
||||||
|
Overflow => self.overflow = false,
|
||||||
|
Negative => self.negative = false,
|
||||||
|
} }
|
||||||
|
|
||||||
fn change_flag(&mut self, flag_to_change: Mos6502Flag, new_value: bool) {
|
fn change_flag(&mut self, flag_to_change: Mos6502Flag, new_value: bool) {
|
||||||
match flag_to_change {
|
if new_value {
|
||||||
Mos6502Flag::Carry => {
|
self.set_flag(flag_to_change);
|
||||||
self.carry = new_value
|
} else {
|
||||||
}
|
self.clear_flag(flag_to_change);
|
||||||
Mos6502Flag::Zero => {
|
|
||||||
self.zero = new_value
|
|
||||||
}
|
|
||||||
Mos6502Flag::Interrupt => {
|
|
||||||
self.interrupt = new_value
|
|
||||||
}
|
|
||||||
Mos6502Flag::Decimal => {
|
|
||||||
self.decimal = new_value
|
|
||||||
}
|
|
||||||
Mos6502Flag::Break => {
|
|
||||||
self.break_flag = new_value
|
|
||||||
}
|
|
||||||
Mos6502Flag::Overflow => {
|
|
||||||
self.overflow = new_value
|
|
||||||
}
|
|
||||||
Mos6502Flag::Negative => {
|
|
||||||
self.negative = new_value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flag(&self, flag_to_read: Mos6502Flag) -> bool {
|
pub fn flag(&self, flag_to_read: Mos6502Flag) -> bool {
|
||||||
match flag_to_read {
|
match flag_to_read {
|
||||||
Mos6502Flag::Carry => {
|
Mos6502Flag::Negative => self.negative,
|
||||||
self.carry
|
Mos6502Flag::Overflow => self.overflow,
|
||||||
}
|
// 5
|
||||||
Mos6502Flag::Zero => {
|
Mos6502Flag::Break => self.break_flag,
|
||||||
self.zero
|
Mos6502Flag::Decimal => self.decimal,
|
||||||
}
|
Mos6502Flag::Interrupt => self.interrupt,
|
||||||
Mos6502Flag::Interrupt => {
|
Mos6502Flag::Zero => self.zero,
|
||||||
self.interrupt
|
Mos6502Flag::Carry => self.carry,
|
||||||
}
|
|
||||||
Mos6502Flag::Decimal => {
|
|
||||||
self.decimal
|
|
||||||
}
|
|
||||||
Mos6502Flag::Break => {
|
|
||||||
self.break_flag
|
|
||||||
}
|
|
||||||
Mos6502Flag::Overflow => {
|
|
||||||
self.overflow
|
|
||||||
}
|
|
||||||
Mos6502Flag::Negative => {
|
|
||||||
self.negative
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_byte(&self) -> u8 {
|
||||||
|
let mut working = 0x00;
|
||||||
|
|
||||||
|
if self.flag(Negative) { working += 1 << Negative.index(); }
|
||||||
|
if self.flag(Overflow) { working += 1 << Overflow.index(); }
|
||||||
|
working += 1 << 5; // Always Set
|
||||||
|
if self.flag(Break) { working += 1 << Break.index(); }
|
||||||
|
if self.flag(Decimal) { working += 1 << Decimal.index(); }
|
||||||
|
if self.flag(Interrupt) { working += 1 << Interrupt.index(); }
|
||||||
|
if self.flag(Zero) { working += 1 << Zero.index(); }
|
||||||
|
if self.flag(Carry) { working += 1 << Carry.index(); }
|
||||||
|
|
||||||
|
working
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub fn from_byte(src: u8) -> Self {
|
||||||
|
let mut working = Self::default();
|
||||||
|
|
||||||
|
working.change_flag(Negative, Self::bit(src, Negative.index()));
|
||||||
|
working.change_flag(Overflow, Self::bit(src, Overflow.index()));
|
||||||
|
working.change_flag(Break, Self::bit(src, Break.index()));
|
||||||
|
working.change_flag(Decimal, Self::bit(src, Decimal.index()));
|
||||||
|
working.change_flag(Interrupt, Self::bit(src, Interrupt.index()));
|
||||||
|
working.change_flag(Zero, Self::bit(src, Zero.index()));
|
||||||
|
working.change_flag(Carry, Self::bit(src, Carry.index()));
|
||||||
|
|
||||||
|
working
|
||||||
|
}
|
||||||
|
|
||||||
|
/// bit
|
||||||
|
///
|
||||||
|
/// src -> Source byte to check in
|
||||||
|
/// pos -> Which bit to check
|
||||||
|
///
|
||||||
|
/// returns bool
|
||||||
|
///
|
||||||
|
/// True if the bit is set.
|
||||||
|
/// False if the bit is not set
|
||||||
|
#[inline]
|
||||||
|
fn bit(src: u8, pos: u8) -> bool {
|
||||||
|
(src >> pos) & 1 != 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn smoke() { assert!(true); }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sanity() {
|
||||||
|
let f = Mos6502Flags::default();
|
||||||
|
let magic_byte = 0b1110_1101;
|
||||||
|
let magic_flags = Mos6502Flags {
|
||||||
|
carry: true,
|
||||||
|
zero: false,
|
||||||
|
interrupt: true,
|
||||||
|
decimal: true,
|
||||||
|
break_flag: false,
|
||||||
|
overflow: true,
|
||||||
|
negative: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(magic_flags, Mos6502Flags::from_byte(magic_byte));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,117 +5,121 @@ pub enum Operation {
|
|||||||
///
|
///
|
||||||
/// Affects flags: N, V, Z, C
|
/// Affects flags: N, V, Z, C
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
|
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
|
||||||
|
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
|
||||||
ADC,
|
ADC,
|
||||||
|
|
||||||
/// Logical AND with Accumulator
|
/// Logical AND with Accumulator
|
||||||
///
|
///
|
||||||
/// Affects flags: N, Z
|
/// Affects flags: N, Z
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
|
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
|
||||||
|
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
|
||||||
AND,
|
AND,
|
||||||
|
|
||||||
/// Arithmetic Shift Left
|
/// Arithmetic Shift Left
|
||||||
///
|
///
|
||||||
/// Affects flags: N, Z, C
|
/// Affects flags: N, Z, C
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Accumulator, ZeroPage, ZeroPageX, Absolute, AbsoluteX
|
/// Addressing Modes: Accumulator (1/2), ZeroPage (2/5), ZeroPageX (2/6), Absolute (3/6),
|
||||||
|
/// AbsoluteX (3/7)
|
||||||
ASL,
|
ASL,
|
||||||
|
|
||||||
/// Branch if Carry Clear
|
/// Branch if Carry Clear
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Relative
|
/// Addressing Modes: Relative (2/2)
|
||||||
BCC,
|
BCC,
|
||||||
|
|
||||||
/// Branch if Carry Set
|
/// Branch if Carry Set
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Relative
|
/// Addressing Modes: Relative (2/2)
|
||||||
BCS,
|
BCS,
|
||||||
|
|
||||||
/// Branch if Equal (Zero Set)
|
/// Branch if Equal (Zero Set)
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Relative
|
/// Addressing Modes: Relative (2/2)
|
||||||
BEQ,
|
BEQ,
|
||||||
|
|
||||||
/// Bit Test
|
/// Bit Test
|
||||||
///
|
///
|
||||||
/// Affects flags: N, V, Z
|
/// Affects flags: N, V, Z
|
||||||
///
|
///
|
||||||
/// Addressing Modes: ZeroPage, Absolute
|
/// Addressing Modes: ZeroPage (2/3), Absolute (3/4)
|
||||||
BIT,
|
BIT,
|
||||||
|
|
||||||
/// Branch if Minus (Negative Set)
|
/// Branch if Minus (Negative Set)
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Relative
|
/// Addressing Modes: Relative (2/2)
|
||||||
BMI,
|
BMI,
|
||||||
|
|
||||||
/// Branch if Not Equal (Zero Clear)
|
/// Branch if Not Equal (Zero Clear)
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Relative
|
/// Addressing Modes: Relative (2/2)
|
||||||
BNE,
|
BNE,
|
||||||
|
|
||||||
/// Branch if Positive (Negative Clear)
|
/// Branch if Positive (Negative Clear)
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Relative
|
/// Addressing Modes: Relative (2/2)
|
||||||
BPL,
|
BPL,
|
||||||
|
|
||||||
/// Force Interrupt
|
/// Force Interrupt
|
||||||
///
|
///
|
||||||
/// Affects flags: B
|
/// Affects flags: B
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Implied
|
/// Addressing Modes: Implied (1/7)
|
||||||
BRK,
|
BRK,
|
||||||
|
|
||||||
/// Branch if Overflow Clear
|
/// Branch if Overflow Clear
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Relative
|
/// Addressing Modes: Relative (2/2)
|
||||||
BVC,
|
BVC,
|
||||||
|
|
||||||
/// Branch if Overflow Set
|
/// Branch if Overflow Set
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Relative
|
/// Addressing Modes: Relative (2/2)
|
||||||
BVS,
|
BVS,
|
||||||
|
|
||||||
/// Clear Carry Flag
|
/// Clear Carry Flag
|
||||||
///
|
///
|
||||||
/// Affects flags: C
|
/// Affects flags: C
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Implied
|
/// Addressing Modes: Implied (1/2)
|
||||||
CLC,
|
CLC,
|
||||||
|
|
||||||
/// Clear Decimal Mode
|
/// Clear Decimal Mode
|
||||||
///
|
///
|
||||||
/// Affects flags: D
|
/// Affects flags: D
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Implied
|
/// Addressing Modes: Implied (1/2)
|
||||||
CLD,
|
CLD,
|
||||||
|
|
||||||
/// Clear Interrupt Disable
|
/// Clear Interrupt Disable
|
||||||
///
|
///
|
||||||
/// Affects flags: I
|
/// Affects flags: I
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Implied
|
/// Addressing Modes: Implied (1/2)
|
||||||
CLI,
|
CLI,
|
||||||
|
|
||||||
/// Clear Overflow Flag
|
/// Clear Overflow Flag
|
||||||
///
|
///
|
||||||
/// Affects flags: V
|
/// Affects flags: V
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Implied
|
/// Addressing Modes: Implied (2/2)
|
||||||
CLV,
|
CLV,
|
||||||
|
|
||||||
/// Compare Accumulator
|
/// Compare Accumulator
|
||||||
///
|
///
|
||||||
/// Affects flags: N, Z, C
|
/// Affects flags: N, Z, C
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Immediate, ZeroPage, ZeroPageX, Absolute, AbsoluteX, AbsoluteY, IndirectX, IndirectY
|
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), ZeroPageX (2/4), Absolute (3/4),
|
||||||
|
/// AbsoluteX (3/4), AbsoluteY (3/4), IndirectX (2/6), IndirectY (2/5)
|
||||||
CMP,
|
CMP,
|
||||||
|
|
||||||
/// Compare X Register
|
/// Compare X Register
|
||||||
///
|
///
|
||||||
/// Affects flags: N, Z, C
|
/// Affects flags: N, Z, C
|
||||||
///
|
///
|
||||||
/// Addressing Modes: Immediate, ZeroPage, Absolute
|
/// Addressing Modes: Immediate (2/2), ZeroPage (2/3), Absolute (3/4)
|
||||||
CPX,
|
CPX,
|
||||||
|
|
||||||
/// Compare Y Register
|
/// Compare Y Register
|
||||||
|
|||||||
@ -14,6 +14,12 @@ pub struct At28C256 {
|
|||||||
data: Box<[u8; SIZE_32KB]>,
|
data: Box<[u8; SIZE_32KB]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl At28C256 {
|
||||||
|
pub fn program(&mut self, new_program: &[u8; 32768]) {
|
||||||
|
self.data= new_program.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::constants::constants_system::SIZE_1KB;
|
use crate::constants::constants_system::SIZE_1KB;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ impl RomChip for At28C256 {
|
|||||||
self.data[*offset as usize]
|
self.data[*offset as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn program(new_data: Box<[u8; SIZE_32KB]>) -> Box<At28C256> {
|
fn program(new_data: &[u8; SIZE_32KB]) -> Box<At28C256> {
|
||||||
println!("Writing new chip.");
|
println!("Writing new chip.");
|
||||||
let mut working = At28C256::default();
|
let mut working = At28C256::default();
|
||||||
working.data = Box::new(*new_data);
|
working.data = Box::new(*new_data);
|
||||||
|
|||||||
@ -29,7 +29,7 @@ impl RomChip for Hm62256 {
|
|||||||
self.data[effective as usize]
|
self.data[effective as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn program(_: Box<[u8; SIZE_32KB]>) -> Box<Self> {
|
fn program(_: &[u8; SIZE_32KB]) -> Box<Self> {
|
||||||
debug!("Dont program ram.");
|
debug!("Dont program ram.");
|
||||||
Hm62256::default().into()
|
Hm62256::default().into()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,5 +8,5 @@ pub trait RomChip {
|
|||||||
/// Program
|
/// Program
|
||||||
///
|
///
|
||||||
/// Replaces all data in the ROM chip
|
/// Replaces all data in the ROM chip
|
||||||
fn program(new_data: Box<[u8; SIZE_32KB]>) -> Box<Self>;
|
fn program(new_data: &[u8; SIZE_32KB]) -> Box<Self>;
|
||||||
}
|
}
|
||||||
|
|||||||
0
core/tests/execution_tests.rs
Normal file
0
core/tests/execution_tests.rs
Normal file
Loading…
x
Reference in New Issue
Block a user