mirror of https://github.com/bsnes-emu/bsnes.git
Update to v082r22 release.
byuu says: Mappers are now optionally threaded. Fixed up MMC3 emulation, SMB3 and MM3-6 are all fully playable. However, many unusual variants of this chip are not supported still. Added UNROM+UOROM for Contra and MM1, allowing all six MM games to play now. Added VRC6 with sound emulation, because I wanted to get audio mixing in place. Chose VRC6 because it has Esper Dream 2, which is an absolutely amazing game that everyone should play :D The game didn't use sawtooth, and I didn't test any other VRC6 games, so hopefully that is emulated passably well enough.
This commit is contained in:
parent
82a17ac0f5
commit
046e478d86
|
@ -47,7 +47,7 @@ void APU::main() {
|
|||
clock_frame_counter_divider();
|
||||
|
||||
signed output = rectangle_dac[rectangle_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
|
||||
interface->audioSample(output);
|
||||
interface->audioSample(output + cartridge_sample);
|
||||
|
||||
tick();
|
||||
}
|
||||
|
@ -62,6 +62,10 @@ void APU::set_irq_line() {
|
|||
cpu.set_irq_apu_line(frame.irq_pending || dmc.irq_pending);
|
||||
}
|
||||
|
||||
void APU::set_sample(int16 sample) {
|
||||
cartridge_sample = sample;
|
||||
}
|
||||
|
||||
void APU::power() {
|
||||
for(unsigned n = 0; n < 2; n++) {
|
||||
rectangle[n].sweep.shift = 0;
|
||||
|
@ -144,6 +148,8 @@ void APU::reset() {
|
|||
frame.divider = 1;
|
||||
|
||||
enabled_channels = 0;
|
||||
cartridge_sample = 0;
|
||||
|
||||
set_irq_line();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ struct APU : Processor {
|
|||
void main();
|
||||
void tick();
|
||||
void set_irq_line();
|
||||
void set_sample(int16 sample);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
@ -139,6 +140,7 @@ struct APU : Processor {
|
|||
void clock_frame_counter_divider();
|
||||
|
||||
uint8 enabled_channels;
|
||||
int16 cartridge_sample;
|
||||
|
||||
int16 rectangle_dac[32];
|
||||
int16 dmc_triangle_noise_dac[128][16][16];
|
||||
|
|
|
@ -8,6 +8,7 @@ void APU::serialize(serializer &s) {
|
|||
frame.serialize(s);
|
||||
|
||||
s.integer(enabled_channels);
|
||||
s.integer(cartridge_sample);
|
||||
}
|
||||
|
||||
void APU::Envelope::serialize(serializer &s) {
|
||||
|
|
|
@ -4,6 +4,14 @@ namespace NES {
|
|||
|
||||
Cartridge cartridge;
|
||||
|
||||
void Cartridge::Main() {
|
||||
cartridge.main();
|
||||
}
|
||||
|
||||
void Cartridge::main() {
|
||||
mapper->main();
|
||||
}
|
||||
|
||||
void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
||||
rom_size = size - 16;
|
||||
rom_data = new uint8[rom_size];
|
||||
|
@ -31,10 +39,13 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
|
|||
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;
|
||||
}
|
||||
|
||||
system.load();
|
||||
|
@ -61,10 +72,12 @@ uint8* Cartridge::ram_data() {
|
|||
}
|
||||
|
||||
void Cartridge::power() {
|
||||
create(Cartridge::Main, 21477272);
|
||||
mapper->power();
|
||||
}
|
||||
|
||||
void Cartridge::reset() {
|
||||
create(Cartridge::Main, 21477272);
|
||||
mapper->reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
struct Cartridge : property<Cartridge> {
|
||||
struct Cartridge : Processor, property<Cartridge> {
|
||||
static void Main();
|
||||
void main();
|
||||
|
||||
void load(const string &xml, const uint8_t *data, unsigned size);
|
||||
void unload();
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@ void CPU::add_clocks(unsigned clocks) {
|
|||
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(ppu.thread);
|
||||
|
||||
cartridge.clock -= clocks;
|
||||
if(cartridge.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cartridge.thread);
|
||||
}
|
||||
|
||||
void CPU::power() {
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
BandaiFCG bandaiFCG;
|
||||
|
||||
uint8 BandaiFCG::prg_read(uint16 addr) {
|
||||
clock();
|
||||
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(uint16 addr) {
|
||||
if(addr >= 0x8000 && addr <= 0xbfff) {
|
||||
unsigned rom_addr = (prg_bank << 14) | (addr & 0x3fff);
|
||||
return cartridge.prg_data[mirror(rom_addr, cartridge.prg_size)];
|
||||
|
@ -17,8 +32,6 @@ uint8 BandaiFCG::prg_read(uint16 addr) {
|
|||
}
|
||||
|
||||
void BandaiFCG::prg_write(uint16 addr, uint8 data) {
|
||||
clock();
|
||||
|
||||
if(addr >= 0x6000) {
|
||||
addr &= 0x0f;
|
||||
switch(addr) {
|
||||
|
@ -95,15 +108,6 @@ unsigned BandaiFCG::ciram_addr(unsigned addr) const {
|
|||
throw;
|
||||
}
|
||||
|
||||
void BandaiFCG::clock() {
|
||||
if(irq_counter_enable) {
|
||||
if(--irq_counter == 0xffff) {
|
||||
cpu.set_irq_line(1);
|
||||
irq_counter_enable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void BandaiFCG::serialize(serializer &s) {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
struct BandaiFCG : Mapper {
|
||||
void main();
|
||||
|
||||
uint8 prg_read(uint16 addr);
|
||||
void prg_write(uint16 addr, uint8 data);
|
||||
|
||||
|
@ -15,7 +17,6 @@ struct BandaiFCG : Mapper {
|
|||
|
||||
private:
|
||||
unsigned ciram_addr(unsigned addr) const;
|
||||
void clock();
|
||||
|
||||
uint8 chr_bank[8];
|
||||
uint8 prg_bank;
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
#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) {
|
||||
|
@ -43,6 +59,8 @@ namespace Mapper {
|
|||
#include "cnrom/cnrom.cpp"
|
||||
#include "mmc1/mmc1.cpp"
|
||||
#include "mmc3/mmc3.cpp"
|
||||
}
|
||||
#include "uorom/uorom.cpp"
|
||||
#include "vrc6/vrc6.cpp"
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
namespace Mapper {
|
||||
struct Mapper {
|
||||
virtual void main();
|
||||
virtual void tick();
|
||||
|
||||
unsigned mirror(unsigned addr, unsigned size) const;
|
||||
uint8& prg_data(unsigned addr);
|
||||
uint8& chr_data(unsigned addr);
|
||||
|
@ -28,4 +31,6 @@ namespace Mapper {
|
|||
#include "cnrom/cnrom.hpp"
|
||||
#include "mmc1/mmc1.hpp"
|
||||
#include "mmc3/mmc3.hpp"
|
||||
#include "uorom/uorom.hpp"
|
||||
#include "vrc6/vrc6.hpp"
|
||||
}
|
||||
|
|
|
@ -1,24 +1,34 @@
|
|||
MMC3 mmc3;
|
||||
|
||||
void MMC3::clock_irq_test(uint16 addr) {
|
||||
if(!(last_chr_addr & 0x1000) && (addr & 0x1000)) {
|
||||
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;
|
||||
irq_line = irq_enable;
|
||||
cpu.set_irq_line(irq_line);
|
||||
} else if(--irq_counter == 0) {
|
||||
if(irq_enable) irq_line = 1;
|
||||
}
|
||||
irq_counter--;
|
||||
}
|
||||
irq_delay = 5;
|
||||
irq_delay = 6;
|
||||
}
|
||||
|
||||
last_chr_addr = addr;
|
||||
chr_abus = addr;
|
||||
}
|
||||
|
||||
unsigned MMC3::prg_addr(uint16 addr) {
|
||||
if((addr & 0xe000) == 0x8000) {
|
||||
if((bank_select & 0x40) == 1) return (0x3e << 13) | (addr & 0x1fff);
|
||||
if((bank_select & 0x40) != 0) return (0x3e << 13) | (addr & 0x1fff);
|
||||
return (prg_bank[0] << 13) | (addr & 0x1fff);
|
||||
}
|
||||
|
||||
|
@ -39,8 +49,6 @@ unsigned MMC3::prg_addr(uint16 addr) {
|
|||
}
|
||||
|
||||
uint8 MMC3::prg_read(uint16 addr) {
|
||||
if(irq_delay) irq_delay--;
|
||||
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
if(prg_ram_enable) {
|
||||
return prg_ram[addr & 0x1fff];
|
||||
|
@ -55,8 +63,6 @@ uint8 MMC3::prg_read(uint16 addr) {
|
|||
}
|
||||
|
||||
void MMC3::prg_write(uint16 addr, uint8 data) {
|
||||
if(irq_delay) irq_delay--;
|
||||
|
||||
if((addr & 0xe000) == 0x6000) { //$6000-7fff
|
||||
if(prg_ram_enable && prg_ram_write_protect == false) {
|
||||
prg_ram[addr & 0x1fff] = data;
|
||||
|
@ -133,26 +139,25 @@ unsigned MMC3::chr_addr(uint16 addr) {
|
|||
}
|
||||
|
||||
uint8 MMC3::chr_read(uint16 addr) {
|
||||
clock_irq_test(addr);
|
||||
irq_test(addr);
|
||||
return chr_data(chr_addr(addr));
|
||||
}
|
||||
|
||||
void MMC3::chr_write(uint16 addr, uint8 data) {
|
||||
clock_irq_test(addr);
|
||||
last_chr_addr = addr;
|
||||
irq_test(addr);
|
||||
if(cartridge.chr_ram == false) return;
|
||||
chr_data(chr_addr(addr)) = data;
|
||||
}
|
||||
|
||||
unsigned MMC3::ciram_addr(uint13 addr) {
|
||||
clock_irq_test(0x2000 | addr);
|
||||
irq_test(0x2000 | addr);
|
||||
if(mirror_select == 0) return ((addr & 0x0400) >> 0) | (addr & 0x03ff);
|
||||
if(mirror_select == 1) return ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
throw;
|
||||
}
|
||||
|
||||
uint8 MMC3::ciram_read(uint13 addr) {
|
||||
clock_irq_test(0x2000 | addr);
|
||||
irq_test(0x2000 | addr);
|
||||
return ppu.ciram_read(ciram_addr(addr));
|
||||
}
|
||||
|
||||
|
@ -194,7 +199,24 @@ void MMC3::reset() {
|
|||
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,4 +1,6 @@
|
|||
struct MMC3 : Mapper {
|
||||
void main();
|
||||
|
||||
uint8 prg_read(uint16 addr);
|
||||
void prg_write(uint16 addr, uint8 data);
|
||||
|
||||
|
@ -31,12 +33,12 @@ private:
|
|||
unsigned irq_delay;
|
||||
bool irq_line;
|
||||
|
||||
uint16 last_chr_addr;
|
||||
uint16 chr_abus;
|
||||
|
||||
void irq_test(uint16 addr);
|
||||
unsigned prg_addr(uint16 addr);
|
||||
unsigned chr_addr(uint16 addr);
|
||||
unsigned ciram_addr(uint13 addr);
|
||||
void clock_irq_test(uint16 addr);
|
||||
};
|
||||
|
||||
extern MMC3 mmc3;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
UOROM uorom;
|
||||
|
||||
uint8 UOROM::prg_read(uint16 addr) {
|
||||
if((addr & 0xc000) == 0x8000) {
|
||||
return prg_data((prg_bank << 14) | (addr & 0x3fff));
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0xc000) {
|
||||
return prg_data((0x0f << 14) | (addr & 0x3fff));
|
||||
}
|
||||
}
|
||||
|
||||
void UOROM::prg_write(uint16 addr, uint8 data) {
|
||||
if(addr & 0x8000) prg_bank = data & 0x0f;
|
||||
}
|
||||
|
||||
uint8 UOROM::chr_read(uint16 addr) {
|
||||
return chr_data(addr);
|
||||
}
|
||||
|
||||
void UOROM::chr_write(uint16 addr, uint8 data) {
|
||||
if(cartridge.chr_ram == false) return;
|
||||
chr_data(addr) = data;
|
||||
}
|
||||
|
||||
uint8 UOROM::ciram_read(uint13 addr) {
|
||||
if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_read(addr);
|
||||
}
|
||||
|
||||
void UOROM::ciram_write(uint13 addr, uint8 data) {
|
||||
if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff);
|
||||
return ppu.ciram_write(addr, data);
|
||||
}
|
||||
|
||||
void UOROM::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void UOROM::reset() {
|
||||
prg_bank = 0;
|
||||
}
|
||||
|
||||
void UOROM::serialize(serializer &s) {
|
||||
s.integer(prg_bank);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
struct UOROM : Mapper {
|
||||
uint8 prg_read(uint16 addr);
|
||||
void prg_write(uint16 addr, uint8 data);
|
||||
|
||||
uint8 chr_read(uint16 addr);
|
||||
void chr_write(uint16 addr, uint8 data);
|
||||
|
||||
uint8 ciram_read(uint13 addr);
|
||||
void ciram_write(uint13 addr, uint8 data);
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint4 prg_bank;
|
||||
};
|
||||
|
||||
extern UOROM uorom;
|
|
@ -0,0 +1,342 @@
|
|||
VRC6 vrc6;
|
||||
|
||||
//
|
||||
|
||||
void VRC6::Pulse::clock() {
|
||||
if(--divider == 0) {
|
||||
divider = frequency;
|
||||
cycle++;
|
||||
output = (mode == 1 || cycle < duty) ? volume : (uint4)0;
|
||||
}
|
||||
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
|
||||
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::Pulse::Pulse() {
|
||||
for(unsigned n = 0; n < 16; n++) {
|
||||
dac[n] = -15360 + (n * 2048);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void VRC6::Sawtooth::clock() {
|
||||
if(--divider == 0) {
|
||||
divider = frequency;
|
||||
if(phase == 0) {
|
||||
phase = 0;
|
||||
accumulator = 0;
|
||||
} else if((phase & 1) == 0) {
|
||||
accumulator += rate;
|
||||
}
|
||||
}
|
||||
|
||||
output = accumulator >> 3;
|
||||
if(enable == false) output = 0;
|
||||
}
|
||||
|
||||
void VRC6::Sawtooth::serialize(serializer &s) {
|
||||
s.integer(rate);
|
||||
s.integer(enable);
|
||||
s.integer(frequency);
|
||||
|
||||
s.integer(divider);
|
||||
s.integer(phase);
|
||||
s.integer(accumulator);
|
||||
s.integer(output);
|
||||
}
|
||||
|
||||
VRC6::Sawtooth::Sawtooth() {
|
||||
for(unsigned n = 0; n < 32; n++) {
|
||||
dac[n] = -15872 + (n * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void VRC6::main() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
if(irq_enable) {
|
||||
if(irq_mode == 0) {
|
||||
irq_scalar -= 3;
|
||||
if(irq_scalar <= 0) {
|
||||
irq_scalar += 341;
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(irq_mode == 1) {
|
||||
if(irq_counter == 0xff) {
|
||||
irq_counter = irq_latch;
|
||||
irq_line = 1;
|
||||
} else {
|
||||
irq_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pulse1.clock();
|
||||
pulse2.clock();
|
||||
sawtooth.clock();
|
||||
|
||||
signed output = 0;
|
||||
output += pulse1.dac[ pulse1.output];
|
||||
output += pulse2.dac[ pulse2.output];
|
||||
output += sawtooth.dac[sawtooth.output];
|
||||
output /= 6; //div by 3 for channel count; div that by 2 to match NES volume intensity
|
||||
output = sclamp<16>(output);
|
||||
|
||||
apu.set_sample(output);
|
||||
cpu.set_irq_line(irq_line);
|
||||
tick();
|
||||
}
|
||||
}
|
||||
|
||||
uint8 VRC6::prg_read(uint16 addr) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
return prg_ram[addr & 0x1fff];
|
||||
}
|
||||
|
||||
if((addr & 0xc000) == 0x8000) {
|
||||
return prg_data((prg_bank[0] << 14) | (addr & 0x3fff));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xc000) {
|
||||
return prg_data((prg_bank[1] << 13) | (addr & 0x1fff));
|
||||
}
|
||||
|
||||
if((addr & 0xe000) == 0xe000) {
|
||||
return prg_data((0xff << 13) | (addr & 0x1fff));
|
||||
}
|
||||
|
||||
return cpu.mdr();
|
||||
}
|
||||
|
||||
void VRC6::prg_write(uint16 addr, uint8 data) {
|
||||
if((addr & 0xe000) == 0x6000) {
|
||||
prg_ram[addr & 0x1fff] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
addr = (addr & 0xf003);
|
||||
if(abus_swap) addr = (addr & ~3) | ((addr & 2) >> 1) | ((addr & 1) << 1);
|
||||
|
||||
switch(addr) {
|
||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||
prg_bank[0] = data;
|
||||
break;
|
||||
|
||||
case 0x9000:
|
||||
pulse1.mode = data & 0x80;
|
||||
pulse1.duty = (data & 0x70) >> 4;
|
||||
pulse1.volume = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0x9001:
|
||||
pulse1.frequency = (pulse1.frequency & 0x0f00) | ((data & 0xff) << 0);
|
||||
break;
|
||||
|
||||
case 0x9002:
|
||||
pulse1.frequency = (pulse1.frequency & 0x00ff) | ((data & 0x0f) << 8);
|
||||
pulse1.enable = data & 0x80;
|
||||
break;
|
||||
|
||||
case 0xa000:
|
||||
pulse2.mode = data & 0x80;
|
||||
pulse2.duty = (data & 0x70) >> 4;
|
||||
pulse2.volume = data & 0x0f;
|
||||
break;
|
||||
|
||||
case 0xa001:
|
||||
pulse2.frequency = (pulse2.frequency & 0x0f00) | ((data & 0xff) << 0);
|
||||
break;
|
||||
|
||||
case 0xa002:
|
||||
pulse2.frequency = (pulse2.frequency & 0x00ff) | ((data & 0x0f) << 8);
|
||||
pulse2.enable = data & 0x80;
|
||||
break;
|
||||
|
||||
case 0xb000:
|
||||
sawtooth.rate = data & 0x3f;
|
||||
break;
|
||||
|
||||
case 0xb001:
|
||||
sawtooth.frequency = (sawtooth.frequency & 0x0f00) | ((data & 0xff) << 0);
|
||||
break;
|
||||
|
||||
case 0xb002:
|
||||
sawtooth.frequency = (sawtooth.frequency & 0x00ff) | ((data & 0x0f) << 8);
|
||||
sawtooth.enable = data & 0x80;
|
||||
break;
|
||||
|
||||
case 0xb003:
|
||||
mirror_select = (data >> 2) & 3;
|
||||
break;
|
||||
|
||||
case 0xc000: case 0xc001: case 0xc002: case 0xc003:
|
||||
prg_bank[1] = data;
|
||||
break;
|
||||
|
||||
case 0xd000: case 0xd001: case 0xd002: case 0xd003:
|
||||
chr_bank[0 + (addr & 3)] = data;
|
||||
break;
|
||||
|
||||
case 0xe000: case 0xe001: case 0xe002: case 0xe003:
|
||||
chr_bank[4 + (addr & 3)] = data;
|
||||
break;
|
||||
|
||||
case 0xf000:
|
||||
irq_latch = data;
|
||||
break;
|
||||
|
||||
case 0xf001:
|
||||
irq_mode = data & 0x04;
|
||||
irq_enable = data & 0x02;
|
||||
irq_acknowledge = data & 0x01;
|
||||
if(irq_enable) {
|
||||
irq_counter = irq_latch;
|
||||
irq_scalar = 341;
|
||||
}
|
||||
irq_line = 0;
|
||||
break;
|
||||
|
||||
case 0xf002:
|
||||
irq_enable = irq_acknowledge;
|
||||
irq_line = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 VRC6::chr_read(uint16 addr) {
|
||||
unsigned bank = chr_bank[(addr >> 10) & 7];
|
||||
return chr_data((bank << 10) | (addr & 0x03ff));
|
||||
}
|
||||
|
||||
void VRC6::chr_write(uint16 addr, uint8 data) {
|
||||
if(cartridge.chr_ram == false) return;
|
||||
unsigned bank = chr_bank[(addr >> 10) & 7];
|
||||
chr_data((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)
|
||||
}
|
||||
}
|
||||
|
||||
uint8 VRC6::ciram_read(uint13 addr) {
|
||||
return ppu.ciram_read(ciram_addr(addr));
|
||||
}
|
||||
|
||||
void VRC6::ciram_write(uint13 addr, uint8 data) {
|
||||
return ppu.ciram_write(ciram_addr(addr), data);
|
||||
}
|
||||
|
||||
unsigned VRC6::ram_size() {
|
||||
return 8192u;
|
||||
}
|
||||
|
||||
uint8* VRC6::ram_data() {
|
||||
return prg_ram;
|
||||
}
|
||||
|
||||
void VRC6::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void VRC6::reset() {
|
||||
foreach(n, prg_ram) n = 0xff;
|
||||
|
||||
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;
|
||||
chr_bank[6] = 0;
|
||||
chr_bank[7] = 0;
|
||||
mirror_select = 0;
|
||||
irq_latch = 0;
|
||||
irq_mode = 0;
|
||||
irq_enable = 0;
|
||||
irq_acknowledge = 0;
|
||||
|
||||
irq_counter = 0;
|
||||
irq_scalar = 0;
|
||||
irq_line = 0;
|
||||
|
||||
pulse1.mode = 0;
|
||||
pulse1.duty = 0;
|
||||
pulse1.volume = 0;
|
||||
pulse1.enable = 0;
|
||||
pulse1.frequency = 0;
|
||||
|
||||
pulse1.divider = 0;
|
||||
pulse1.cycle = 0;
|
||||
pulse1.output = 0;
|
||||
|
||||
pulse2.mode = 0;
|
||||
pulse2.duty = 0;
|
||||
pulse2.volume = 0;
|
||||
pulse2.enable = 0;
|
||||
pulse2.frequency = 0;
|
||||
|
||||
pulse2.divider = 0;
|
||||
pulse2.cycle = 0;
|
||||
pulse2.output = 0;
|
||||
|
||||
sawtooth.rate = 0;
|
||||
sawtooth.enable = 0;
|
||||
sawtooth.frequency = 0;
|
||||
|
||||
sawtooth.divider = 0;
|
||||
sawtooth.phase = 0;
|
||||
sawtooth.accumulator = 0;
|
||||
sawtooth.output = 0;
|
||||
}
|
||||
|
||||
void VRC6::serialize(serializer &s) {
|
||||
s.array(prg_ram);
|
||||
|
||||
s.array(prg_bank);
|
||||
s.array(chr_bank);
|
||||
s.integer(mirror_select);
|
||||
s.integer(irq_latch);
|
||||
s.integer(irq_mode);
|
||||
s.integer(irq_enable);
|
||||
s.integer(irq_acknowledge);
|
||||
|
||||
s.integer(irq_counter);
|
||||
s.integer(irq_scalar);
|
||||
s.integer(irq_line);
|
||||
|
||||
pulse1.serialize(s);
|
||||
pulse2.serialize(s);
|
||||
sawtooth.serialize(s);
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
struct VRC6 : Mapper {
|
||||
void main();
|
||||
|
||||
uint8 prg_read(uint16 addr);
|
||||
void prg_write(uint16 addr, uint8 data);
|
||||
|
||||
uint8 chr_read(uint16 addr);
|
||||
void chr_write(uint16 addr, uint8 data);
|
||||
|
||||
uint8 ciram_read(uint13 addr);
|
||||
void ciram_write(uint13 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;
|
||||
int16 dac[16];
|
||||
|
||||
void clock();
|
||||
void serialize(serializer&);
|
||||
Pulse();
|
||||
} pulse1, pulse2;
|
||||
|
||||
struct Sawtooth {
|
||||
uint6 rate;
|
||||
bool enable;
|
||||
uint12 frequency;
|
||||
|
||||
uint12 divider;
|
||||
uint4 phase;
|
||||
uint8 accumulator;
|
||||
uint5 output;
|
||||
int16 dac[32];
|
||||
|
||||
void clock();
|
||||
void serialize(serializer&);
|
||||
Sawtooth();
|
||||
} sawtooth;
|
||||
|
||||
unsigned ciram_addr(unsigned addr) const;
|
||||
};
|
||||
|
||||
extern VRC6 vrc6;
|
|
@ -21,6 +21,10 @@ void System::runtosave() {
|
|||
scheduler.thread = apu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.thread = cartridge.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ Application::Application(int argc, char **argv) {
|
|||
inputManager = new InputManager;
|
||||
utility = new Utility;
|
||||
|
||||
title = "bsnes v082.21";
|
||||
title = "bsnes v082.22";
|
||||
|
||||
#if defined(PLATFORM_WIN)
|
||||
normalFont = "Tahoma, 8";
|
||||
|
|
Loading…
Reference in New Issue