Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 14 additions & 30 deletions aliusnes/build.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,36 @@
use heck::ToSnakeCase;
use std::{
env,
fs::File,
io::Write,
path::{Path, PathBuf},
};

enum Proc {
W65816,
Spc700,
}
use std::{env, fs::File, io::Write};

fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=tests");
generate_tomharte_opcode_test(Proc::W65816);
generate_tomharte_opcode_test(Proc::Spc700);
generate_tomharte_opcode_test("tomharte_65816.rs");
generate_tomharte_opcode_test("tomharte_spc700.rs");

let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let base_path = root_dir.join("tests/krom/");
let mut file = File::create(Path::new(&env::var_os("OUT_DIR").unwrap()).join("krom_test.rs"))
.expect("File creation failed");
let base_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/krom/");
let mut file = create_file_in_out_dir("krom_test.rs");

visit_dirs(&base_path, &mut file).unwrap();
}

fn generate_tomharte_opcode_test(proc: Proc) {
let (file_name, interface) = match proc {
Proc::W65816 => ("tomharte_65816.rs", "CpuState"),
Proc::Spc700 => ("tomharte_spc700.rs", "Spc700State"),
};

let mut file = File::create(Path::new(&env::var_os("OUT_DIR").unwrap()).join(file_name))
.expect("File creation failed");
fn generate_tomharte_opcode_test(file_name: &'static str) {
let mut file = create_file_in_out_dir(file_name);

for i in 0..256 {
let test_name = format!("test_{i:02x}");
let test_body = match proc {
Proc::W65816 => format!("run_test::<{interface}>(\"{i:02x}.n\");"),
Proc::Spc700 => format!("run_test::<{interface}>(\"{i:02x}\");"),
};
let test_body = format!("run_test::<CpuState>(\"{i:02x}\");");
let test = format!("#[test]\nfn {test_name}() {{\n\t{test_body}\n}}\n\n");

file.write_all(test.as_bytes())
.expect("Write to file failed");
}
}

fn create_file_in_out_dir(file_name: &'static str) -> File {
let file_path = std::path::Path::new(&env::var_os("OUT_DIR").unwrap()).join(file_name);
File::create(file_path).expect("File creation failed")
}

fn generate_krom_test(file: &mut File, path: &str, name: &str) {
let rom_path = format!("{path}/{name}.sfc");
let png_path = format!("{path}/{name}.png");
Expand All @@ -59,7 +43,7 @@ fn generate_krom_test(file: &mut File, path: &str, name: &str) {
.expect("Write to file failed");
}

fn visit_dirs(dir: &Path, file: &mut File) -> std::io::Result<()> {
fn visit_dirs(dir: &std::path::Path, file: &mut File) -> std::io::Result<()> {
if dir.is_dir() {
let mut has_subdir = false;
for entry in dir.read_dir()? {
Expand Down
419 changes: 116 additions & 303 deletions aliusnes/src/apu/spc700/mod.rs

Large diffs are not rendered by default.

295 changes: 295 additions & 0 deletions aliusnes/src/apu/spc700/opcode.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions aliusnes/src/bus/dma.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{utils::int_traits::ManipulateU16, w65c816::addressing::Address};
use crate::utils::int_traits::ManipulateU16;

use super::{system_bus::SystemBus, Access};
use super::{system_bus::SystemBus, Access, Address};

bitfield! {
#[derive(Copy, Clone)]
Expand Down
59 changes: 55 additions & 4 deletions aliusnes/src/bus/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,62 @@
use crate::w65c816::addressing::Address;

pub(super) mod dma;
mod math;
pub mod system_bus;
mod wram;

pub trait Bus {
#[derive(Clone, Copy)]
pub(crate) struct Address {
pub bank: u8,
pub offset: u16,
}

impl Address {
pub fn new(offset: u16, bank: u8) -> Self {
Self { bank, offset }
}

pub fn wrapping_offset_add(&self, rhs: u16) -> Self {
Self {
bank: self.bank,
offset: self.offset.wrapping_add(rhs),
}
}

pub fn wrapping_add(&self, rhs: u32) -> Self {
(u32::from(*self).wrapping_add(rhs)).into()
}
}

impl From<u16> for Address {
fn from(value: u16) -> Self {
Self {
bank: 0,
offset: value,
}
}
}

impl From<u32> for Address {
fn from(value: u32) -> Self {
Self {
bank: (value >> 16) as u8,
offset: value as u16,
}
}
}

impl From<Address> for u32 {
fn from(value: Address) -> Self {
(u32::from(value.bank) << 16) | u32::from(value.offset)
}
}

impl From<Address> for usize {
fn from(value: Address) -> Self {
((value.bank as usize) << 16) | value.offset as usize
}
}

pub(crate) trait Bus {
fn peek_at(&self, addr: Address) -> Option<u8>;
fn read_and_tick(&mut self, addr: Address) -> u8;
fn write_and_tick(&mut self, addr: Address, data: u8);
Expand All @@ -14,7 +65,7 @@ pub trait Bus {
fn fired_irq(&mut self) -> bool;
}

pub trait Access {
pub(crate) trait Access {
fn read(&mut self, addr: u16, time: u64) -> Option<u8>;
fn write(&mut self, addr: u16, data: u8);
}
44 changes: 18 additions & 26 deletions aliusnes/src/bus/system_bus.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use crate::{
cart::Cart, ppu::Ppu, scheduler::Scheduler, utils::int_traits::ManipulateU16,
w65c816::addressing::Address,
};
use crate::{cart::Cart, ppu::Ppu, scheduler::Scheduler, utils::int_traits::ManipulateU16};

use super::{dma::Dma, math::Math, wram::Wram, Access, Bus};
use super::{dma::Dma, math::Math, wram::Wram, Access, Address, Bus};

pub struct SystemBus {
mdr: u8,
Expand Down Expand Up @@ -60,24 +57,7 @@ impl SystemBus {
}
}

fn peek(&self, addr: Address) -> Option<u8> {
let bank = addr.bank;
let page = addr.offset;

match bank {
0x00..=0x3F | 0x80..=0xBF => {
if let 0x00..=0x1F = page.high_byte() {
return Some(self.wram.ram[page as usize & 0x1FFF]);
}
}
0x7E..=0x7F => return Some(self.wram.ram[u32::from(addr) as usize & 0x1_FFFF]),
_ => {}
}

self.cart.read(bank.into(), page.into())
}

pub fn read<const DMA: bool>(&mut self, addr: Address) -> u8 {
pub(crate) fn read<const DMA: bool>(&mut self, addr: Address) -> u8 {
let bank = addr.bank;
let page = addr.offset;

Expand Down Expand Up @@ -140,7 +120,7 @@ impl SystemBus {
}
}

pub fn write<const DMA: bool>(&mut self, addr: Address, data: u8) {
pub(crate) fn write<const DMA: bool>(&mut self, addr: Address, data: u8) {
self.mdr = data;
let bank = addr.bank;
let page = addr.offset;
Expand Down Expand Up @@ -170,7 +150,7 @@ impl SystemBus {
self.cart.write(bank.into(), page.into(), data);
}

pub fn memory_access_cycles(&self, addr: &Address) -> u32 {
pub(crate) fn memory_access_cycles(&self, addr: &Address) -> u32 {
const FAST: u32 = 6;
const SLOW: u32 = 8;
const XSLOW: u32 = 12;
Expand Down Expand Up @@ -202,7 +182,19 @@ impl SystemBus {

impl Bus for SystemBus {
fn peek_at(&self, addr: Address) -> Option<u8> {
self.peek(addr)
let bank = addr.bank;
let page = addr.offset;

match bank {
0x00..=0x3F | 0x80..=0xBF => {
if let 0x00..=0x1F = page.high_byte() {
return Some(self.wram.ram[page as usize & 0x1FFF]);
}
}
0x7E..=0x7F => return Some(self.wram.ram[u32::from(addr) as usize & 0x1_FFFF]),
_ => {}
}
self.cart.read(bank.into(), page.into())
}

fn read_and_tick(&mut self, addr: Address) -> u8 {
Expand Down
4 changes: 2 additions & 2 deletions aliusnes/src/bus/wram.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::Access;
use crate::{utils::int_traits::ManipulateU16, w65c816::addressing::Address};
use super::{Access, Address};
use crate::utils::int_traits::ManipulateU16;

pub struct Wram {
pub ram: [u8; 0x20000],
Expand Down
2 changes: 1 addition & 1 deletion aliusnes/src/emu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl Emu {
}

pub fn reset(&mut self) {
self.w65c816.cpu.reset(&mut self.bus);
self.w65c816.reset(&mut self.bus);
}

pub fn step(&mut self) {
Expand Down
4 changes: 4 additions & 0 deletions aliusnes/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
pub mod int_traits;
#[cfg(test)]
pub(crate) mod testbus;
#[cfg(test)]
pub(crate) mod testrun;
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
use aliusnes::{bus::Bus, w65c816::addressing::Address};
use crate::bus::{Address, Bus};
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;

pub(crate) fn deserialize_as_map<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<HashMap<u32, u8>, D::Error> {
Vec::deserialize(deserializer).map(|vec| vec.into_iter().collect())
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Cycle {
Expand Down
48 changes: 15 additions & 33 deletions aliusnes/tests/tomharte_test.rs → aliusnes/src/utils/testrun.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
mod utils;
use crate::utils::testbus::Cycle;
use serde::{Deserialize, Deserializer};
use std::{fs::File, io::BufReader};
use xz2::read::XzDecoder;

use pretty_assertions::Comparison;

use std::{
fmt::{Debug, Display},
fs::File,
io::BufReader,
path::PathBuf,
};

use pretty_assertions::Comparison;
use serde::{Deserialize, Deserializer};
use utils::test_bus::{Cycle, TomHarteBus};
use xz2::read::XzDecoder;

mod w65c816 {
use crate::{run_test, utils::cpu_state::CpuState};
include!(concat!(env!("OUT_DIR"), "/tomharte_65816.rs"));
}

mod spc700 {
use crate::{run_test, utils::cpu_state::Spc700State};
include!(concat!(env!("OUT_DIR"), "/tomharte_spc700.rs"));
}

#[derive(Deserialize)]
struct TestCase<T> {
name: String,
Expand All @@ -47,30 +35,26 @@ impl<T: OpcodeTest> TestCase<T> {
}

pub(crate) fn run_test<T: OpcodeTest>(name: &str) {
for mut test_case in TestCase::<T>::iter_json(T::test_path(name)) {
let (proc, bus, skip_cycles) = test_case
for test_case in TestCase::<T>::iter_json(T::test_path(name)) {
let (result, mut cycles, skip_cycles) = test_case
.initial
.do_step(&test_case.final_state, test_case.cycles.len());
.step(&test_case.final_state, test_case.cycles.len());

let mut cycles = bus.cycles.clone();
cycles.sort();

let state = T::from((proc, bus));

let state_match = state == test_case.final_state;
let state_match = result == test_case.final_state;
let cycles_match = cycles == test_case.cycles || skip_cycles;

if state_match && cycles_match {
continue;
}

println!("Test {} failed", test_case.name,);

println!("Test {} failed", test_case.name);
if !state_match {
println!("Initial: {}", &test_case.initial);
println!(
"Result: {}",
Comparison::new(&state, &test_case.final_state)
Comparison::new(&result, &test_case.final_state)
);
}
if !cycles_match {
Expand All @@ -80,13 +64,11 @@ pub(crate) fn run_test<T: OpcodeTest>(name: &str) {
}
}

pub trait OpcodeTest:
Debug + Display + PartialEq + for<'de> Deserialize<'de> + From<(Self::Proc, TomHarteBus)>
pub(crate) trait OpcodeTest:
Debug + Display + PartialEq + for<'de> Deserialize<'de>
{
type Proc;

fn test_path(name: &str) -> PathBuf;
fn do_step(&mut self, other: &Self, cycles_len: usize) -> (Self::Proc, TomHarteBus, bool);
fn step(&self, other: &Self, cycles_len: usize) -> (Self, Vec<Cycle>, bool);
fn deserialize_cycles<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<Cycle>, D::Error>;
Expand Down
Loading
Loading