ANESE/src/nes/cartridge/mapper.h
Daniel Prilik 8423bd9a30 cleanup
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)
2018-07-03 22:42:18 -07:00

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);
};