mirror of
https://github.com/daniel5151/ANESE.git
synced 2024-06-01 19:07:27 -04:00
8423bd9a30
I'd like to think this commit marks the end of the first phase of ANESE's development. It's been a long road, but ANESE is finally at a point where I'm mostly happy with it. ANESE sure as hell isn't perfect, but hey, it's pretty good! The core code is pretty clean, the UI code is... acceptable, but most importantly, ANESE actually plays my favorite NES games! There is still some work to be done before I'd be comfortable giving ANESE a v1.0.0 release, but what I have here is still pretty great. Let's call this ANESE v0.9.0 :) ------- So, what next? Well, contrary to what I said in some earlier commits, I think i'll continue to work on ANESE a little bit more! Specifically, i'd like to rewrite the 6502 emulation. My current implementation is... okay. It's instruction-level cycle accurate, but I don't think that's good enough. It really should be sub-instruction level accurate. Odds are the added accuracy will fix _a lot_ of bugs. Aside from accuracy though, I have another reason to do a rewrite... As a Waterloo student, I have to do a Work Term Report on some technical project i've worked on recently. ANESE is one such project. Since the report isn't designed to be very long, I'd limit my scope to just a small aspect of ANESE: the 6502 emulator. Yes, I could just write the report on how I arrived at my current implementation, but I think it would be cool to attempt a cleaner rewrite, and compare and contrast the two versions. So yeah, stay tuned! I might also post the writeup (once I get around to it)
145 lines
4.2 KiB
C++
145 lines
4.2 KiB
C++
#pragma once
|
|
|
|
#include "common/util.h"
|
|
#include "nes/generic/ram/ram.h"
|
|
#include "nes/generic/rom/rom.h"
|
|
#include "nes/interfaces/memory.h"
|
|
#include "nes/interfaces/mirroring.h"
|
|
#include "rom_file.h"
|
|
|
|
#include "nes/wiring/interrupt_lines.h"
|
|
|
|
#include "common/serializable.h"
|
|
|
|
// Base Mapper Interface
|
|
// Implements common mapper utilities (eg: bank-chunking, IRQ handling)
|
|
// At minimum, Mappers must implement the following methods:
|
|
// - peek/write
|
|
// - update_banks
|
|
// - mirroring
|
|
// - reset
|
|
// The following virtual methods implement default behaviors:
|
|
// - read ................. calls peek
|
|
// - get/setBatterySave ... get returns nullptr, set does nothing
|
|
// - cycle ................ does nothing
|
|
// - power_cycle .......... clears CHR RAM, and calls reset + update_banks
|
|
class Mapper : public Memory, public Serializable {
|
|
private:
|
|
/*--------------------------------- Data ---------------------------------*/
|
|
|
|
// Identifiers
|
|
const char* name;
|
|
const uint number;
|
|
|
|
// Wiring
|
|
InterruptLines* interrupt_line = nullptr;
|
|
|
|
// Banks
|
|
struct {
|
|
struct {
|
|
uint len;
|
|
ROM** bank;
|
|
} prg;
|
|
|
|
struct {
|
|
bool is_RAM = false;
|
|
uint len;
|
|
Memory** bank;
|
|
} chr;
|
|
} banks;
|
|
|
|
/*---------------------------- Serialization -----------------------------*/
|
|
|
|
protected:
|
|
SERIALIZE_START(this->banks.chr.len, "Mapper")
|
|
SERIALIZE_CUSTOM() {
|
|
for (uint j = 0; j < this->banks.chr.len; j++) {
|
|
RAM* chr_bank = this->banks.chr.is_RAM
|
|
? static_cast<RAM*>(this->banks.chr.bank[j])
|
|
: nullptr;
|
|
SERIALIZE_SERIALIZABLE_PTR(chr_bank)
|
|
}
|
|
}
|
|
SERIALIZE_END(this->banks.chr.len)
|
|
|
|
virtual void update_banks() = 0;
|
|
|
|
virtual const Serializable::Chunk* deserialize(const Serializable::Chunk* c) override {
|
|
c = this->Serializable::deserialize(c);
|
|
this->update_banks();
|
|
return c;
|
|
}
|
|
|
|
/*------------------------------- Helpers --------------------------------*/
|
|
|
|
private:
|
|
void init_prg_banks(const ROM_File& rom_file, const u16 size);
|
|
void init_chr_banks(const ROM_File& rom_file, const u16 size);
|
|
|
|
/*----------------- Common Mapper Functions / Services -------------------*/
|
|
|
|
protected:
|
|
// Services provided to all mappers
|
|
void irq_trigger() const;
|
|
void irq_service() const;
|
|
uint get_prg_bank_len() const;
|
|
uint get_chr_bank_len() const;
|
|
ROM& get_prg_bank(uint bank) const;
|
|
Memory& get_chr_bank(uint bank) const;
|
|
|
|
/*-------------------------- External Interface --------------------------*/
|
|
|
|
public:
|
|
virtual ~Mapper();
|
|
Mapper(
|
|
uint number, const char* name,
|
|
const ROM_File& rom_file,
|
|
u16 prg_bank_size, u16 chr_bank_size
|
|
);
|
|
|
|
// Should be called when when inserting / removing cartridges from NES
|
|
void set_interrupt_line(InterruptLines* interrupt_line) {
|
|
this->interrupt_line = interrupt_line;
|
|
}
|
|
|
|
// ---- Mapper Queries ---- //
|
|
const char* mapper_name() const { return this->name; };
|
|
uint mapper_number() const { return this->number; };
|
|
|
|
// ---- Battery Backed Saving ---- //
|
|
virtual const Serializable::Chunk* getBatterySave() const { return nullptr; }
|
|
virtual void setBatterySave(const Serializable::Chunk* c) { return (void)c; }
|
|
|
|
/*------------------------ Core Mapper Interface -------------------------*/
|
|
|
|
// reads tend to be side-effect free, except with some fancier mappers
|
|
virtual u8 read(u16 addr) override { return this->peek(addr); }
|
|
virtual u8 peek(u16 addr) const override = 0;
|
|
virtual void write(u16 addr, u8 val) override = 0;
|
|
|
|
virtual Mirroring::Type mirroring() const = 0; // Get mirroring mode
|
|
|
|
virtual void cycle() {}
|
|
|
|
virtual void power_cycle() {
|
|
// NOTE: there are a couple of boards that have battery-backed CHR RAM.
|
|
// They required a complete override of the power_cycle() method
|
|
// iNES 168
|
|
// NES 2.0 513
|
|
if (this->banks.chr.is_RAM) {
|
|
for (uint j = 0; j < this->banks.chr.len; j++) {
|
|
static_cast<RAM*>(this->banks.chr.bank[j])->clear();
|
|
}
|
|
}
|
|
this->reset();
|
|
this->update_banks();
|
|
};
|
|
virtual void reset() = 0;
|
|
|
|
public:
|
|
/*------------------------------ Utilities -------------------------------*/
|
|
|
|
// creates correct mapper given an ROM_File object
|
|
static Mapper* Factory(const ROM_File* rom_file);
|
|
};
|