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:
Tim Allen 2011-10-01 22:06:48 +10:00
parent 7115047d85
commit ba2e6b5789
39 changed files with 843 additions and 1167 deletions

View File

@ -40,6 +40,8 @@ void reset() {
}
void serialize(serializer &s) {
Board::serialize(s);
s.integer(prg_bank);
s.integer(mirror_select);
}

View File

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

View File

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

View File

@ -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;

View File

@ -44,6 +44,8 @@ void reset() {
}
void serialize(serializer &s) {
Board::serialize(s);
s.integer(chr_bank);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -44,6 +44,8 @@ void reset() {
}
void serialize(serializer &s) {
Board::serialize(s);
s.integer(prg_bank);
}

View File

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

View File

@ -9,7 +9,7 @@ struct Cartridge : Processor, property<Cartridge> {
void unload();
unsigned ram_size();
uint8 *ram_data();
uint8* ram_data();
void power();
void reset();

View File

@ -0,0 +1,10 @@
#include "mmc1.cpp"
#include "mmc3.cpp"
#include "vrc6.cpp"
void Chip::tick() {
board.tick();
}
Chip::Chip(Board &board) : board(board) {
}

View File

@ -1,2 +1,7 @@
struct Board;
struct Chip {
Board &board;
void tick();
Chip(Board &board);
};

View File

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

141
bsnes/nes/cartridge/chip/mmc3.cpp Executable file
View File

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

View File

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

View File

@ -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;
}
*/

View File

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

View File

@ -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;

View File

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

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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"
}
}

View File

@ -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"
}

View File

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

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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) {
}

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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()];

View File

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

View File

@ -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" };

View File

@ -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();