mirror of https://github.com/bsnes-emu/bsnes.git
Update to v082r27 release.
byuu says: Finished porting over all mappers to board/chip disambiguations. Had to nearly rewrite the MMC1 code to do it, but all variants should be supported. iNES1 is too stupid to express them all, so you'll need a board markup if you want to play the >8KB PRG RAM games. For whatever reason, they call VRC6's memory WRAM, and MMC1's PRG RAM. I am calling them all PRG RAM, since it's the same damn thing. Board spec is not going to be stable until I have a hell of a lot more mappers implemented, so be wary of that. Anyway, at this time, all games can be loaded sans iNES header, if you have the board markup. I'd also like to have a board database for all commercial titles. I'm treating *.fc as PRG-ROM(+CHR-ROM). Will work on loading the split files later possibly.
This commit is contained in:
parent
7115047d85
commit
ba2e6b5789
|
@ -40,6 +40,8 @@ void reset() {
|
|||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.integer(prg_bank);
|
||||
s.integer(mirror_select);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
//BANDAI-FCG
|
||||
|
||||
struct BandaiFCG : Board {
|
||||
|
||||
uint8 chr_bank[8];
|
||||
uint8 prg_bank;
|
||||
uint2 mirror;
|
||||
bool irq_counter_enable;
|
||||
uint16 irq_counter;
|
||||
uint16 irq_latch;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(1);
|
||||
irq_counter_enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
case 2: return 0x0000 | (addr & 0x03ff);
|
||||
case 3: return 0x0400 | (addr & 0x03ff);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) {
|
||||
bool region = addr & 0x4000;
|
||||
unsigned bank = (region == 0 ? prg_bank : 0x0f);
|
||||
return Board::prg_read((bank << 14) | (addr & 0x3fff));
|
||||
}
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if(addr >= 0x6000) {
|
||||
switch(addr & 15) {
|
||||
case 0x00: case 0x01: case 0x02: case 0x03:
|
||||
case 0x04: case 0x05: case 0x06: case 0x07:
|
||||
chr_bank[addr & 7] = data;
|
||||
break;
|
||||
case 0x08:
|
||||
prg_bank = data & 0x0f;
|
||||
break;
|
||||
case 0x09:
|
||||
mirror = data & 0x03;
|
||||
break;
|
||||
case 0x0a:
|
||||
cpu.set_irq_line(0);
|
||||
irq_counter_enable = data & 0x01;
|
||||
irq_counter = irq_latch;
|
||||
break;
|
||||
case 0x0b:
|
||||
irq_latch = (irq_latch & 0xff00) | (data << 0);
|
||||
break;
|
||||
case 0x0c:
|
||||
irq_latch = (irq_latch & 0x00ff) | (data << 8);
|
||||
break;
|
||||
case 0x0d:
|
||||
//TODO: serial EEPROM support
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
||||
return Board::chr_read(addr);
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
||||
return Board::chr_write(addr, data);
|
||||
}
|
||||
|
||||
void power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for(auto &n : chr_bank) n = 0;
|
||||
prg_bank = 0;
|
||||
mirror = 0;
|
||||
irq_counter_enable = 0;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.array(chr_bank);
|
||||
s.integer(prg_bank);
|
||||
s.integer(mirror);
|
||||
s.integer(irq_counter_enable);
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_latch);
|
||||
}
|
||||
|
||||
BandaiFCG(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
|
||||
}
|
||||
|
||||
};
|
|
@ -2,7 +2,10 @@
|
|||
#include "cnrom.cpp"
|
||||
#include "nrom.cpp"
|
||||
#include "sxrom.cpp"
|
||||
#include "txrom.cpp"
|
||||
#include "uxrom.cpp"
|
||||
#include "bandai-fcg.cpp"
|
||||
#include "konami-vrc6.cpp"
|
||||
|
||||
unsigned Board::mirror(unsigned addr, unsigned size) const {
|
||||
unsigned base = 0;
|
||||
|
@ -106,10 +109,16 @@ Board* Board::load(const string &markup, const uint8_t *data, unsigned size) {
|
|||
if(type == "NES-AN1ROM" ) return new AxROM(board, data, size);
|
||||
if(type == "NES-AOROM" ) return new AxROM(board, data, size);
|
||||
if(type == "NES-CNROM" ) return new CNROM(board, data, size);
|
||||
if(type == "NES-NROM-256") return new NROM(board, data, size);
|
||||
if(type == "NES-NROM-256") return new NROM (board, data, size);
|
||||
if(type == "NES-UNROM" ) return new UxROM(board, data, size);
|
||||
if(type == "NES-SNROM" ) return new SxROM(board, data, size);
|
||||
if(type == "NES-SXROM" ) return new SxROM(board, data, size);
|
||||
if(type == "NES-TLROM" ) return new TxROM(board, data, size);
|
||||
if(type == "NES-UOROM" ) return new UxROM(board, data, size);
|
||||
|
||||
if(type == "BANDAI-FCG") return new BandaiFCG(board, data, size);
|
||||
|
||||
if(type == "KONAMI-VRC-6") return new KonamiVRC6(board, data, size);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ struct Board {
|
|||
struct Memory {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
inline Memory(uint8_t *data, unsigned size) : data(data), size(size) {}
|
||||
inline Memory() : data(nullptr), size(0u) {}
|
||||
};
|
||||
|
||||
|
@ -32,7 +33,6 @@ struct Board {
|
|||
bool battery;
|
||||
} information;
|
||||
|
||||
protected:
|
||||
Memory prgrom;
|
||||
Memory prgram;
|
||||
Memory chrrom;
|
||||
|
|
|
@ -44,6 +44,8 @@ void reset() {
|
|||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.integer(chr_bank);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
struct KonamiVRC6 : Board {
|
||||
|
||||
VRC6 vrc6;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) return vrc6.ram_read(addr);
|
||||
if(addr & 0x8000) return Board::prg_read(vrc6.prg_addr(addr));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) return vrc6.ram_write(addr, data);
|
||||
if(addr & 0x8000) {
|
||||
addr = (addr & 0xf003);
|
||||
if(prgram.size) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
|
||||
return vrc6.reg_write(addr, data);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(vrc6.ciram_addr(addr));
|
||||
return Board::chr_read(vrc6.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(vrc6.ciram_addr(addr), data);
|
||||
return Board::chr_write(vrc6.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
Board::serialize(s);
|
||||
vrc6.serialize(s);
|
||||
}
|
||||
|
||||
void main() { vrc6.main(); }
|
||||
void power() { vrc6.power(); }
|
||||
void reset() { vrc6.reset(); }
|
||||
Memory memory() { return prgram; }
|
||||
|
||||
KonamiVRC6(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc6(*this) {
|
||||
}
|
||||
|
||||
};
|
|
@ -31,6 +31,10 @@ void chr_write(unsigned addr, uint8 data) {
|
|||
return Board::chr_write(addr, data);
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
Board::serialize(s);
|
||||
}
|
||||
|
||||
NROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
|
||||
settings.mirror = board["mirror"].value() == "vertical" ? 1 : 0;
|
||||
}
|
||||
|
|
|
@ -1,29 +1,148 @@
|
|||
//NES-SAROM
|
||||
//NES-SBROM
|
||||
//NES-SCROM
|
||||
//NES-SC1ROM
|
||||
//NES-SEROM
|
||||
//NES-SFROM
|
||||
//NES-SGROM
|
||||
//NES-SHROM
|
||||
//NES-SH1ROM
|
||||
//NES-SIROM
|
||||
//NES-SJROM
|
||||
//NES-SKROM
|
||||
//NES-SLROM
|
||||
//NES-SL1ROM
|
||||
//NES-SL2ROM
|
||||
//NES-SL3ROM
|
||||
//NES-SLRROM
|
||||
//NES-SMROM
|
||||
//NES-SNROM
|
||||
//NES-SOROM
|
||||
//NES-SUROM
|
||||
//NES-SXROM
|
||||
|
||||
struct SxROM : Board {
|
||||
|
||||
SxROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size) {
|
||||
enum class Revision : unsigned {
|
||||
SAROM,
|
||||
SBROM,
|
||||
SCROM,
|
||||
SC1ROM,
|
||||
SEROM,
|
||||
SFROM,
|
||||
SGROM,
|
||||
SHROM,
|
||||
SH1ROM,
|
||||
SIROM,
|
||||
SJROM,
|
||||
SKROM,
|
||||
SLROM,
|
||||
SL1ROM,
|
||||
SL2ROM,
|
||||
SL3ROM,
|
||||
SLRROM,
|
||||
SMROM,
|
||||
SNROM,
|
||||
SOROM,
|
||||
SUROM,
|
||||
SXROM,
|
||||
} revision;
|
||||
|
||||
MMC1 mmc1;
|
||||
|
||||
unsigned shiftaddr;
|
||||
unsigned shiftdata;
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) return mmc1.ram_read(addr);
|
||||
if(addr & 0x8000) return Board::prg_read(mmc1.prg_addr(addr));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) return mmc1.ram_write(addr, data);
|
||||
|
||||
if(addr & 0x8000) {
|
||||
if(data & 0x80) {
|
||||
shiftaddr = 0;
|
||||
mmc1.prg_size = 1;
|
||||
mmc1.prg_mode = 1;
|
||||
} else {
|
||||
shiftdata = ((data & 1) << 4) | (shiftdata >> 1);
|
||||
if(++shiftaddr == 5) {
|
||||
shiftaddr = 0;
|
||||
reg_write((addr >> 13) & 3, shiftdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(mmc1.ciram_addr(addr));
|
||||
return Board::chr_read(mmc1.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(mmc1.ciram_addr(addr), data);
|
||||
return Board::chr_write(mmc1.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr) {
|
||||
case 0:
|
||||
mmc1.chr_mode = (data & 0x10);
|
||||
mmc1.prg_size = (data & 0x08);
|
||||
mmc1.prg_mode = (data & 0x04);
|
||||
mmc1.mirror = (data & 0x03);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
mmc1.chr_bank[0] = (data & 0x1f);
|
||||
switch(revision) {
|
||||
case Revision::SNROM:
|
||||
mmc1.ram_disable[1] = (data & 0x10);
|
||||
break;
|
||||
case Revision::SOROM:
|
||||
mmc1.ram_bank = (data & 0x08) >> 3;
|
||||
break;
|
||||
case Revision::SUROM:
|
||||
mmc1.prg_page = (data & 0x10);
|
||||
break;
|
||||
case Revision::SXROM:
|
||||
mmc1.prg_page = (data & 0x10);
|
||||
mmc1.ram_bank = (data & 0x0c) >> 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
mmc1.chr_bank[1] = (data & 0x1f);
|
||||
switch(revision) {
|
||||
case Revision::SNROM:
|
||||
mmc1.ram_disable[1] = (data & 0x10);
|
||||
break;
|
||||
case Revision::SOROM:
|
||||
mmc1.ram_bank = (data & 0x08) >> 3;
|
||||
break;
|
||||
case Revision::SUROM:
|
||||
mmc1.prg_page = (data & 0x10);
|
||||
break;
|
||||
case Revision::SXROM:
|
||||
mmc1.prg_page = (data & 0x10);
|
||||
mmc1.ram_bank = (data & 0x0c) >> 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
mmc1.ram_disable[0] = (data & 0x10);
|
||||
mmc1.prg_bank = (data & 0x0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Memory memory() {
|
||||
return prgram;
|
||||
}
|
||||
|
||||
void power() {
|
||||
mmc1.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mmc1.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
Board::serialize(s);
|
||||
mmc1.serialize(s);
|
||||
|
||||
s.integer(shiftaddr);
|
||||
s.integer(shiftdata);
|
||||
}
|
||||
|
||||
SxROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), mmc1(*this) {
|
||||
revision = Revision::SXROM;
|
||||
|
||||
shiftaddr = 0;
|
||||
shiftdata = 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
struct TxROM : Board {
|
||||
|
||||
enum class Revision : unsigned {
|
||||
TBROM,
|
||||
TEROM,
|
||||
TFROM,
|
||||
TGROM,
|
||||
TKROM,
|
||||
TKSROM,
|
||||
TLROM,
|
||||
TL1ROM,
|
||||
TL2ROM,
|
||||
TLSROM,
|
||||
TNROM,
|
||||
TQROM,
|
||||
TR1ROM,
|
||||
TSROM,
|
||||
TVROM,
|
||||
} revision;
|
||||
|
||||
MMC3 mmc3;
|
||||
|
||||
void main() {
|
||||
mmc3.main();
|
||||
}
|
||||
|
||||
uint8 prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) return mmc3.ram_read(addr);
|
||||
if(addr & 0x8000) return Board::prg_read(mmc3.prg_addr(addr));
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) return mmc3.ram_write(addr, data);
|
||||
|
||||
switch(addr & 0xe001) {
|
||||
case 0x8000:
|
||||
mmc3.chr_mode = data & 0x80;
|
||||
mmc3.prg_mode = data & 0x40;
|
||||
mmc3.bank_select = data & 0x07;
|
||||
break;
|
||||
|
||||
case 0x8001:
|
||||
switch(mmc3.bank_select) {
|
||||
case 0: mmc3.chr_bank[0] = data & ~1; break;
|
||||
case 1: mmc3.chr_bank[1] = data & ~1; break;
|
||||
case 2: mmc3.chr_bank[2] = data; break;
|
||||
case 3: mmc3.chr_bank[3] = data; break;
|
||||
case 4: mmc3.chr_bank[4] = data; break;
|
||||
case 5: mmc3.chr_bank[5] = data; break;
|
||||
case 6: mmc3.prg_bank[0] = data & 0x3f; break;
|
||||
case 7: mmc3.prg_bank[1] = data & 0x3f; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa000:
|
||||
mmc3.mirror = data & 0x01;
|
||||
break;
|
||||
|
||||
case 0xa001:
|
||||
mmc3.ram_enable = data & 0x80;
|
||||
mmc3.ram_write_protect = data & 0x40;
|
||||
break;
|
||||
|
||||
case 0xc000:
|
||||
mmc3.irq_latch = data;
|
||||
break;
|
||||
|
||||
case 0xc001:
|
||||
mmc3.irq_counter = 0;
|
||||
break;
|
||||
|
||||
case 0xe000:
|
||||
mmc3.irq_enable = false;
|
||||
mmc3.irq_line = 0;
|
||||
break;
|
||||
|
||||
case 0xe001:
|
||||
mmc3.irq_enable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 chr_read(unsigned addr) {
|
||||
mmc3.irq_test(addr);
|
||||
if(addr & 0x2000) return ppu.ciram_read(mmc3.ciram_addr(addr));
|
||||
return Board::chr_read(mmc3.chr_addr(addr));
|
||||
}
|
||||
|
||||
void chr_write(unsigned addr, uint8 data) {
|
||||
mmc3.irq_test(addr);
|
||||
if(addr & 0x2000) return ppu.ciram_write(mmc3.ciram_addr(addr), data);
|
||||
return Board::chr_write(mmc3.chr_addr(addr), data);
|
||||
}
|
||||
|
||||
Memory memory() {
|
||||
return prgram;
|
||||
}
|
||||
|
||||
void power() {
|
||||
mmc3.power();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mmc3.reset();
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
Board::serialize(s);
|
||||
mmc3.serialize(s);
|
||||
}
|
||||
|
||||
TxROM(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), mmc3(*this) {
|
||||
revision = Revision::TLROM;
|
||||
}
|
||||
|
||||
};
|
|
@ -44,6 +44,8 @@ void reset() {
|
|||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
Board::serialize(s);
|
||||
|
||||
s.integer(prg_bank);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
|
|||
sha256 = nall::sha256(data, size);
|
||||
board = Board::load(markup, data, size);
|
||||
} else {
|
||||
sha256 = nall::sha256(data + 16, size - 16);
|
||||
board = Board::load(markup != "" ? markup : iNES(data, size), data + 16, size - 16);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ struct Cartridge : Processor, property<Cartridge> {
|
|||
void unload();
|
||||
|
||||
unsigned ram_size();
|
||||
uint8 *ram_data();
|
||||
uint8* ram_data();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#include "mmc1.cpp"
|
||||
#include "mmc3.cpp"
|
||||
#include "vrc6.cpp"
|
||||
|
||||
void Chip::tick() {
|
||||
board.tick();
|
||||
}
|
||||
|
||||
Chip::Chip(Board &board) : board(board) {
|
||||
}
|
|
@ -1,2 +1,7 @@
|
|||
struct Board;
|
||||
|
||||
struct Chip {
|
||||
Board &board;
|
||||
void tick();
|
||||
Chip(Board &board);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
struct MMC1 : Chip {
|
||||
|
||||
enum class Revision : unsigned {
|
||||
MMC1,
|
||||
MMC1A,
|
||||
MMC1B1,
|
||||
MMC1B2,
|
||||
MMC1B3,
|
||||
MMC1C,
|
||||
} revision;
|
||||
|
||||
bool chr_mode;
|
||||
bool prg_size; //0 = 32K, 1 = 16K
|
||||
bool prg_mode;
|
||||
uint2 mirror; //0 = first, 1 = second, 2 = vertical, 3 = horizontal
|
||||
uint5 chr_bank[2];
|
||||
uint4 prg_bank;
|
||||
bool prg_page;
|
||||
uint2 ram_bank;
|
||||
bool ram_disable[2];
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
bool region = addr & 0x4000;
|
||||
unsigned bank = (prg_bank & ~1) + region;
|
||||
|
||||
if(prg_size) {
|
||||
bank = (region == 0 ? 0x0 : 0xf);
|
||||
if(region != prg_mode) bank = prg_bank;
|
||||
}
|
||||
|
||||
return (prg_page << 18) | (bank << 14) | (addr & 0x3fff);
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
bool region = addr & 0x1000;
|
||||
unsigned bank = chr_bank[region];
|
||||
if(chr_mode == 0) bank = (chr_bank[0] & ~1) | region;
|
||||
return (bank << 12) | (addr & 0x0fff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return 0x0000 | (addr & 0x03ff);
|
||||
case 1: return 0x0400 | (addr & 0x03ff);
|
||||
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 ram_read(unsigned addr) {
|
||||
addr = (ram_bank * 0x2000) | (addr & 0x1fff);
|
||||
if(ram_disable[0] == false && ram_disable[1] == false) return board.prgram.data[addr];
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void ram_write(unsigned addr, uint8 data) {
|
||||
addr = (ram_bank * 0x2000) | (addr & 0x1fff);
|
||||
if(ram_disable[0] == false && ram_disable[1] == false) board.prgram.data[addr] = data;
|
||||
}
|
||||
|
||||
void power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
chr_mode = 0;
|
||||
prg_size = 1;
|
||||
prg_mode = 1;
|
||||
mirror = 0;
|
||||
chr_bank[0] = 0;
|
||||
chr_bank[1] = 1;
|
||||
prg_bank = 0;
|
||||
prg_page = 0;
|
||||
ram_bank = 0;
|
||||
ram_disable[0] = 0;
|
||||
ram_disable[1] = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
s.integer(chr_mode);
|
||||
s.integer(prg_size);
|
||||
s.integer(prg_mode);
|
||||
s.integer(mirror);
|
||||
s.array(chr_bank);
|
||||
s.integer(prg_bank);
|
||||
s.integer(prg_page);
|
||||
s.integer(ram_bank);
|
||||
s.array(ram_disable);
|
||||
}
|
||||
|
||||
MMC1(Board &board) : Chip(board) {
|
||||
revision = Revision::MMC1B2;
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,141 @@
|
|||
struct MMC3 : Chip {
|
||||
|
||||
bool chr_mode;
|
||||
bool prg_mode;
|
||||
uint3 bank_select;
|
||||
uint8 prg_bank[2];
|
||||
uint8 chr_bank[6];
|
||||
bool mirror;
|
||||
bool ram_enable;
|
||||
bool ram_write_protect;
|
||||
uint8 irq_latch;
|
||||
uint8 irq_counter;
|
||||
bool irq_enable;
|
||||
unsigned irq_delay;
|
||||
bool irq_line;
|
||||
|
||||
uint16 chr_abus;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_delay) irq_delay--;
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void irq_test(unsigned addr) {
|
||||
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
|
||||
if(irq_delay == 0) {
|
||||
if(irq_counter == 0) {
|
||||
irq_counter = irq_latch;
|
||||
} else if(--irq_counter == 0) {
|
||||
if(irq_enable) irq_line = 1;
|
||||
}
|
||||
}
|
||||
irq_delay = 6;
|
||||
}
|
||||
chr_abus = addr;
|
||||
}
|
||||
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
switch((addr >> 13) & 3) {
|
||||
case 0:
|
||||
if(prg_mode == 1) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
case 1:
|
||||
return (prg_bank[1] << 13) | (addr & 0x1fff);
|
||||
case 2:
|
||||
if(prg_mode == 0) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
case 3:
|
||||
return (0x3f << 13) | (addr & 0x1fff);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
if(chr_mode == 0) {
|
||||
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
|
||||
} else {
|
||||
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
if(mirror == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
if(mirror == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
}
|
||||
|
||||
uint8 ram_read(unsigned addr) {
|
||||
if(ram_enable) return board.prgram.data[addr & 0x1fff];
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void ram_write(unsigned addr, uint8 data) {
|
||||
if(ram_enable && !ram_write_protect) board.prgram.data[addr & 0x1fff] = data;
|
||||
}
|
||||
|
||||
void power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
chr_mode = 0;
|
||||
prg_mode = 0;
|
||||
bank_select = 0;
|
||||
prg_bank[0] = 0;
|
||||
prg_bank[1] = 0;
|
||||
chr_bank[0] = 0;
|
||||
chr_bank[1] = 0;
|
||||
chr_bank[2] = 0;
|
||||
chr_bank[3] = 0;
|
||||
chr_bank[4] = 0;
|
||||
chr_bank[5] = 0;
|
||||
mirror = 0;
|
||||
ram_enable = 1;
|
||||
ram_write_protect = 0;
|
||||
irq_latch = 0;
|
||||
irq_counter = 0;
|
||||
irq_enable = false;
|
||||
irq_delay = 0;
|
||||
irq_line = 0;
|
||||
|
||||
chr_abus = 0;
|
||||
}
|
||||
|
||||
void serialize(serializer &s) {
|
||||
s.integer(chr_mode);
|
||||
s.integer(prg_mode);
|
||||
s.integer(bank_select);
|
||||
s.array(prg_bank);
|
||||
s.array(chr_bank);
|
||||
s.integer(mirror);
|
||||
s.integer(ram_enable);
|
||||
s.integer(ram_write_protect);
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_delay);
|
||||
s.integer(irq_line);
|
||||
|
||||
s.integer(chr_abus);
|
||||
}
|
||||
|
||||
MMC3(Board &board) : Chip(board) {
|
||||
}
|
||||
|
||||
};
|
|
@ -1,32 +1,92 @@
|
|||
VRC6 vrc6;
|
||||
struct VRC6 : Chip {
|
||||
|
||||
void VRC6::Pulse::clock() {
|
||||
if(--divider == 0) {
|
||||
divider = frequency + 1;
|
||||
cycle++;
|
||||
output = (mode == 1 || cycle > duty) ? volume : (uint4)0;
|
||||
uint8 prg_bank[2];
|
||||
uint8 chr_bank[8];
|
||||
uint2 mirror;
|
||||
uint8 irq_latch;
|
||||
bool irq_mode;
|
||||
bool irq_enable;
|
||||
bool irq_acknowledge;
|
||||
|
||||
uint8 irq_counter;
|
||||
signed irq_scalar;
|
||||
bool irq_line;
|
||||
|
||||
struct Pulse {
|
||||
bool mode;
|
||||
uint3 duty;
|
||||
uint4 volume;
|
||||
bool enable;
|
||||
uint12 frequency;
|
||||
|
||||
uint12 divider;
|
||||
uint4 cycle;
|
||||
uint4 output;
|
||||
|
||||
void clock() {
|
||||
if(--divider == 0) {
|
||||
divider = frequency + 1;
|
||||
cycle++;
|
||||
output = (mode == 1 || cycle > duty) ? volume : (uint4)0;
|
||||
}
|
||||
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
void serialize(serializer &s) {
|
||||
s.integer(mode);
|
||||
s.integer(duty);
|
||||
s.integer(volume);
|
||||
s.integer(enable);
|
||||
s.integer(frequency);
|
||||
|
||||
void VRC6::Sawtooth::clock() {
|
||||
if(--divider == 0) {
|
||||
divider = frequency + 1;
|
||||
if(++phase == 0) {
|
||||
accumulator += rate;
|
||||
if(++stage == 7) {
|
||||
stage = 0;
|
||||
accumulator = 0;
|
||||
s.integer(divider);
|
||||
s.integer(cycle);
|
||||
s.integer(output);
|
||||
}
|
||||
} pulse1, pulse2;
|
||||
|
||||
struct Sawtooth {
|
||||
uint6 rate;
|
||||
bool enable;
|
||||
uint12 frequency;
|
||||
|
||||
uint12 divider;
|
||||
uint1 phase;
|
||||
uint3 stage;
|
||||
uint8 accumulator;
|
||||
uint5 output;
|
||||
|
||||
void clock() {
|
||||
if(--divider == 0) {
|
||||
divider = frequency + 1;
|
||||
if(++phase == 0) {
|
||||
accumulator += rate;
|
||||
if(++stage == 7) {
|
||||
stage = 0;
|
||||
accumulator = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output = accumulator >> 3;
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
|
||||
output = accumulator >> 3;
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
void serialize(serializer &s) {
|
||||
s.integer(rate);
|
||||
s.integer(enable);
|
||||
s.integer(frequency);
|
||||
|
||||
void VRC6::main() {
|
||||
s.integer(divider);
|
||||
s.integer(phase);
|
||||
s.integer(stage);
|
||||
s.integer(accumulator);
|
||||
s.integer(output);
|
||||
}
|
||||
} sawtooth;
|
||||
|
||||
void main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
|
@ -67,35 +127,35 @@ void VRC6::main() {
|
|||
}
|
||||
}
|
||||
|
||||
uint8 VRC6::prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
return prg_ram[addr & 0x1fff];
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x8000) {
|
||||
return Mapper::prg_read((prg_bank[0] << 14) | (addr & 0x3fff));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xc000) {
|
||||
return Mapper::prg_read((prg_bank[1] << 13) | (addr & 0x1fff));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xe000) {
|
||||
return Mapper::prg_read((0xff << 13) | (addr & 0x1fff));
|
||||
}
|
||||
|
||||
return cpu.mdr();
|
||||
unsigned prg_addr(unsigned addr) const {
|
||||
if((addr & 0xc000) == 0x8000) return (prg_bank[0] << 14) | (addr & 0x3fff);
|
||||
if((addr & 0xe000) == 0xc000) return (prg_bank[1] << 13) | (addr & 0x1fff);
|
||||
if((addr & 0xe000) == 0xe000) return ( 0xff << 13) | (addr & 0x1fff);
|
||||
}
|
||||
|
||||
void VRC6::prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
prg_ram[addr & 0x1fff] = data;
|
||||
return;
|
||||
unsigned chr_addr(unsigned addr) const {
|
||||
unsigned bank = chr_bank[(addr >> 10) & 7];
|
||||
return (bank << 10) | (addr & 0x03ff);
|
||||
}
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const {
|
||||
switch(mirror) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||
}
|
||||
}
|
||||
|
||||
addr = (addr & 0xf003);
|
||||
if(abus_swap) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
|
||||
uint8 ram_read(unsigned addr) {
|
||||
return board.prgram.data[addr & 0x1fff];
|
||||
}
|
||||
|
||||
void ram_write(unsigned addr, uint8 data) {
|
||||
board.prgram.data[addr & 0x1fff] = data;
|
||||
}
|
||||
|
||||
void reg_write(unsigned addr, uint8 data) {
|
||||
switch(addr) {
|
||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||
prg_bank[0] = data;
|
||||
|
@ -145,7 +205,7 @@ void VRC6::prg_write(unsigned addr, uint8 data) {
|
|||
break;
|
||||
|
||||
case 0xb003:
|
||||
mirror_select = (data >> 2) & 3;
|
||||
mirror = (data >> 2) & 3;
|
||||
break;
|
||||
|
||||
case 0xc000: case 0xc001: case 0xc002: case 0xc003:
|
||||
|
@ -182,44 +242,11 @@ void VRC6::prg_write(unsigned addr, uint8 data) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8 VRC6::chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||
|
||||
unsigned bank = chr_bank[(addr >> 10) & 7];
|
||||
return Mapper::chr_read((bank << 10) | (addr & 0x03ff));
|
||||
}
|
||||
|
||||
void VRC6::chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||
|
||||
unsigned bank = chr_bank[(addr >> 10) & 7];
|
||||
Mapper::chr_write((bank << 10) | (addr & 0x03ff), data);
|
||||
}
|
||||
|
||||
unsigned VRC6::ciram_addr(unsigned addr) const {
|
||||
switch(mirror_select) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned VRC6::ram_size() {
|
||||
return 8192u;
|
||||
}
|
||||
|
||||
uint8* VRC6::ram_data() {
|
||||
return prg_ram;
|
||||
}
|
||||
|
||||
void VRC6::power() {
|
||||
void power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void VRC6::reset() {
|
||||
for(auto &n : prg_ram) n = 0xff;
|
||||
|
||||
void reset() {
|
||||
prg_bank[0] = 0;
|
||||
prg_bank[1] = 0;
|
||||
chr_bank[0] = 0;
|
||||
|
@ -230,7 +257,7 @@ void VRC6::reset() {
|
|||
chr_bank[5] = 0;
|
||||
chr_bank[6] = 0;
|
||||
chr_bank[7] = 0;
|
||||
mirror_select = 0;
|
||||
mirror = 0;
|
||||
irq_latch = 0;
|
||||
irq_mode = 0;
|
||||
irq_enable = 0;
|
||||
|
@ -271,12 +298,14 @@ void VRC6::reset() {
|
|||
sawtooth.output = 0;
|
||||
}
|
||||
|
||||
void VRC6::serialize(serializer &s) {
|
||||
s.array(prg_ram);
|
||||
void serialize(serializer &s) {
|
||||
pulse1.serialize(s);
|
||||
pulse2.serialize(s);
|
||||
sawtooth.serialize(s);
|
||||
|
||||
s.array(prg_bank);
|
||||
s.array(chr_bank);
|
||||
s.integer(mirror_select);
|
||||
s.integer(mirror);
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_mode);
|
||||
s.integer(irq_enable);
|
||||
|
@ -285,32 +314,9 @@ void VRC6::serialize(serializer &s) {
|
|||
s.integer(irq_counter);
|
||||
s.integer(irq_scalar);
|
||||
s.integer(irq_line);
|
||||
|
||||
pulse1.serialize(s);
|
||||
pulse2.serialize(s);
|
||||
sawtooth.serialize(s);
|
||||
}
|
||||
|
||||
void VRC6::Pulse::serialize(serializer &s) {
|
||||
s.integer(mode);
|
||||
s.integer(duty);
|
||||
s.integer(volume);
|
||||
s.integer(enable);
|
||||
s.integer(frequency);
|
||||
|
||||
s.integer(divider);
|
||||
s.integer(cycle);
|
||||
s.integer(output);
|
||||
VRC6(Board &board) : Chip(board) {
|
||||
}
|
||||
|
||||
void VRC6::Sawtooth::serialize(serializer &s) {
|
||||
s.integer(rate);
|
||||
s.integer(enable);
|
||||
s.integer(frequency);
|
||||
|
||||
s.integer(divider);
|
||||
s.integer(phase);
|
||||
s.integer(stage);
|
||||
s.integer(accumulator);
|
||||
s.integer(output);
|
||||
}
|
||||
};
|
|
@ -24,23 +24,47 @@ static string iNES(const uint8_t *data, unsigned size) {
|
|||
output.append(" mirror=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 1:
|
||||
output.append(" board type=NES-SXROM\n");
|
||||
output.append(" chip type=MMC1B2\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 2:
|
||||
output.append(" board type=NES-UOROM\n");
|
||||
output.append(" mirror=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 3:
|
||||
output.append(" board type=NES-CNROM\n");
|
||||
output.append(" mirror=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 4:
|
||||
output.append(" board type=NES-TLROM\n");
|
||||
output.append(" chip type=MMC3B\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
output.append(" board type=NES-AOROM\n");
|
||||
break;
|
||||
|
||||
case 16:
|
||||
output.append(" board type=BANDAI-FCG\n");
|
||||
output.append(" chip type=LZ93D50 manufacturer=Sharp\n");
|
||||
break;
|
||||
|
||||
case 24:
|
||||
output.append(" board type=KONAMI-VRC-6\n");
|
||||
output.append(" chip type=VRC6\n");
|
||||
break;
|
||||
|
||||
case 26:
|
||||
output.append(" board type=KONAMI-VRC-6\n");
|
||||
output.append(" chip type=VRC6\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
}
|
||||
|
||||
output.append(" prg rom=", prgrom, " ram=", prgram, "\n");
|
||||
|
@ -50,17 +74,3 @@ static string iNES(const uint8_t *data, unsigned size) {
|
|||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
switch(mapperNumber) {
|
||||
//default : mapper = &Mapper::none; break;
|
||||
//case 1: mapper = &Mapper::mmc1; break;
|
||||
//case 2: mapper = &Mapper::uorom; break;
|
||||
//case 3: mapper = &Mapper::cnrom; break;
|
||||
case 4: mapper = &Mapper::mmc3; break;
|
||||
//case 7: mapper = &Mapper::aorom; break;
|
||||
case 16: mapper = &Mapper::bandaiFCG; break;
|
||||
case 24: mapper = &Mapper::vrc6; Mapper::vrc6.abus_swap = 0; break;
|
||||
case 26: mapper = &Mapper::vrc6; Mapper::vrc6.abus_swap = 1; break;
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
AOROM aorom;
|
||||
|
||||
uint8 AOROM::prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) {
|
||||
addr = (prg_bank << 15) | (addr & 0x7fff);
|
||||
return Mapper::prg_read(addr);
|
||||
}
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void AOROM::prg_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x8000) {
|
||||
prg_bank = data & 0x0f;
|
||||
mirror_select = data & 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 AOROM::chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff));
|
||||
return Mapper::chr_read(addr);
|
||||
}
|
||||
|
||||
void AOROM::chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data);
|
||||
return Mapper::chr_write(addr, data);
|
||||
}
|
||||
|
||||
void AOROM::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void AOROM::reset() {
|
||||
prg_bank = 0x0f;
|
||||
mirror_select = 0;
|
||||
}
|
||||
|
||||
void AOROM::serialize(serializer &s) {
|
||||
s.integer(prg_bank);
|
||||
s.integer(mirror_select);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
struct AOROM : Mapper {
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint4 prg_bank;
|
||||
bool mirror_select;
|
||||
};
|
||||
|
||||
extern AOROM aorom;
|
|
@ -1,109 +0,0 @@
|
|||
BandaiFCG bandaiFCG;
|
||||
|
||||
void BandaiFCG::main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(1);
|
||||
irq_counter_enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
uint8 BandaiFCG::prg_read(unsigned addr) {
|
||||
if((addr & 0xc000) == 0x8000) {
|
||||
addr = (prg_bank << 14) | (addr & 0x3fff);
|
||||
return Mapper::prg_read(addr);
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0xc000) {
|
||||
addr = (0x0f << 14) | (addr & 0x3fff);
|
||||
return Mapper::prg_read(addr);
|
||||
}
|
||||
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void BandaiFCG::prg_write(unsigned addr, uint8 data) {
|
||||
if(addr >= 0x6000) {
|
||||
addr &= 0x0f;
|
||||
switch(addr) {
|
||||
case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
|
||||
chr_bank[addr] = data;
|
||||
break;
|
||||
case 0x8:
|
||||
prg_bank = data & 0x0f;
|
||||
break;
|
||||
case 0x9:
|
||||
mirror_select = data & 0x03;
|
||||
break;
|
||||
case 0xa:
|
||||
cpu.set_irq_line(0);
|
||||
irq_counter_enable = data & 0x01;
|
||||
irq_counter = irq_latch;
|
||||
break;
|
||||
case 0xb:
|
||||
irq_latch = (irq_latch & 0xff00) | (data << 0);
|
||||
break;
|
||||
case 0xc:
|
||||
irq_latch = (irq_latch & 0x00ff) | (data << 8);
|
||||
break;
|
||||
case 0xd:
|
||||
//TODO: serial EEPROM support
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 BandaiFCG::chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||
|
||||
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
||||
return Mapper::chr_read(addr);
|
||||
}
|
||||
|
||||
void BandaiFCG::chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||
|
||||
addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff);
|
||||
Mapper::chr_write(addr, data);
|
||||
}
|
||||
|
||||
void BandaiFCG::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void BandaiFCG::reset() {
|
||||
for(unsigned n = 0; n < 8; n++) chr_bank[n] = 0x00;
|
||||
prg_bank = 0x00;
|
||||
mirror_select = 0;
|
||||
irq_counter_enable = false;
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
}
|
||||
|
||||
unsigned BandaiFCG::ciram_addr(unsigned addr) const {
|
||||
switch(mirror_select & 0x03) {
|
||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
void BandaiFCG::serialize(serializer &s) {
|
||||
s.array(chr_bank);
|
||||
s.integer(prg_bank);
|
||||
s.integer(mirror_select);
|
||||
s.integer(irq_counter_enable);
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_latch);
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
struct BandaiFCG : Mapper {
|
||||
void main();
|
||||
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
unsigned ciram_addr(unsigned addr) const;
|
||||
|
||||
uint8 chr_bank[8];
|
||||
uint8 prg_bank;
|
||||
uint8 mirror_select;
|
||||
bool irq_counter_enable;
|
||||
uint16 irq_counter;
|
||||
uint16 irq_latch;
|
||||
};
|
||||
|
||||
extern BandaiFCG bandaiFCG;
|
|
@ -1,42 +0,0 @@
|
|||
CNROM cnrom;
|
||||
|
||||
uint8 CNROM::prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) return Mapper::prg_read(addr & 0x7fff);
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void CNROM::prg_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x8000) chr_bank = data & 0x03;
|
||||
}
|
||||
|
||||
uint8 CNROM::chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) {
|
||||
if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_read(addr & 0x07ff);
|
||||
}
|
||||
|
||||
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
||||
return Mapper::chr_read(addr);
|
||||
}
|
||||
|
||||
void CNROM::chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) {
|
||||
if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_write(addr & 0x07ff, data);
|
||||
}
|
||||
|
||||
addr = (chr_bank * 0x2000) + (addr & 0x1fff);
|
||||
Mapper::chr_write(addr, data);
|
||||
}
|
||||
|
||||
void CNROM::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void CNROM::reset() {
|
||||
chr_bank = 0;
|
||||
}
|
||||
|
||||
void CNROM::serialize(serializer &s) {
|
||||
s.integer(chr_bank);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
struct CNROM : Mapper {
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint2 chr_bank;
|
||||
};
|
||||
|
||||
extern CNROM cnrom;
|
|
@ -1,75 +0,0 @@
|
|||
#include <nes/nes.hpp>
|
||||
|
||||
namespace NES {
|
||||
namespace Mapper {
|
||||
|
||||
void Mapper::main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
cartridge.clock += 12 * 4095;
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void Mapper::tick() {
|
||||
cartridge.clock += 12;
|
||||
if(cartridge.clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
unsigned Mapper::mirror(unsigned addr, unsigned size) const {
|
||||
unsigned base = 0;
|
||||
if(size) {
|
||||
unsigned mask = 1 << 23;
|
||||
while(addr >= size) {
|
||||
while(!(addr & mask)) mask >>= 1;
|
||||
addr -= mask;
|
||||
if(size > mask) {
|
||||
size -= mask;
|
||||
base += mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
base += addr;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
uint8 Mapper::prg_read(unsigned addr) {
|
||||
return cartridge.prg_data[mirror(addr, cartridge.prg_size)];
|
||||
}
|
||||
|
||||
void Mapper::prg_write(unsigned addr, uint8 data) {
|
||||
cartridge.prg_data[mirror(addr, cartridge.prg_size)] = data;
|
||||
}
|
||||
|
||||
uint8 Mapper::chr_read(unsigned addr) {
|
||||
return cartridge.chr_data[mirror(addr, cartridge.chr_size)];
|
||||
}
|
||||
|
||||
void Mapper::chr_write(unsigned addr, uint8 data) {
|
||||
if(cartridge.chr_ram == false) return;
|
||||
cartridge.chr_data[mirror(addr, cartridge.chr_size)] = data;
|
||||
}
|
||||
|
||||
unsigned Mapper::ram_size() {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint8* Mapper::ram_data() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "none/none.cpp"
|
||||
#include "aorom/aorom.cpp"
|
||||
#include "bandai-fcg/bandai-fcg.cpp"
|
||||
#include "cnrom/cnrom.cpp"
|
||||
#include "mmc1/mmc1.cpp"
|
||||
#include "mmc3/mmc3.cpp"
|
||||
#include "uorom/uorom.cpp"
|
||||
#include "vrc6/vrc6.cpp"
|
||||
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
namespace Mapper {
|
||||
struct Mapper {
|
||||
virtual void main();
|
||||
virtual void tick();
|
||||
|
||||
unsigned mirror(unsigned addr, unsigned size) const;
|
||||
|
||||
//note: Mapper::{prg,chr}_read() functions take literal ROM addresses; mirroring appropriately
|
||||
//subclasses of Mapper take 16-bit bus addresses; decode them; and call Mapper read functions
|
||||
|
||||
virtual uint8 prg_read(unsigned addr);
|
||||
virtual void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
virtual uint8 chr_read(unsigned addr);
|
||||
virtual void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
virtual uint8 ciram_read(uint13 addr) {}
|
||||
virtual void ciram_write(uint13 addr, uint8 data) {}
|
||||
|
||||
virtual unsigned ram_size();
|
||||
virtual uint8* ram_data();
|
||||
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual void serialize(serializer&) = 0;
|
||||
};
|
||||
|
||||
#include "none/none.hpp"
|
||||
#include "aorom/aorom.hpp"
|
||||
#include "bandai-fcg/bandai-fcg.hpp"
|
||||
#include "cnrom/cnrom.hpp"
|
||||
#include "mmc1/mmc1.hpp"
|
||||
#include "mmc3/mmc3.hpp"
|
||||
#include "uorom/uorom.hpp"
|
||||
#include "vrc6/vrc6.hpp"
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
MMC1 mmc1;
|
||||
|
||||
unsigned MMC1::ciram_addr(unsigned addr) const {
|
||||
switch(r[0] & 0x03) {
|
||||
case 0: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||
case 1: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||
case 2: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||
case 3: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||
}
|
||||
}
|
||||
|
||||
//static block: 0 = 8000-ffff, 1 = c000-ffff
|
||||
bool MMC1::prg_mode() const {
|
||||
return r[0] & 0x04;
|
||||
}
|
||||
|
||||
//block size: 0 = 32K, 1 = 16K
|
||||
bool MMC1::prg_size() const {
|
||||
return r[0] & 0x08;
|
||||
}
|
||||
|
||||
unsigned MMC1::prg_addr(bool region) const {
|
||||
unsigned addr, bank;
|
||||
|
||||
//region(0) = $8000-bfff; region(1) = $c000-ffff
|
||||
if(prg_size()) { //16K mode
|
||||
bank = (region == 0 ? 0x0 : 0xf); //8000-bfff defaults to first bank; c000-ffff defaults to last bank
|
||||
if(region != prg_mode()) { //if this is the active dynamic region ...
|
||||
bank = prg_bank();
|
||||
}
|
||||
} else { //32K mode
|
||||
bank = (prg_bank() & ~1) + region;
|
||||
}
|
||||
addr = bank << 14; //<<14 = 16K
|
||||
|
||||
//256K page selection (for 512K PRG ROMs)
|
||||
if(prg_ex_select == 0) {
|
||||
addr |= (chr_banklo() >> 4) << 18; //<<18 = 256K
|
||||
} else {
|
||||
addr |= (chr_bankhi() >> 4) << 18;
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
//0 = 8K, 1 = 4K
|
||||
bool MMC1::chr_mode() const {
|
||||
return r[0] & 0x10;
|
||||
}
|
||||
|
||||
unsigned MMC1::chr_banklo() const {
|
||||
if(chr_mode() == 0) return (r[1] & ~1) | 0;
|
||||
return r[1];
|
||||
}
|
||||
|
||||
unsigned MMC1::chr_bankhi() const {
|
||||
if(chr_mode() == 0) return (r[1] & ~1) | 1;
|
||||
return r[2];
|
||||
}
|
||||
|
||||
unsigned MMC1::prg_bank() const {
|
||||
return r[3] & 0x0f;
|
||||
}
|
||||
|
||||
bool MMC1::prg_ram_disable() const {
|
||||
return r[3] & 0x10;
|
||||
}
|
||||
|
||||
uint8 MMC1::prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
if(prg_ram_disable() == false) return prg_ram[addr & 0x1fff];
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
if(addr & 0x8000) {
|
||||
addr = prg_addr(addr & 0x4000) | (addr & 0x3fff);
|
||||
return Mapper::prg_read(addr);
|
||||
}
|
||||
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void MMC1::prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
if(prg_ram_disable() == false) prg_ram[addr & 0x1fff] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr & 0x8000) {
|
||||
if(data & 0x80) {
|
||||
shiftaddr = 0;
|
||||
r[0] |= 0x0c;
|
||||
} else {
|
||||
shiftdata >>= 1;
|
||||
shiftdata |= (data & 1) << 4;
|
||||
if(++shiftaddr == 5) {
|
||||
shiftaddr = 0;
|
||||
r[(addr >> 13) & 3] = shiftdata;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 MMC1::chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) {
|
||||
return ppu.ciram_read(ciram_addr(addr));
|
||||
}
|
||||
|
||||
prg_ex_select = addr & 0x1000;
|
||||
|
||||
if(addr <= 0x0fff) {
|
||||
addr = chr_banklo() * 0x1000 + (addr & 0x0fff);
|
||||
return Mapper::chr_read(addr);
|
||||
}
|
||||
|
||||
if(addr <= 0x1fff) {
|
||||
addr = chr_bankhi() * 0x1000 + (addr & 0x0fff);
|
||||
return Mapper::chr_read(addr);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void MMC1::chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) {
|
||||
return ppu.ciram_write(ciram_addr(addr), data);
|
||||
}
|
||||
|
||||
prg_ex_select = addr & 0x1000;
|
||||
|
||||
if(addr <= 0x0fff) {
|
||||
if(cartridge.chr_ram == false) return;
|
||||
addr = chr_banklo() * 0x1000 + (addr & 0x0fff);
|
||||
return Mapper::chr_write(addr, data);
|
||||
}
|
||||
|
||||
if(addr <= 0x1fff) {
|
||||
if(cartridge.chr_ram == false) return;
|
||||
addr = chr_bankhi() * 0x1000 + (addr & 0x0fff);
|
||||
return Mapper::chr_write(addr, data);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
unsigned MMC1::ram_size() {
|
||||
return 8192u;
|
||||
}
|
||||
|
||||
uint8* MMC1::ram_data() {
|
||||
return prg_ram;
|
||||
}
|
||||
|
||||
void MMC1::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void MMC1::reset() {
|
||||
prg_ex_select = 0;
|
||||
shiftaddr = 0;
|
||||
shiftdata = 0;
|
||||
|
||||
r[0] = 0x0c;
|
||||
r[1] = 0x00;
|
||||
r[2] = 0x01;
|
||||
r[3] = 0x00;
|
||||
}
|
||||
|
||||
void MMC1::serialize(serializer &s) {
|
||||
s.array(prg_ram);
|
||||
s.array(r);
|
||||
s.integer(prg_ex_select);
|
||||
s.integer(shiftaddr);
|
||||
s.integer(shiftdata);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
struct MMC1 : Mapper {
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
unsigned ram_size();
|
||||
uint8* ram_data();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint8 prg_ram[8192];
|
||||
uint8 r[4];
|
||||
bool prg_ex_select;
|
||||
unsigned shiftaddr;
|
||||
unsigned shiftdata;
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const;
|
||||
bool prg_mode() const;
|
||||
bool prg_size() const;
|
||||
unsigned prg_addr(bool region) const;
|
||||
bool chr_mode() const;
|
||||
unsigned chr_banklo() const;
|
||||
unsigned chr_bankhi() const;
|
||||
unsigned prg_bank() const;
|
||||
bool prg_ram_disable() const;
|
||||
};
|
||||
|
||||
extern MMC1 mmc1;
|
|
@ -1,213 +0,0 @@
|
|||
MMC3 mmc3;
|
||||
|
||||
void MMC3::main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_delay) irq_delay--;
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
void MMC3::irq_test(uint16 addr) {
|
||||
if(!(chr_abus & 0x1000) && (addr & 0x1000)) {
|
||||
if(irq_delay == 0) {
|
||||
if(irq_counter == 0) {
|
||||
irq_counter = irq_latch;
|
||||
} else if(--irq_counter == 0) {
|
||||
if(irq_enable) irq_line = 1;
|
||||
}
|
||||
}
|
||||
irq_delay = 6;
|
||||
}
|
||||
chr_abus = addr;
|
||||
}
|
||||
|
||||
unsigned MMC3::prg_addr(uint16 addr) {
|
||||
if((addr & 0xe000) == 0x8000) {
|
||||
if((bank_select & 0x40) != 0) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xa000) {
|
||||
return (prg_bank[1] << 13) | (addr & 0x1fff);
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xc000) {
|
||||
if((bank_select & 0x40) == 0) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xe000) {
|
||||
return (0x3f << 13) | (addr & 0x1fff);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
uint8 MMC3::prg_read(unsigned addr) {
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
if(prg_ram_enable) {
|
||||
return prg_ram[addr & 0x1fff];
|
||||
}
|
||||
}
|
||||
|
||||
if(addr & 0x8000) { //$8000-ffff
|
||||
return Mapper::prg_read(prg_addr(addr));
|
||||
}
|
||||
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void MMC3::prg_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
if(prg_ram_enable && prg_ram_write_protect == false) {
|
||||
prg_ram[addr & 0x1fff] = data;
|
||||
}
|
||||
}
|
||||
|
||||
switch(addr & 0xe001) {
|
||||
case 0x8000:
|
||||
bank_select = data & 0xc7;
|
||||
break;
|
||||
|
||||
case 0x8001:
|
||||
switch(bank_select & 7) {
|
||||
case 0: chr_bank[0] = data & ~1; break;
|
||||
case 1: chr_bank[1] = data & ~1; break;
|
||||
case 2: chr_bank[2] = data; break;
|
||||
case 3: chr_bank[3] = data; break;
|
||||
case 4: chr_bank[4] = data; break;
|
||||
case 5: chr_bank[5] = data; break;
|
||||
case 6: prg_bank[0] = data & 0x3f; break;
|
||||
case 7: prg_bank[1] = data & 0x3f; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xa000:
|
||||
mirror_select = data & 0x01;
|
||||
break;
|
||||
|
||||
case 0xa001:
|
||||
prg_ram_enable = data & 0x80;
|
||||
prg_ram_write_protect = data & 0x40;
|
||||
break;
|
||||
|
||||
case 0xc000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
|
||||
case 0xc001:
|
||||
irq_counter = 0;
|
||||
break;
|
||||
|
||||
case 0xe000:
|
||||
irq_enable = false;
|
||||
irq_line = 0;
|
||||
cpu.set_irq_line(irq_line);
|
||||
break;
|
||||
|
||||
case 0xe001:
|
||||
irq_enable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned MMC3::chr_addr(uint16 addr) {
|
||||
if((bank_select & 0x80) == 0) {
|
||||
if(addr <= 0x07ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x0fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x13ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x17ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x1bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x1fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
|
||||
}
|
||||
|
||||
if((bank_select & 0x80) != 0) {
|
||||
if(addr <= 0x03ff) return (chr_bank[2] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x07ff) return (chr_bank[3] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x0bff) return (chr_bank[4] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x0fff) return (chr_bank[5] << 10) | (addr & 0x03ff);
|
||||
if(addr <= 0x17ff) return (chr_bank[0] << 10) | (addr & 0x07ff);
|
||||
if(addr <= 0x1fff) return (chr_bank[1] << 10) | (addr & 0x07ff);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
uint8 MMC3::chr_read(unsigned addr) {
|
||||
irq_test(addr);
|
||||
if(addr & 0x2000) return ppu.ciram_read(ciram_addr(addr));
|
||||
return Mapper::chr_read(chr_addr(addr));
|
||||
}
|
||||
|
||||
void MMC3::chr_write(unsigned addr, uint8 data) {
|
||||
irq_test(addr);
|
||||
if(addr & 0x2000) return ppu.ciram_write(ciram_addr(addr), data);
|
||||
return Mapper::chr_write(chr_addr(addr), data);
|
||||
}
|
||||
|
||||
unsigned MMC3::ciram_addr(uint13 addr) {
|
||||
if(mirror_select == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
if(mirror_select == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
throw;
|
||||
}
|
||||
|
||||
unsigned MMC3::ram_size() {
|
||||
return 8192u;
|
||||
}
|
||||
|
||||
uint8* MMC3::ram_data() {
|
||||
return prg_ram;
|
||||
}
|
||||
|
||||
void MMC3::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void MMC3::reset() {
|
||||
bank_select = 0;
|
||||
|
||||
prg_bank[0] = 0;
|
||||
prg_bank[1] = 0;
|
||||
|
||||
chr_bank[0] = 0;
|
||||
chr_bank[1] = 0;
|
||||
chr_bank[2] = 0;
|
||||
chr_bank[3] = 0;
|
||||
chr_bank[4] = 0;
|
||||
chr_bank[5] = 0;
|
||||
|
||||
mirror_select = 0;
|
||||
prg_ram_enable = 1;
|
||||
prg_ram_write_protect = 0;
|
||||
|
||||
irq_latch = 0x00;
|
||||
irq_counter = 0x00;
|
||||
irq_enable = false;
|
||||
irq_delay = 0;
|
||||
irq_line = 0;
|
||||
|
||||
chr_abus = 0;
|
||||
}
|
||||
|
||||
void MMC3::serialize(serializer &s) {
|
||||
s.array(prg_ram);
|
||||
|
||||
s.integer(bank_select);
|
||||
s.array(prg_bank);
|
||||
s.array(chr_bank);
|
||||
s.integer(mirror_select);
|
||||
s.integer(prg_ram_enable);
|
||||
s.integer(prg_ram_write_protect);
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_delay);
|
||||
s.integer(irq_line);
|
||||
|
||||
s.integer(chr_abus);
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
struct MMC3 : Mapper {
|
||||
void main();
|
||||
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
unsigned ram_size();
|
||||
uint8* ram_data();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint8 prg_ram[8192];
|
||||
|
||||
uint8 bank_select;
|
||||
uint8 prg_bank[2];
|
||||
uint8 chr_bank[6];
|
||||
bool mirror_select;
|
||||
bool prg_ram_enable;
|
||||
bool prg_ram_write_protect;
|
||||
uint8 irq_latch;
|
||||
uint8 irq_counter;
|
||||
bool irq_enable;
|
||||
unsigned irq_delay;
|
||||
bool irq_line;
|
||||
|
||||
uint16 chr_abus;
|
||||
|
||||
void irq_test(uint16 addr);
|
||||
unsigned prg_addr(uint16 addr);
|
||||
unsigned chr_addr(uint16 addr);
|
||||
unsigned ciram_addr(uint13 addr);
|
||||
};
|
||||
|
||||
extern MMC3 mmc3;
|
|
@ -1,34 +0,0 @@
|
|||
None none;
|
||||
|
||||
uint8 None::prg_read(unsigned addr) {
|
||||
if(addr & 0x8000) return Mapper::prg_read(addr);
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void None::prg_write(unsigned addr, uint8 data) {
|
||||
}
|
||||
|
||||
uint8 None::chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) {
|
||||
if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_read(addr & 0x07ff);
|
||||
}
|
||||
return Mapper::chr_read(addr);
|
||||
}
|
||||
|
||||
void None::chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) {
|
||||
if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_write(addr & 0x07ff, data);
|
||||
}
|
||||
return Mapper::chr_write(addr, data);
|
||||
}
|
||||
|
||||
void None::power() {
|
||||
}
|
||||
|
||||
void None::reset() {
|
||||
}
|
||||
|
||||
void None::serialize(serializer &s) {
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
struct None : Mapper {
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
};
|
||||
|
||||
extern None none;
|
|
@ -1,45 +0,0 @@
|
|||
UOROM uorom;
|
||||
|
||||
uint8 UOROM::prg_read(unsigned addr) {
|
||||
if((addr & 0xc000) == 0x8000) {
|
||||
return Mapper::prg_read((prg_bank << 14) | (addr & 0x3fff));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0xc000) {
|
||||
return Mapper::prg_read((0x0f << 14) | (addr & 0x3fff));
|
||||
}
|
||||
}
|
||||
|
||||
void UOROM::prg_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x8000) prg_bank = data & 0x0f;
|
||||
}
|
||||
|
||||
uint8 UOROM::chr_read(unsigned addr) {
|
||||
if(addr & 0x2000) {
|
||||
if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_read(addr);
|
||||
}
|
||||
|
||||
return Mapper::chr_read(addr);
|
||||
}
|
||||
|
||||
void UOROM::chr_write(unsigned addr, uint8 data) {
|
||||
if(addr & 0x2000) {
|
||||
if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_write(addr, data);
|
||||
}
|
||||
|
||||
return Mapper::chr_write(addr, data);
|
||||
}
|
||||
|
||||
void UOROM::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void UOROM::reset() {
|
||||
prg_bank = 0;
|
||||
}
|
||||
|
||||
void UOROM::serialize(serializer &s) {
|
||||
s.integer(prg_bank);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
struct UOROM : Mapper {
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint4 prg_bank;
|
||||
};
|
||||
|
||||
extern UOROM uorom;
|
|
@ -1,69 +0,0 @@
|
|||
struct VRC6 : Mapper {
|
||||
void main();
|
||||
|
||||
uint8 prg_read(unsigned addr);
|
||||
void prg_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 chr_read(unsigned addr);
|
||||
void chr_write(unsigned addr, uint8 data);
|
||||
|
||||
unsigned ram_size();
|
||||
uint8* ram_data();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
//privileged:
|
||||
bool abus_swap;
|
||||
|
||||
private:
|
||||
uint8 prg_ram[8192];
|
||||
|
||||
uint8 prg_bank[2];
|
||||
uint8 chr_bank[8];
|
||||
uint2 mirror_select;
|
||||
uint8 irq_latch;
|
||||
bool irq_mode;
|
||||
bool irq_enable;
|
||||
bool irq_acknowledge;
|
||||
|
||||
uint8 irq_counter;
|
||||
signed irq_scalar;
|
||||
bool irq_line;
|
||||
|
||||
struct Pulse {
|
||||
bool mode;
|
||||
uint3 duty;
|
||||
uint4 volume;
|
||||
bool enable;
|
||||
uint12 frequency;
|
||||
|
||||
uint12 divider;
|
||||
uint4 cycle;
|
||||
uint4 output;
|
||||
|
||||
void clock();
|
||||
void serialize(serializer&);
|
||||
} pulse1, pulse2;
|
||||
|
||||
struct Sawtooth {
|
||||
uint6 rate;
|
||||
bool enable;
|
||||
uint12 frequency;
|
||||
|
||||
uint12 divider;
|
||||
uint1 phase;
|
||||
uint3 stage;
|
||||
uint8 accumulator;
|
||||
uint5 output;
|
||||
|
||||
void clock();
|
||||
void serialize(serializer&);
|
||||
} sawtooth;
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const;
|
||||
};
|
||||
|
||||
extern VRC6 vrc6;
|
|
@ -326,7 +326,7 @@ void MainWindow::synchronize() {
|
|||
|
||||
void MainWindow::setupVideoFilters() {
|
||||
lstring files = directory::files({ application->basepath, "filters/" }, "*.filter");
|
||||
if(files.size() == 0) directory::files({ application->userpath, "filters/" }, "*.filter");
|
||||
if(files.size() == 0) files = directory::files({ application->userpath, "filters/" }, "*.filter");
|
||||
reference_array<RadioItem&> group;
|
||||
|
||||
settingsVideoFilterList = new RadioItem[files.size()];
|
||||
|
|
|
@ -44,7 +44,7 @@ void Interface::setController(unsigned port, unsigned device) {
|
|||
}
|
||||
|
||||
void Interface::updateDSP() {
|
||||
dspaudio.setVolume((double)config->audio.volume / 100.0);
|
||||
dspaudio.setVolume(config->audio.mute == false ? (double)config->audio.volume / 100.0 : 0.0);
|
||||
switch(mode()) {
|
||||
case Mode::NES: return dspaudio.setFrequency(config->audio.frequencyNES);
|
||||
case Mode::SNES: return dspaudio.setFrequency(config->audio.frequencySNES);
|
||||
|
|
|
@ -49,7 +49,7 @@ Application::Application(int argc, char **argv) {
|
|||
inputManager = new InputManager;
|
||||
utility = new Utility;
|
||||
|
||||
title = "bsnes v082.26";
|
||||
title = "bsnes v082.27";
|
||||
|
||||
string fontFamily = Intrinsics::platform() == Intrinsics::Platform::Windows ? "Tahoma, " : "Sans, ";
|
||||
normalFont = { fontFamily, "8" };
|
||||
|
|
|
@ -161,11 +161,11 @@ bool CheatEditor::save(const string &filename) {
|
|||
file fp;
|
||||
if(fp.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
fp.print("cartridge sha256=", interface->sha256(), "\n");
|
||||
fp.print("cartridge sha256=`", interface->sha256(), "`\n");
|
||||
for(unsigned n = 0; n <= lastSave; n++) {
|
||||
fp.print(" cheat", cheatList.checked(n) ? " enable" : "", "\n");
|
||||
fp.print(" description=|", cheatText[n][Desc], "|\n");
|
||||
fp.print(" code=|", cheatText[n][Code], "|\n");
|
||||
fp.print(" description=`", string{cheatText[n][Desc]}.transform("`", "'"), "`\n");
|
||||
fp.print(" code=`", string{cheatText[n][Code]}.transform("`", "'"), "`\n");
|
||||
}
|
||||
|
||||
fp.close();
|
||||
|
|
Loading…
Reference in New Issue