From 046e478d86dcfda5ad1ae3cb0b0185b5c3677103 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 26 Sep 2011 21:27:06 +1000 Subject: [PATCH] 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. --- bsnes/nes/apu/apu.cpp | 8 +- bsnes/nes/apu/apu.hpp | 2 + bsnes/nes/apu/serialization.cpp | 1 + bsnes/nes/cartridge/cartridge.cpp | 13 + bsnes/nes/cartridge/cartridge.hpp | 5 +- bsnes/nes/cpu/cpu.cpp | 3 + bsnes/nes/mapper/bandai-fcg/bandai-fcg.cpp | 30 +- bsnes/nes/mapper/bandai-fcg/bandai-fcg.hpp | 3 +- bsnes/nes/mapper/mapper.cpp | 94 +++--- bsnes/nes/mapper/mapper.hpp | 5 + bsnes/nes/mapper/mmc1/mmc1.hpp | 2 +- bsnes/nes/mapper/mmc3/mmc3.cpp | 58 ++-- bsnes/nes/mapper/mmc3/mmc3.hpp | 6 +- bsnes/nes/mapper/uorom/uorom.cpp | 46 +++ bsnes/nes/mapper/uorom/uorom.hpp | 20 ++ bsnes/nes/mapper/vrc6/vrc6.cpp | 342 +++++++++++++++++++++ bsnes/nes/mapper/vrc6/vrc6.hpp | 75 +++++ bsnes/nes/system/system.cpp | 4 + bsnes/ui/main.cpp | 2 +- 19 files changed, 643 insertions(+), 76 deletions(-) create mode 100755 bsnes/nes/mapper/uorom/uorom.cpp create mode 100755 bsnes/nes/mapper/uorom/uorom.hpp create mode 100755 bsnes/nes/mapper/vrc6/vrc6.cpp create mode 100755 bsnes/nes/mapper/vrc6/vrc6.hpp diff --git a/bsnes/nes/apu/apu.cpp b/bsnes/nes/apu/apu.cpp index ccaf892c0..3e55022fb 100755 --- a/bsnes/nes/apu/apu.cpp +++ b/bsnes/nes/apu/apu.cpp @@ -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(); } diff --git a/bsnes/nes/apu/apu.hpp b/bsnes/nes/apu/apu.hpp index c480cbac4..5d56e90f4 100755 --- a/bsnes/nes/apu/apu.hpp +++ b/bsnes/nes/apu/apu.hpp @@ -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]; diff --git a/bsnes/nes/apu/serialization.cpp b/bsnes/nes/apu/serialization.cpp index 3842edeb4..a59df34ff 100755 --- a/bsnes/nes/apu/serialization.cpp +++ b/bsnes/nes/apu/serialization.cpp @@ -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) { diff --git a/bsnes/nes/cartridge/cartridge.cpp b/bsnes/nes/cartridge/cartridge.cpp index 7a98a5389..dcbd48039 100755 --- a/bsnes/nes/cartridge/cartridge.cpp +++ b/bsnes/nes/cartridge/cartridge.cpp @@ -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(); } diff --git a/bsnes/nes/cartridge/cartridge.hpp b/bsnes/nes/cartridge/cartridge.hpp index 6cc39821d..901413b7a 100755 --- a/bsnes/nes/cartridge/cartridge.hpp +++ b/bsnes/nes/cartridge/cartridge.hpp @@ -1,4 +1,7 @@ -struct Cartridge : property { +struct Cartridge : Processor, property { + static void Main(); + void main(); + void load(const string &xml, const uint8_t *data, unsigned size); void unload(); diff --git a/bsnes/nes/cpu/cpu.cpp b/bsnes/nes/cpu/cpu.cpp index 230e87dcb..77d4f87e2 100755 --- a/bsnes/nes/cpu/cpu.cpp +++ b/bsnes/nes/cpu/cpu.cpp @@ -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() { diff --git a/bsnes/nes/mapper/bandai-fcg/bandai-fcg.cpp b/bsnes/nes/mapper/bandai-fcg/bandai-fcg.cpp index b0066bed5..e3bbfddcd 100755 --- a/bsnes/nes/mapper/bandai-fcg/bandai-fcg.cpp +++ b/bsnes/nes/mapper/bandai-fcg/bandai-fcg.cpp @@ -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) { diff --git a/bsnes/nes/mapper/bandai-fcg/bandai-fcg.hpp b/bsnes/nes/mapper/bandai-fcg/bandai-fcg.hpp index 13bdbf41b..b39f224f4 100755 --- a/bsnes/nes/mapper/bandai-fcg/bandai-fcg.hpp +++ b/bsnes/nes/mapper/bandai-fcg/bandai-fcg.hpp @@ -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; diff --git a/bsnes/nes/mapper/mapper.cpp b/bsnes/nes/mapper/mapper.cpp index 7ee3837a5..0aa5e5195 100755 --- a/bsnes/nes/mapper/mapper.cpp +++ b/bsnes/nes/mapper/mapper.cpp @@ -1,48 +1,66 @@ #include namespace NES { - namespace Mapper { - 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; + +void Mapper::main() { + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } - return base; - } - uint8& Mapper::prg_data(unsigned addr) { - return cartridge.prg_data[mirror(addr, cartridge.prg_size)]; + cartridge.clock += 12 * 4095; + tick(); } - - uint8& Mapper::chr_data(unsigned addr) { - return cartridge.chr_data[mirror(addr, cartridge.chr_size)]; - } - - 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" } +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_data(unsigned addr) { + return cartridge.prg_data[mirror(addr, cartridge.prg_size)]; +} + +uint8& Mapper::chr_data(unsigned addr) { + return cartridge.chr_data[mirror(addr, cartridge.chr_size)]; +} + +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" + +} } diff --git a/bsnes/nes/mapper/mapper.hpp b/bsnes/nes/mapper/mapper.hpp index 0b74a1e53..6989e7a48 100755 --- a/bsnes/nes/mapper/mapper.hpp +++ b/bsnes/nes/mapper/mapper.hpp @@ -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" } diff --git a/bsnes/nes/mapper/mmc1/mmc1.hpp b/bsnes/nes/mapper/mmc1/mmc1.hpp index d41d48b13..8eab17504 100755 --- a/bsnes/nes/mapper/mmc1/mmc1.hpp +++ b/bsnes/nes/mapper/mmc1/mmc1.hpp @@ -9,7 +9,7 @@ struct MMC1 : Mapper { void ciram_write(uint13 addr, uint8 data); unsigned ram_size(); - uint8 *ram_data(); + uint8* ram_data(); void power(); void reset(); diff --git a/bsnes/nes/mapper/mmc3/mmc3.cpp b/bsnes/nes/mapper/mmc3/mmc3.cpp index d1db70a57..d6b3e2b9c 100755 --- a/bsnes/nes/mapper/mmc3/mmc3.cpp +++ b/bsnes/nes/mapper/mmc3/mmc3.cpp @@ -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); } diff --git a/bsnes/nes/mapper/mmc3/mmc3.hpp b/bsnes/nes/mapper/mmc3/mmc3.hpp index cbf3ca7ed..3b0038fab 100755 --- a/bsnes/nes/mapper/mmc3/mmc3.hpp +++ b/bsnes/nes/mapper/mmc3/mmc3.hpp @@ -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; diff --git a/bsnes/nes/mapper/uorom/uorom.cpp b/bsnes/nes/mapper/uorom/uorom.cpp new file mode 100755 index 000000000..bff2be93a --- /dev/null +++ b/bsnes/nes/mapper/uorom/uorom.cpp @@ -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); +} diff --git a/bsnes/nes/mapper/uorom/uorom.hpp b/bsnes/nes/mapper/uorom/uorom.hpp new file mode 100755 index 000000000..ec06cb67c --- /dev/null +++ b/bsnes/nes/mapper/uorom/uorom.hpp @@ -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; diff --git a/bsnes/nes/mapper/vrc6/vrc6.cpp b/bsnes/nes/mapper/vrc6/vrc6.cpp new file mode 100755 index 000000000..152e8468a --- /dev/null +++ b/bsnes/nes/mapper/vrc6/vrc6.cpp @@ -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); +} diff --git a/bsnes/nes/mapper/vrc6/vrc6.hpp b/bsnes/nes/mapper/vrc6/vrc6.hpp new file mode 100755 index 000000000..8ffe47117 --- /dev/null +++ b/bsnes/nes/mapper/vrc6/vrc6.hpp @@ -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; diff --git a/bsnes/nes/system/system.cpp b/bsnes/nes/system/system.cpp index fdbd2c522..f25bf07af 100755 --- a/bsnes/nes/system/system.cpp +++ b/bsnes/nes/system/system.cpp @@ -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; } diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index 60788c550..eb6ef0e3e 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -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";