From c668d10ac77dcfa4a4eb355279971a3fa2f29a4a Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 12 Sep 2011 20:30:44 +1000 Subject: [PATCH] Update to v082r08 release. byuu says: Fixed up the PPU to be as close to cycle-perfect as possible. Fixed RMW to write twice instead of read twice. Ryphecha added AOROM and fixed up MMC1. Have CNROM too, but I need to rethink the mapper/board distinction. Apparently the same logic IC is used in both AOROM and CNROM, and it's just a matter of routing the pins to it. I need to consider how crazy it'd be to emulate the logic IC and have boards simply reroute pins to it. If it's too much work, we'll just treat mappers as board + logic IC combinations. We'll see. --- bsnes/nall/varint.hpp | 3 + bsnes/nes/Makefile | 6 +- bsnes/nes/apu/apu.cpp | 99 +++++++++++ bsnes/nes/apu/apu.hpp | 45 +++++ bsnes/nes/cartridge/cartridge.cpp | 37 ++-- bsnes/nes/cartridge/cartridge.hpp | 23 +-- bsnes/nes/cartridge/none/none.cpp | 43 ----- bsnes/nes/cpu/core/core.cpp | 7 + bsnes/nes/cpu/core/core.hpp | 8 + bsnes/nes/cpu/core/exec.cpp | 2 + bsnes/nes/cpu/core/interrupt.cpp | 32 ++++ bsnes/nes/cpu/core/opcodes.cpp | 26 +-- bsnes/nes/cpu/cpu.cpp | 55 +++--- bsnes/nes/cpu/cpu.hpp | 5 +- bsnes/nes/cpu/memory/memory.cpp | 6 +- bsnes/nes/interface/interface.hpp | 2 +- bsnes/nes/mapper/lz93d50/lz93d50.cpp | 105 ++++++++++++ bsnes/nes/mapper/lz93d50/lz93d50.hpp | 26 +++ bsnes/nes/mapper/mapper.cpp | 30 ++++ bsnes/nes/mapper/mapper.hpp | 22 +++ bsnes/nes/{cartridge => mapper}/mmc1/mmc1.cpp | 102 ++++++----- bsnes/nes/{cartridge => mapper}/mmc1/mmc1.hpp | 9 +- bsnes/nes/mapper/none/none.cpp | 39 +++++ bsnes/nes/{cartridge => mapper}/none/none.hpp | 5 + bsnes/nes/mapper/sn74hc161n/sn74hc161n.cpp | 47 +++++ bsnes/nes/mapper/sn74hc161n/sn74hc161n.hpp | 19 +++ bsnes/nes/memory/memory.cpp | 5 +- bsnes/nes/nes.hpp | 44 ++++- bsnes/nes/ppu/ppu.cpp | 160 +++++++++++------- bsnes/nes/ppu/ppu.hpp | 5 +- bsnes/nes/system/system.cpp | 2 + bsnes/snes/snes.hpp | 2 +- bsnes/ui/Makefile | 11 +- bsnes/ui/base.hpp | 4 + bsnes/ui/config/config.cpp | 8 + bsnes/ui/config/config.hpp | 6 + bsnes/ui/interface/interface.cpp | 8 + bsnes/ui/interface/interface.hpp | 1 + bsnes/ui/interface/nes.cpp | 8 +- bsnes/ui/interface/nes.hpp | 2 +- bsnes/ui/main.cpp | 11 ++ 41 files changed, 821 insertions(+), 259 deletions(-) create mode 100755 bsnes/nes/apu/apu.cpp create mode 100755 bsnes/nes/apu/apu.hpp delete mode 100755 bsnes/nes/cartridge/none/none.cpp create mode 100755 bsnes/nes/cpu/core/interrupt.cpp create mode 100755 bsnes/nes/mapper/lz93d50/lz93d50.cpp create mode 100755 bsnes/nes/mapper/lz93d50/lz93d50.hpp create mode 100755 bsnes/nes/mapper/mapper.cpp create mode 100755 bsnes/nes/mapper/mapper.hpp rename bsnes/nes/{cartridge => mapper}/mmc1/mmc1.cpp (60%) rename bsnes/nes/{cartridge => mapper}/mmc1/mmc1.hpp (76%) create mode 100755 bsnes/nes/mapper/none/none.cpp rename bsnes/nes/{cartridge => mapper}/none/none.hpp (67%) create mode 100755 bsnes/nes/mapper/sn74hc161n/sn74hc161n.cpp create mode 100755 bsnes/nes/mapper/sn74hc161n/sn74hc161n.hpp create mode 100755 bsnes/ui/config/config.cpp create mode 100755 bsnes/ui/config/config.hpp diff --git a/bsnes/nall/varint.hpp b/bsnes/nall/varint.hpp index 35649896..d91ea2a5 100755 --- a/bsnes/nall/varint.hpp +++ b/bsnes/nall/varint.hpp @@ -30,6 +30,9 @@ namespace nall { inline uint_t() : data(0) {} inline uint_t(const unsigned i) : data(uclip(i)) {} + + template inline unsigned operator=(const uint_t &i) { return data = uclip((unsigned)i); } + template inline uint_t(const uint_t &i) : data(uclip(i)) {} }; template class int_t { diff --git a/bsnes/nes/Makefile b/bsnes/nes/Makefile index 64732a51..84b9fc7e 100755 --- a/bsnes/nes/Makefile +++ b/bsnes/nes/Makefile @@ -1,11 +1,13 @@ nes_objects := nes-system nes-scheduler -nes_objects += nes-cartridge nes-memory -nes_objects += nes-cpu nes-ppu +nes_objects += nes-mapper nes-cartridge nes-memory +nes_objects += nes-cpu nes-apu nes-ppu objects += $(nes_objects) obj/nes-system.o: $(nes)/system/system.cpp $(call rwildcard,$(nes)/system/) obj/nes-scheduler.o: $(nes)/scheduler/scheduler.cpp $(call rwildcard,$(nes)/scheduler/) +obj/nes-mapper.o: $(nes)/mapper/mapper.cpp $(call rwildcard,$(nes)/mapper/) obj/nes-cartridge.o: $(nes)/cartridge/cartridge.cpp $(call rwildcard,$(nes)/cartridge/) obj/nes-memory.o: $(nes)/memory/memory.cpp $(call rwildcard,$(nes)/memory/) obj/nes-cpu.o: $(nes)/cpu/cpu.cpp $(call rwildcard,$(nes)/cpu/) +obj/nes-apu.o: $(nes)/apu/apu.cpp $(call rwildcard,$(nes)/apu/) obj/nes-ppu.o: $(nes)/ppu/ppu.cpp $(call rwildcard,$(nes)/ppu/) diff --git a/bsnes/nes/apu/apu.cpp b/bsnes/nes/apu/apu.cpp new file mode 100755 index 00000000..2aba5980 --- /dev/null +++ b/bsnes/nes/apu/apu.cpp @@ -0,0 +1,99 @@ +#include + +namespace NES { + +APU apu; + +void APU::Main() { + apu.main(); +} + +void APU::main() { + while(true) { + system.interface->audio_sample(0); + tick(); + } +} + +void APU::tick() { + clock += 12; + if(clock >= 0) co_switch(cpu.thread); +} + +void APU::power() { + reset(); +} + +void APU::reset() { + Processor::create(APU::Main, 21477272); +} + +uint8 APU::read(uint16 addr) { +} + +void APU::write(uint16 addr, uint8 data) { + switch(addr) { + case 0x4000: + pulse1.duty = (data >> 6); + pulse1.halt = data & 0x20; + pulse1.envelope = data & 0x1f; + break; + case 0x4002: + pulse1.timer = (pulse1.timer & 0x0700) | ((data & 0xff) << 0); + break; + case 0x4003: + pulse1.timer = (pulse1.timer & 0x00ff) | ((data & 0x07) << 8); + pulse1.length = (data >> 3); + break; + case 0x4004: + pulse2.duty = (data >> 6); + pulse2.halt = data & 0x20; + pulse2.envelope = data & 0x1f; + break; + case 0x4006: + pulse2.timer = (pulse2.timer & 0x0700) | ((data & 0xff) << 0); + break; + case 0x4007: + pulse2.timer = (pulse2.timer & 0x00ff) | ((data & 0x07) << 8); + pulse2.length = (data >> 3); + break; + case 0x4008: + triangle.control = data & 0x80; + triangle.reload = data & 0x7f; + break; + case 0x400a: + triangle.timer = (triangle.timer & 0x0700) | ((data & 0xff) << 0); + break; + case 0x400b: + triangle.timer = (triangle.timer & 0x00ff) | ((data & 0x07) << 8); + triangle.length = (data >> 3); + break; + case 0x400c: + noise.halt = data & 0x20; + noise.envelope = data & 0x1f; + break; + case 0x400e: + noise.mode = data & 0x80; + noise.period = data & 0x0f; + break; + case 0x400f: + noise.length = (data >> 3); + break; + case 0x4010: + dmc.irq = data & 0x80; + dmc.loop = data & 0x40; + dmc.rate = data & 0x0f; + break; + case 0x4011: + dmc.direct_load = data & 0x7f; + break; + case 0x4012: + dmc.sample_addr = 0xc000 | (data << 6); + break; + case 0x4013: + dmc.sample_length = (data << 4) | 1; + break; + } +} + +} diff --git a/bsnes/nes/apu/apu.hpp b/bsnes/nes/apu/apu.hpp new file mode 100755 index 00000000..b2fd6e96 --- /dev/null +++ b/bsnes/nes/apu/apu.hpp @@ -0,0 +1,45 @@ +struct APU : Processor { + static void Main(); + void main(); + void tick(); + + void power(); + void reset(); + + uint8 read(uint16 addr); + void write(uint16 addr, uint8 data); + + struct Pulse { + uint2 duty; + bool halt; + uint5 envelope; + uint11 timer; + uint5 length; + } pulse1, pulse2; + + struct Triangle { + bool control; + uint7 reload; + uint11 timer; + uint5 length; + } triangle; + + struct Noise { + bool halt; + uint5 envelope; + bool mode; + uint4 period; + uint5 length; + } noise; + + struct DMC { //delta modulation channel + bool irq; + bool loop; + uint4 rate; + uint7 direct_load; + uint16 sample_addr; + uint12 sample_length; + } dmc; +}; + +extern APU apu; diff --git a/bsnes/nes/cartridge/cartridge.cpp b/bsnes/nes/cartridge/cartridge.cpp index d8d5cb3f..77792733 100755 --- a/bsnes/nes/cartridge/cartridge.cpp +++ b/bsnes/nes/cartridge/cartridge.cpp @@ -2,29 +2,6 @@ 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; - } - return base; - } - - #include "none/none.cpp" - #include "mmc1/mmc1.cpp" -} - Cartridge cartridge; void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) { @@ -52,8 +29,10 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) { uint8 mapperNumber = ((data[7] >> 4) << 4) | (data[6] >> 4); switch(mapperNumber) { - default: mapper = &mapperNone; break; - case 0x01: mapper = &mapperMMC1; break; + default : mapper = &Mapper::none; break; + case 1: mapper = &Mapper::mmc1; break; + case 7: mapper = &Mapper::sn74hc161n; break; + case 16: mapper = &Mapper::lz93d50; break; } loaded = true; @@ -97,4 +76,12 @@ void Cartridge::chr_write(uint16 addr, uint8 data) { return mapper->chr_write(addr, data); } +uint8 Cartridge::ciram_read(uint13 addr) { + return mapper->ciram_read(addr); +} + +void Cartridge::ciram_write(uint13 addr, uint8 data) { + return mapper->ciram_write(addr, data); +} + } diff --git a/bsnes/nes/cartridge/cartridge.hpp b/bsnes/nes/cartridge/cartridge.hpp index 0b34002d..b3bb1df6 100755 --- a/bsnes/nes/cartridge/cartridge.hpp +++ b/bsnes/nes/cartridge/cartridge.hpp @@ -1,21 +1,3 @@ -namespace Mapper { - struct Mapper { - unsigned mirror(unsigned addr, unsigned size) const; - - virtual uint8 prg_read(uint16 addr) = 0; - virtual void prg_write(uint16 addr, uint8 data) = 0; - - virtual uint8 chr_read(uint16 addr) = 0; - virtual void chr_write(uint16 addr, uint8 data) = 0; - - virtual void power() = 0; - virtual void reset() = 0; - }; - - #include "none/none.hpp" - #include "mmc1/mmc1.hpp" -} - struct Cartridge : property { void load(const string &xml, const uint8_t *data, unsigned size); void unload(); @@ -29,8 +11,6 @@ struct Cartridge : property { //privileged: Mapper::Mapper *mapper; - Mapper::None mapperNone; - Mapper::MMC1 mapperMMC1; uint8 prg_read(uint16 addr); void prg_write(uint16 addr, uint8 data); @@ -38,6 +18,9 @@ struct Cartridge : property { uint8 chr_read(uint16 addr); void chr_write(uint16 addr, uint8 data); + uint8 ciram_read(uint13 addr); + void ciram_write(uint13 addr, uint8 data); + uint8 *rom_data; unsigned rom_size; diff --git a/bsnes/nes/cartridge/none/none.cpp b/bsnes/nes/cartridge/none/none.cpp deleted file mode 100755 index 5215e8e4..00000000 --- a/bsnes/nes/cartridge/none/none.cpp +++ /dev/null @@ -1,43 +0,0 @@ -uint8 None::prg_read(uint16 addr) { - if(addr >= 0x8000 && addr <= 0xffff) { - addr &= 0x7fff; - if(addr >= cartridge.prg_size) addr &= cartridge.prg_size - 1; - return cartridge.prg_data[addr]; - } -} - -void None::prg_write(uint16 addr, uint8 data) { -} - -uint8 None::chr_read(uint16 addr) { - if(addr <= 0x1fff) { - return cartridge.chr_data[addr & 0x1fff]; - } - - if(addr <= 0x3eff) { - if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_read(addr & 0x07ff); - } - - return ppu.cgram_read(addr & 0x001f); -} - -void None::chr_write(uint16 addr, uint8 data) { - if(addr <= 0x1fff) { - if(cartridge.chr_ram == false) return; - cartridge.chr_data[addr & 0x1fff] = data; - } - - if(addr <= 0x3eff) { - if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); - return ppu.ciram_write(addr & 0x07ff, data); - } - - return ppu.cgram_write(addr & 0x001f, data); -} - -void None::power() { -} - -void None::reset() { -} diff --git a/bsnes/nes/cpu/core/core.cpp b/bsnes/nes/cpu/core/core.cpp index 8fbd2acd..f4394e93 100755 --- a/bsnes/nes/cpu/core/core.cpp +++ b/bsnes/nes/cpu/core/core.cpp @@ -1,3 +1,10 @@ +#define call(op) (this->*op)() +#define L interrupt_test(); + +#include "interrupt.cpp" #include "opcodes.cpp" #include "exec.cpp" #include "disassembler.cpp" + +#undef L +#undef call diff --git a/bsnes/nes/cpu/core/core.hpp b/bsnes/nes/cpu/core/core.hpp index 2aad005b..d8f3527d 100755 --- a/bsnes/nes/cpu/core/core.hpp +++ b/bsnes/nes/cpu/core/core.hpp @@ -13,6 +13,7 @@ struct Flags { }; struct Registers { + uint8 mdr; uint16 pc; uint8 a, x, y, s; Flags p; @@ -29,6 +30,12 @@ uint8 rd; uint8 zp; uint16 aa; +//interrupt.cpp +void interrupt(); +void interrupt_test(); +void set_nmi_line(bool); +void set_irq_line(bool); + //opcodes.cpp void opf_asl(); void opf_adc(); @@ -94,6 +101,7 @@ void op_plp(); void op_rti(); void op_rts(); +void opill_arr_immediate(); void opill_nop_absolute(); void opill_nop_absolute_x(); void opill_nop_immediate(); diff --git a/bsnes/nes/cpu/core/exec.cpp b/bsnes/nes/cpu/core/exec.cpp index 16d2b7bc..cf393395 100755 --- a/bsnes/nes/cpu/core/exec.cpp +++ b/bsnes/nes/cpu/core/exec.cpp @@ -78,6 +78,7 @@ void CPU::op_exec() { case 0x68: return opi_pull(regs.a); case 0x69: return opi_read_immediate<&CPU::opf_adc>(); case 0x6a: return opi_shift<&CPU::opf_rra>(); + I case 0x6b: return opill_arr_immediate(); case 0x6c: return op_jmp_indirect_absolute(); case 0x6d: return opi_read_absolute<&CPU::opf_adc>(); case 0x6e: return opi_rmw_absolute<&CPU::opf_ror>(); @@ -168,6 +169,7 @@ void CPU::op_exec() { case 0xe8: return opi_increment(regs.x); case 0xe9: return opi_read_immediate<&CPU::opf_sbc>(); case 0xea: return op_nop(); + I case 0xeb: return opi_read_immediate<&CPU::opf_sbc>(); case 0xec: return opi_read_absolute<&CPU::opf_cpx>(); case 0xed: return opi_read_absolute<&CPU::opf_sbc>(); case 0xee: return opi_rmw_absolute<&CPU::opf_inc>(); diff --git a/bsnes/nes/cpu/core/interrupt.cpp b/bsnes/nes/cpu/core/interrupt.cpp new file mode 100755 index 00000000..0c094705 --- /dev/null +++ b/bsnes/nes/cpu/core/interrupt.cpp @@ -0,0 +1,32 @@ +void CPU::interrupt() { + op_readpc(); + op_readpc(); + op_writesp(regs.pc >> 8); + op_writesp(regs.pc >> 0); + op_writesp(regs.p | 0x20); + uint16 vector = 0xfffe; //IRQ + if(status.nmi_pending) { + status.nmi_pending = false; + vector = 0xfffa; + } + abs.l = op_read(vector++); + regs.p.i = 1; + regs.p.d = 0; +L abs.h = op_read(vector++); + regs.pc = abs.w; +} + +void CPU::interrupt_test() { + status.interrupt_pending = (status.irq_line & ~regs.p.i) | status.nmi_pending; +} + +void CPU::set_nmi_line(bool line) { + //edge-sensitive (0->1) + if(!status.nmi_line && line) status.nmi_pending = true; + status.nmi_line = line; +} + +void CPU::set_irq_line(bool line) { + //level-sensitive + status.irq_line = line; +} diff --git a/bsnes/nes/cpu/core/opcodes.cpp b/bsnes/nes/cpu/core/opcodes.cpp index afca5a02..f7fb5241 100755 --- a/bsnes/nes/cpu/core/opcodes.cpp +++ b/bsnes/nes/cpu/core/opcodes.cpp @@ -1,6 +1,3 @@ -#define call(op) (this->*op)() -#define L - //opcode functions //================ @@ -280,7 +277,7 @@ void CPU::opi_rmw_absolute() { abs.l = op_readpci(); abs.h = op_readpci(); rd = op_read(abs.w); - op_readpc(); + op_write(abs.w, rd); call(op); L op_write(abs.w, rd); } @@ -291,7 +288,7 @@ void CPU::opi_rmw_absolute_x() { abs.h = op_readpci(); op_readpc(); rd = op_read(abs.w + regs.x); - op_readpc(); + op_write(abs.w + regs.x, rd); call(op); L op_write(abs.w + regs.x, rd); } @@ -300,8 +297,8 @@ template void CPU::opi_rmw_zero_page() { zp = op_readpci(); rd = op_read(zp); + op_write(zp, rd); call(op); - op_readpc(); L op_write(zp, rd); } @@ -310,8 +307,8 @@ void CPU::opi_rmw_zero_page_x() { zp = op_readpci(); op_readpc(); rd = op_readdp(zp + regs.x); + op_writedp(zp + regs.x, rd); call(op); - op_readpc(); L op_writedp(zp + regs.x, rd); } @@ -398,7 +395,7 @@ void CPU::op_brk() { abs.l = op_read(0xfffe); regs.p.i = 1; regs.p.d = 0; - abs.h = op_read(0xffff); +L abs.h = op_read(0xffff); regs.pc = abs.w; } @@ -462,6 +459,16 @@ L op_readpc(); //illegal opcodes //=============== +void CPU::opill_arr_immediate() { +L rd = op_readpci(); + regs.a &= rd; + regs.a = (regs.p.c << 7) | (regs.a >> 1); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); + regs.p.c = (regs.a & 0x40); + regs.p.v = regs.p.c ^ ((regs.a >> 5) & 1); +} + void CPU::opill_nop_absolute() { abs.l = op_readpci(); abs.h = op_readpci(); @@ -493,6 +500,3 @@ void CPU::opill_nop_zero_page_x() { op_readpc(); L op_readpc(); } - -#undef call -#undef L diff --git a/bsnes/nes/cpu/cpu.cpp b/bsnes/nes/cpu/cpu.cpp index b7f13099..8207b6dd 100755 --- a/bsnes/nes/cpu/cpu.cpp +++ b/bsnes/nes/cpu/cpu.cpp @@ -17,44 +17,29 @@ void CPU::main() { unsigned lpc = 0xffff; while(true) { - if(status.nmi_line) { - status.nmi_line = 0; - interrupt(0xfffa); + if(status.interrupt_pending) { + interrupt(); continue; } - if(status.irq_line) { - status.irq_line = 0; - interrupt(0xfffe); - continue; + if(trace) { + if(lpc != regs.pc) { print(disassemble(), "\n"); } lpc = regs.pc; + //if(lpc != regs.pc) { fprintf(fp, "%s\n", (const char*)disassemble()); fflush(fp); } lpc = regs.pc; } - if(trace) { - if(lpc != regs.pc) { print(disassemble(), "\n"); } lpc = regs.pc; - //if(lpc != regs.pc) { fprintf(fp, "%s\n", (const char*)disassemble()); fflush(fp); } lpc = regs.pc; - } - op_exec(); opcodeCounter++; } } void CPU::add_clocks(unsigned clocks) { + apu.clock -= clocks; + if(apu.clock < 0) co_switch(apu.thread); + ppu.clock -= clocks; if(ppu.clock < 0) co_switch(ppu.thread); } -void CPU::interrupt(uint16 vector) { - op_writesp(regs.pc >> 8); - op_writesp(regs.pc >> 0); - op_writesp(regs.p | 0x20); - abs.l = op_read(vector + 0); - regs.p.i = 1; - regs.p.d = 0; - abs.h = op_read(vector + 1); - regs.pc = abs.w; -} - void CPU::power() { regs.a = 0x00; regs.x = 0x00; @@ -74,20 +59,27 @@ void CPU::power() { void CPU::reset() { Processor::create(CPU::Main, 21477272); + regs.mdr = 0x00; regs.s -= 3; regs.p.i = 1; regs.pc = bus.read(0xfffc) << 0; regs.pc |= bus.read(0xfffd) << 8; - status.nmi_line = false; - status.irq_line = false; + status.interrupt_pending = false; + status.nmi_pending = false; + status.nmi_line = 0; + status.irq_line = 0; status.controller_latch = false; status.controller_port0 = 0; status.controller_port1 = 0; } +uint8 CPU::mdr() const { + return regs.mdr; +} + uint8 CPU::ram_read(uint16 addr) { return ram[addr & 0x07ff]; } @@ -98,20 +90,23 @@ void CPU::ram_write(uint16 addr, uint8 data) { uint8 CPU::read(uint16 addr) { if(addr == 0x4016) { - if(status.controller_port0 >= 8) return 1; + if(status.controller_port0 >= 8) return (mdr() & 0xc0) | 1; return system.interface->input_poll(0, 0u, status.controller_port0++); } if(addr == 0x4017) { - if(status.controller_port1 >= 8) return 1; + if(status.controller_port1 >= 8) return (mdr() & 0xc0) | 1; return system.interface->input_poll(1, 0u, status.controller_port1++); } - return 0x00; + return apu.read(addr); } void CPU::write(uint16 addr, uint8 data) { - if(addr == 0x4014) return oam_dma(data << 8); + if(addr == 0x4014) { + return oam_dma(data << 8); + } + if(addr == 0x4016) { status.controller_latch = data & 0x01; if(status.controller_latch) { @@ -119,6 +114,8 @@ void CPU::write(uint16 addr, uint8 data) { status.controller_port1 = 0; } } + + return apu.write(addr, data); } void CPU::oam_dma(uint16 addr) { diff --git a/bsnes/nes/cpu/cpu.hpp b/bsnes/nes/cpu/cpu.hpp index 07de3a40..09973e85 100755 --- a/bsnes/nes/cpu/cpu.hpp +++ b/bsnes/nes/cpu/cpu.hpp @@ -4,6 +4,8 @@ struct CPU : Processor { uint8 ram[0x0800]; struct Status { + bool interrupt_pending; + bool nmi_pending; bool nmi_line; bool irq_line; @@ -15,11 +17,12 @@ struct CPU : Processor { static void Main(); void main(); void add_clocks(unsigned clocks); - void interrupt(uint16 vector); void power(); void reset(); + uint8 mdr() const; + uint8 ram_read(uint16 addr); void ram_write(uint16 addr, uint8 data); diff --git a/bsnes/nes/cpu/memory/memory.cpp b/bsnes/nes/cpu/memory/memory.cpp index 6d0bde3b..3bb994e2 100755 --- a/bsnes/nes/cpu/memory/memory.cpp +++ b/bsnes/nes/cpu/memory/memory.cpp @@ -1,12 +1,12 @@ uint8 CPU::op_read(uint16 addr) { - uint8 data = bus.read(addr); + regs.mdr = bus.read(addr); add_clocks(12); - return data; + return regs.mdr; } void CPU::op_write(uint16 addr, uint8 data) { add_clocks(12); - bus.write(addr, data); + bus.write(addr, regs.mdr = data); } // diff --git a/bsnes/nes/interface/interface.hpp b/bsnes/nes/interface/interface.hpp index f6c92d9c..2e22c488 100755 --- a/bsnes/nes/interface/interface.hpp +++ b/bsnes/nes/interface/interface.hpp @@ -1,6 +1,6 @@ struct Interface { virtual void video_refresh(const uint32_t *data) {} - virtual void audio_sample(int16_t lsample, int16_t rsample) {} + virtual void audio_sample(int16_t sample) {} virtual int16_t input_poll(bool port, unsigned device, unsigned id) { return 0; } virtual void message(const string &text) { print(text, "\n"); } diff --git a/bsnes/nes/mapper/lz93d50/lz93d50.cpp b/bsnes/nes/mapper/lz93d50/lz93d50.cpp new file mode 100755 index 00000000..c56366c5 --- /dev/null +++ b/bsnes/nes/mapper/lz93d50/lz93d50.cpp @@ -0,0 +1,105 @@ +LZ93D50 lz93d50; + +uint8 LZ93D50::prg_read(uint16 addr) { + clock(); + + if(addr >= 0x8000 && addr <= 0xbfff) { + unsigned rom_addr = (prg_bank << 14) | (addr & 0x3fff); + return cartridge.prg_data[mirror(rom_addr, cartridge.prg_size)]; + } + + if(addr >= 0xc000 && addr <= 0xffff) { + unsigned rom_addr = (0x0f << 14) | (addr & 0x3fff); + return cartridge.prg_data[mirror(rom_addr, cartridge.prg_size)]; + } + + return cpu.mdr(); +} + +void LZ93D50::prg_write(uint16 addr, uint8 data) { + clock(); + + 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 LZ93D50::chr_read(uint16 addr) { + unsigned rom_addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff); + return cartridge.chr_data[mirror(rom_addr, cartridge.chr_size)]; +} + +void LZ93D50::chr_write(uint16 addr, uint8 data) { + if(cartridge.chr_ram == false) return; + unsigned rom_addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff); + cartridge.chr_data[mirror(rom_addr, cartridge.chr_size)] = data; +} + +uint8 LZ93D50::ciram_read(uint13 addr) { + addr = ciram_addr(addr); + return ppu.ciram_read(addr); +} + +void LZ93D50::ciram_write(uint13 addr, uint8 data) { + addr = ciram_addr(addr); + return ppu.ciram_write(addr, data); +} + +void LZ93D50::power() { + reset(); +} + +void LZ93D50::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 LZ93D50::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 LZ93D50::clock() { + if(irq_counter_enable) { + if(--irq_counter == 0xffff) { + cpu.set_irq_line(1); + irq_counter_enable = false; + } + } +} diff --git a/bsnes/nes/mapper/lz93d50/lz93d50.hpp b/bsnes/nes/mapper/lz93d50/lz93d50.hpp new file mode 100755 index 00000000..24370114 --- /dev/null +++ b/bsnes/nes/mapper/lz93d50/lz93d50.hpp @@ -0,0 +1,26 @@ +struct LZ93D50 : 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(); + +private: + unsigned ciram_addr(unsigned addr) const; + void clock(); + + uint8 chr_bank[8]; + uint8 prg_bank; + uint8 mirror_select; + bool irq_counter_enable; + uint16 irq_counter; + uint16 irq_latch; +}; + +extern LZ93D50 lz93d50; diff --git a/bsnes/nes/mapper/mapper.cpp b/bsnes/nes/mapper/mapper.cpp new file mode 100755 index 00000000..d86aecef --- /dev/null +++ b/bsnes/nes/mapper/mapper.cpp @@ -0,0 +1,30 @@ +#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; + } + return base; + } + + #include "none/none.cpp" + #include "lz93d50/lz93d50.cpp" + #include "mmc1/mmc1.cpp" + #include "sn74hc161n/sn74hc161n.cpp" +} + +} diff --git a/bsnes/nes/mapper/mapper.hpp b/bsnes/nes/mapper/mapper.hpp new file mode 100755 index 00000000..92474745 --- /dev/null +++ b/bsnes/nes/mapper/mapper.hpp @@ -0,0 +1,22 @@ +namespace Mapper { + struct Mapper { + unsigned mirror(unsigned addr, unsigned size) const; + + virtual uint8 prg_read(uint16 addr) = 0; + virtual void prg_write(uint16 addr, uint8 data) = 0; + + virtual uint8 chr_read(uint16 addr) = 0; + virtual void chr_write(uint16 addr, uint8 data) = 0; + + virtual uint8 ciram_read(uint13 addr) = 0; + virtual void ciram_write(uint13 addr, uint8 data) = 0; + + virtual void power() = 0; + virtual void reset() = 0; + }; + + #include "none/none.hpp" + #include "lz93d50/lz93d50.hpp" + #include "mmc1/mmc1.hpp" + #include "sn74hc161n/sn74hc161n.hpp" +} diff --git a/bsnes/nes/cartridge/mmc1/mmc1.cpp b/bsnes/nes/mapper/mmc1/mmc1.cpp similarity index 60% rename from bsnes/nes/cartridge/mmc1/mmc1.cpp rename to bsnes/nes/mapper/mmc1/mmc1.cpp index 34008887..b2cc578f 100755 --- a/bsnes/nes/cartridge/mmc1/mmc1.cpp +++ b/bsnes/nes/mapper/mmc1/mmc1.cpp @@ -1,3 +1,5 @@ +MMC1 mmc1; + unsigned MMC1::ciram_addr(unsigned addr) const { switch(r[0] & 0x03) { case 0: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first) @@ -7,24 +9,52 @@ unsigned MMC1::ciram_addr(unsigned addr) const { } } +//static block: 0 = 8000-ffff, 1 = c000-ffff bool MMC1::prg_mode() const { return r[0] & 0x04; } -unsigned MMC1::prg_addr() const { - if(r[0] & 0x08) return prg_bank() * 0x4000; - return (prg_bank() & ~1) * 0x8000; +//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]; } @@ -57,28 +87,11 @@ uint8 MMC1::prg_read(uint16 readaddr) { } if(addr >= 0x8000 && addr <= 0xffff) { - if(prg_mode() == 0) { - if(addr >= 0x8000 && addr <= 0xbfff) { - addr = (addr & 0x3fff); - return prg_data(addr); - } - if(addr >= 0xc000 && addr <= 0xffff) { - addr = prg_addr() | (addr & 0x3fff); - return prg_data(addr); - } - } - - if(prg_mode() == 1) { - if(addr >= 0x8000 && addr <= 0xbfff) { - addr = prg_addr() | (addr & 0x3fff); - return prg_data(addr); - } - if(addr >= 0xc000 && addr <= 0xffff) { - addr = 15 * 0x4000 | (addr & 0x3fff); - return prg_data(addr); - } - } + addr = prg_addr(addr & 0x4000) | (addr & 0x3fff); + return prg_data(addr); } + + return cpu.mdr(); } void MMC1::prg_write(uint16 addr, uint8 data) { @@ -107,52 +120,52 @@ void MMC1::prg_write(uint16 addr, uint8 data) { uint8 MMC1::chr_read(uint16 readaddr) { unsigned addr = readaddr; + prg_ex_select = addr & 0x1000; if(addr <= 0x0fff) { - if(chr_mode() == 0) addr = chr_banklo() * 0x2000 + (addr & 0x1fff); - if(chr_mode() == 1) addr = chr_banklo() * 0x1000 + (addr & 0x0fff); + addr = chr_banklo() * 0x1000 + (addr & 0x0fff); return chr_data(addr); } if(addr <= 0x1fff) { - if(chr_mode() == 0) addr = chr_banklo() * 0x2000 + (addr & 0x1fff); - if(chr_mode() == 1) addr = chr_bankhi() * 0x1000 + (addr & 0x0fff); + addr = chr_bankhi() * 0x1000 + (addr & 0x0fff); return chr_data(addr); } - if(addr <= 0x3eff) { - addr = ciram_addr(addr); - return ppu.ciram_read(addr); - } - - return ppu.cgram_read(addr); + throw; } void MMC1::chr_write(uint16 readaddr, uint8 data) { unsigned addr = readaddr; + prg_ex_select = addr & 0x1000; if(addr <= 0x0fff) { if(cartridge.chr_ram == false) return; - if(chr_mode() == 0) addr = chr_banklo() * 0x2000 + (addr & 0x1fff); - if(chr_mode() == 1) addr = chr_banklo() * 0x1000 + (addr & 0x0fff); + addr = chr_banklo() * 0x1000 + (addr & 0x0fff); chr_data(addr) = data; return; } if(addr <= 0x1fff) { if(cartridge.chr_ram == false) return; - if(chr_mode() == 0) addr = chr_banklo() * 0x2000 + (addr & 0x1fff); - if(chr_mode() == 1) addr = chr_bankhi() * 0x1000 + (addr & 0x0fff); + addr = chr_bankhi() * 0x1000 + (addr & 0x0fff); chr_data(addr) = data; return; } - if(addr <= 0x3eff) { - addr = ciram_addr(addr); - return ppu.ciram_write(addr, data); - } + throw; +} - return ppu.cgram_write(addr, data); +// + +uint8 MMC1::ciram_read(uint13 addr) { + addr = ciram_addr(addr); + return ppu.ciram_read(addr); +} + +void MMC1::ciram_write(uint13 addr, uint8 data) { + addr = ciram_addr(addr); + return ppu.ciram_write(addr, data); } // @@ -162,11 +175,12 @@ void MMC1::power() { } void MMC1::reset() { + prg_ex_select = 0; shiftaddr = 0; shiftdata = 0; r[0] = 0x0c; r[1] = 0x00; r[2] = 0x01; - r[3] = 0x0f; + r[3] = 0x00; } diff --git a/bsnes/nes/cartridge/mmc1/mmc1.hpp b/bsnes/nes/mapper/mmc1/mmc1.hpp similarity index 76% rename from bsnes/nes/cartridge/mmc1/mmc1.hpp rename to bsnes/nes/mapper/mmc1/mmc1.hpp index 20ade84c..b8b4bee3 100755 --- a/bsnes/nes/cartridge/mmc1/mmc1.hpp +++ b/bsnes/nes/mapper/mmc1/mmc1.hpp @@ -5,18 +5,23 @@ struct MMC1 : Mapper { 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(); 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; - unsigned prg_addr() const; + bool prg_size() const; + unsigned prg_addr(bool region) const; bool chr_mode() const; unsigned chr_banklo() const; unsigned chr_bankhi() const; @@ -26,3 +31,5 @@ private: uint8& prg_data(unsigned addr); uint8& chr_data(unsigned addr); }; + +extern MMC1 mmc1; diff --git a/bsnes/nes/mapper/none/none.cpp b/bsnes/nes/mapper/none/none.cpp new file mode 100755 index 00000000..d88b5cd5 --- /dev/null +++ b/bsnes/nes/mapper/none/none.cpp @@ -0,0 +1,39 @@ +None none; + +uint8 None::prg_read(uint16 addr) { + if(addr >= 0x8000 && addr <= 0xffff) { + addr &= 0x7fff; + if(addr >= cartridge.prg_size) addr &= cartridge.prg_size - 1; + return cartridge.prg_data[addr]; + } + + return cpu.mdr(); +} + +void None::prg_write(uint16 addr, uint8 data) { +} + +uint8 None::chr_read(uint16 addr) { + return cartridge.chr_data[addr & 0x1fff]; +} + +void None::chr_write(uint16 addr, uint8 data) { + if(cartridge.chr_ram == false) return; + cartridge.chr_data[addr & 0x1fff] = data; +} + +uint8 None::ciram_read(uint13 addr) { + if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); + return ppu.ciram_read(addr & 0x07ff); +} + +void None::ciram_write(uint13 addr, uint8 data) { + if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); + return ppu.ciram_write(addr & 0x07ff, data); +} + +void None::power() { +} + +void None::reset() { +} diff --git a/bsnes/nes/cartridge/none/none.hpp b/bsnes/nes/mapper/none/none.hpp similarity index 67% rename from bsnes/nes/cartridge/none/none.hpp rename to bsnes/nes/mapper/none/none.hpp index 1b7e2e3d..1f78cae4 100755 --- a/bsnes/nes/cartridge/none/none.hpp +++ b/bsnes/nes/mapper/none/none.hpp @@ -5,6 +5,11 @@ struct None : Mapper { 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(); }; + +extern None none; diff --git a/bsnes/nes/mapper/sn74hc161n/sn74hc161n.cpp b/bsnes/nes/mapper/sn74hc161n/sn74hc161n.cpp new file mode 100755 index 00000000..70b20cf9 --- /dev/null +++ b/bsnes/nes/mapper/sn74hc161n/sn74hc161n.cpp @@ -0,0 +1,47 @@ +//TODO: this naming nomenclature does not work. +//AOROM and CNROM use this same chip in different pin configurations. +//this emulates AOROM currently ... + +SN74HC161N sn74hc161n; + +uint8 SN74HC161N::prg_read(uint16 addr) { + if(addr & 0x8000) { + unsigned rom_addr = (prg_bank << 15) | (addr & 0x7fff); + return cartridge.prg_data[mirror(rom_addr, cartridge.prg_size)]; + } + + return cpu.mdr(); +} + +void SN74HC161N::prg_write(uint16 addr, uint8 data) { + if(addr & 0x8000) { + prg_bank = data & 0x0f; + mirror_select = data & 0x10; + } +} + +uint8 SN74HC161N::chr_read(uint16 addr) { + return cartridge.chr_data[mirror(addr, cartridge.chr_size)]; +} + +void SN74HC161N::chr_write(uint16 addr, uint8 data) { + if(cartridge.chr_ram == false) return; + cartridge.chr_data[mirror(addr, cartridge.chr_size)] = data; +} + +uint8 SN74HC161N::ciram_read(uint13 addr) { + return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff)); +} + +void SN74HC161N::ciram_write(uint13 addr, uint8 data) { + return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data); +} + +void SN74HC161N::power() { + reset(); +} + +void SN74HC161N::reset() { + prg_bank = 0x0f; + mirror_select = 0; +} diff --git a/bsnes/nes/mapper/sn74hc161n/sn74hc161n.hpp b/bsnes/nes/mapper/sn74hc161n/sn74hc161n.hpp new file mode 100755 index 00000000..49b9cdaa --- /dev/null +++ b/bsnes/nes/mapper/sn74hc161n/sn74hc161n.hpp @@ -0,0 +1,19 @@ +struct SN74HC161N : 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(); + +private: + uint4 prg_bank; + bool mirror_select; +}; + +extern SN74HC161N sn74hc161n; diff --git a/bsnes/nes/memory/memory.cpp b/bsnes/nes/memory/memory.cpp index bfef3292..9dfe3113 100755 --- a/bsnes/nes/memory/memory.cpp +++ b/bsnes/nes/memory/memory.cpp @@ -12,17 +12,18 @@ Bus bus; //$4018-ffff = Cartridge uint8 Bus::read(uint16 addr) { + uint8 data = cartridge.prg_read(addr); if(addr <= 0x1fff) return cpu.ram_read(addr); if(addr <= 0x3fff) return ppu.read(addr); if(addr <= 0x4017) return cpu.read(addr); - return cartridge.prg_read(addr); + return data; } void Bus::write(uint16 addr, uint8 data) { + cartridge.prg_write(addr, data); if(addr <= 0x1fff) return cpu.ram_write(addr, data); if(addr <= 0x3fff) return ppu.write(addr, data); if(addr <= 0x4017) return cpu.write(addr, data); - return cartridge.prg_write(addr, data); } } diff --git a/bsnes/nes/nes.hpp b/bsnes/nes/nes.hpp index 4fe9d975..e46e705c 100755 --- a/bsnes/nes/nes.hpp +++ b/bsnes/nes/nes.hpp @@ -4,13 +4,13 @@ namespace NES { namespace Info { static const char Name[] = "bnes"; - static const char Version[] = "000.03"; + static const char Version[] = "000.04"; } } /* bnes - NES emulator - author: byuu + authors: byuu, Ryphecha license: GPLv2 project started: 2011-09-05 */ @@ -41,12 +41,42 @@ namespace NES { typedef int32_t int32; typedef int64_t int64; - typedef uint8_t uint8; - typedef uint16_t uint16; - typedef uint32_t uint32; - typedef uint64_t uint64; + typedef uint_t< 1> uint1; + typedef uint_t< 2> uint2; + typedef uint_t< 3> uint3; + typedef uint_t< 4> uint4; + typedef uint_t< 5> uint5; + typedef uint_t< 6> uint6; + typedef uint_t< 7> uint7; + typedef uint8_t uint8; + typedef uint_t< 9> uint9; + typedef uint_t<10> uint10; + typedef uint_t<11> uint11; + typedef uint_t<12> uint12; + typedef uint_t<13> uint13; + typedef uint_t<14> uint14; typedef uint_t<15> uint15; + typedef uint16_t uint16; + + typedef uint_t<17> uint17; + typedef uint_t<18> uint18; + typedef uint_t<19> uint19; + typedef uint_t<20> uint20; + typedef uint_t<21> uint21; + typedef uint_t<22> uint22; + typedef uint_t<23> uint23; + typedef uint_t<24> uint24; + typedef uint_t<25> uint25; + typedef uint_t<26> uint26; + typedef uint_t<27> uint27; + typedef uint_t<28> uint28; + typedef uint_t<29> uint29; + typedef uint_t<30> uint30; + typedef uint_t<31> uint31; + typedef uint32_t uint32; + + typedef uint64_t uint64; struct Processor { cothread_t thread; @@ -71,9 +101,11 @@ namespace NES { #include #include #include + #include #include #include #include + #include #include } diff --git a/bsnes/nes/ppu/ppu.cpp b/bsnes/nes/ppu/ppu.cpp index 47964989..fb34d299 100755 --- a/bsnes/nes/ppu/ppu.cpp +++ b/bsnes/nes/ppu/ppu.cpp @@ -15,34 +15,24 @@ void PPU::main() { } void PPU::tick() { - if(++status.lx == 341) { - status.lx = 0; - scanline_edge(); - if(++status.ly == 262) { - status.ly = 0; - frame_edge(); - } - } - clock += 4; if(clock >= 0) co_switch(cpu.thread); } -void PPU::tick(unsigned cycles) { - while(cycles--) tick(); -} - void PPU::scanline_edge() { if(status.ly == 241) { status.nmi = 1; - if(status.nmi_enable) cpu.status.nmi_line = true; + if(status.nmi_enable) cpu.set_nmi_line(1); + } + if(status.ly == 261) { + status.nmi = 0; + cpu.set_nmi_line(0); + status.sprite_zero_hit = 0; } } void PPU::frame_edge() { status.field ^= 1; - status.nmi = 0; - status.sprite_zero_hit = 0; system.interface->video_refresh(buffer); scheduler.exit(); } @@ -76,7 +66,6 @@ void PPU::reset() { status.mdr = 0x00; status.field = 0; status.ly = 0; - status.lx = 0; status.bus_data = 0x00; status.address_latch = 0; @@ -127,6 +116,7 @@ uint8 PPU::read(uint16 addr) { result |= status.sprite_overflow << 5; result |= status.mdr & 0x1f; status.nmi = 0; + cpu.set_nmi_line(0); status.address_latch = 0; break; case 4: //OAMDATA @@ -134,11 +124,15 @@ uint8 PPU::read(uint16 addr) { if((status.oam_addr & 3) == 3) result &= 0xe3; break; case 7: //PPUDATA - if(addr >= 0x3f00) { - result = cartridge.chr_read(status.vaddr); - } else { + addr = status.vaddr & 0x3fff; + if(addr <= 0x1fff) { result = status.bus_data; - status.bus_data = cartridge.chr_read(status.vaddr); + status.bus_data = cartridge.chr_read(addr); + } else if(addr <= 0x3eff) { + result = status.bus_data; + status.bus_data = cartridge.ciram_read(addr); + } else if(addr <= 0x3fff) { + result = status.bus_data = cgram_read(addr); } status.vaddr += status.vram_increment; break; @@ -153,6 +147,7 @@ void PPU::write(uint16 addr, uint8 data) { switch(addr & 7) { case 0: //PPUCTRL status.nmi_enable = data & 0x80; + cpu.set_nmi_line(status.nmi_enable && status.nmi); status.master_select = data & 0x40; status.sprite_size = data & 0x20; status.bg_addr = (data & 0x10) ? 0x1000 : 0x0000; @@ -197,7 +192,14 @@ void PPU::write(uint16 addr, uint8 data) { status.address_latch ^= 1; return; case 7: //PPUDATA - cartridge.chr_write(status.vaddr, data); + addr = status.vaddr & 0x3fff; + if(addr <= 0x1fff) { + cartridge.chr_write(addr, data); + } else if(addr <= 0x3eff) { + cartridge.ciram_write(addr, data); + } else if(addr <= 0x3fff) { + cgram_write(addr, data); + } status.vaddr += status.vram_increment; return; } @@ -221,12 +223,6 @@ void PPU::cgram_write(uint16 addr, uint8 data) { cgram[addr & 0x1f] = data; } -uint8 PPU::bus_read(uint16 addr) { - uint8 data = cartridge.chr_read(addr); - tick(2); - return data; -} - // //vaddr = 0yyy VHYY YYYX XXXX @@ -254,6 +250,14 @@ unsigned PPU::scrolly() const { // +void PPU::ly_increment() { + if(++status.ly == 262) { + status.ly = 0; + frame_edge(); + } + scanline_edge(); +} + void PPU::scrollx_increment() { status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f); if((status.vaddr & 0x001f) == 0x0000) { @@ -272,22 +276,18 @@ void PPU::scrolly_increment() { } } -#if 1 +// void PPU::raster_scanline() { if((status.ly >= 240 && status.ly <= 260)) { - return tick(341); + for(unsigned x = 0; x < 340; x++) tick(); + if(raster_enable() == false || status.field != 1 || status.ly != 240) tick(); + return ly_increment(); } uint32 *output = buffer + status.ly * 256; signed lx = 0, ly = (status.ly == 261 ? -1 : status.ly); - if(raster_enable() == false) { - while(lx < 256) output[lx++] = paletteRGB[cgram[0]]; - tick(341); - return; - } - for(unsigned tile = 0; tile < 32; tile++) { // 0-255 unsigned mask = 0x8000 >> status.xaddr; for(unsigned n = 0; n < 8; n++) { @@ -326,21 +326,33 @@ void PPU::raster_scanline() { } } + if(raster_enable() == false) palette = 0; output[lx++] = paletteRGB[cgram[palette]]; } - unsigned nametable = bus_read(0x2000 | (status.vaddr & 0x1fff)); + unsigned nametable = cartridge.ciram_read((uint13)status.vaddr); + unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7); + tick(); + tick(); - unsigned attribute = bus_read(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); + unsigned attribute = cartridge.ciram_read(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); if(scrolly() & 16) attribute >>= 4; if(scrollx() & 16) attribute >>= 2; + tick(); - unsigned addr = status.bg_addr + (nametable << 4) + (scrolly() & 7); - scrollx_increment(); - if(tile == 31) scrolly_increment(); + if(raster_enable()) { + scrollx_increment(); + if(tile == 31) scrolly_increment(); + } + tick(); - unsigned tiledatalo = bus_read(addr + 0); - unsigned tiledatahi = bus_read(addr + 8); + unsigned tiledatalo = cartridge.chr_read(tileaddr + 0); + tick(); + tick(); + + unsigned tiledatahi = cartridge.chr_read(tileaddr + 8); + tick(); + tick(); raster.nametable = (raster.nametable << 8) | nametable; raster.attribute = (raster.attribute << 2) | (attribute & 3); @@ -374,37 +386,57 @@ void PPU::raster_scanline() { } for(unsigned sprite = 0; sprite < 8; sprite++) { //256-319 - unsigned nametable = bus_read(0x2000 | (status.vaddr & 0x1fff)); + unsigned nametable = cartridge.ciram_read((uint13)status.vaddr); + tick(); - if(sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257 - unsigned attribute = bus_read(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); + if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f); //257 + tick(); - unsigned addr = (sprite_height == 8) + unsigned attribute = cartridge.ciram_read(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); + unsigned tileaddr = (sprite_height == 8) ? status.sprite_addr + raster.oam[sprite].tile * 16 : ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000); + tick(); + tick(); unsigned spritey = raster.oam[sprite].y; if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height - 1); if(spritey & 8) spritey += 8; - raster.oam[sprite].tiledatalo = bus_read(addr + spritey + 0); - raster.oam[sprite].tiledatahi = bus_read(addr + spritey + 8); + raster.oam[sprite].tiledatalo = cartridge.chr_read(tileaddr + spritey + 0); + tick(); + tick(); - if(sprite == 6 && status.ly == 261) status.vaddr = status.taddr; //304 + raster.oam[sprite].tiledatahi = cartridge.chr_read(tileaddr + spritey + 8); + tick(); + tick(); + + if(raster_enable() && sprite == 6 && status.ly == 261) status.vaddr = status.taddr; //304 } for(unsigned tile = 0; tile < 2; tile++) { //320-335 - unsigned nametable = bus_read(0x2000 | (status.vaddr & 0x1fff)); + unsigned nametable = cartridge.ciram_read((uint13)status.vaddr & 0x1fff); + unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7); + tick(); + tick(); - unsigned attribute = bus_read(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); + unsigned attribute = cartridge.ciram_read(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); if(scrolly() & 16) attribute >>= 4; if(scrollx() & 16) attribute >>= 2; + tick(); - unsigned addr = status.bg_addr + (nametable << 4) + (scrolly() & 7); - scrollx_increment(); + if(raster_enable()) { + scrollx_increment(); + } + tick(); - unsigned tiledatalo = bus_read(addr + 0); - unsigned tiledatahi = bus_read(addr + 8); + unsigned tiledatalo = cartridge.chr_read(tileaddr + 0); + tick(); + tick(); + + unsigned tiledatahi = cartridge.chr_read(tileaddr + 8); + tick(); + tick(); raster.nametable = (raster.nametable << 8) | nametable; raster.attribute = (raster.attribute << 2) | (attribute & 3); @@ -413,14 +445,23 @@ void PPU::raster_scanline() { } //336-339 - unsigned nametable = bus_read(0x2000 | (status.vaddr & 0x1fff)); - unsigned attribute = bus_read(0x23c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); + unsigned nametable = cartridge.ciram_read((uint13)status.vaddr & 0x1fff); + tick(); + tick(); + + unsigned attribute = cartridge.ciram_read(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5)); + tick(); + tick(); //340 tick(); + + return ly_increment(); } -#else +#if 0 + +//scanline-based PPU renderer void PPU::raster_scanline() { if(raster_enable() == false || (status.ly >= 240 && status.ly <= 260)) { @@ -518,6 +559,7 @@ void PPU::raster_scanline() { tick(37); //341 } + #endif } diff --git a/bsnes/nes/ppu/ppu.hpp b/bsnes/nes/ppu/ppu.hpp index 26769236..f38769bc 100755 --- a/bsnes/nes/ppu/ppu.hpp +++ b/bsnes/nes/ppu/ppu.hpp @@ -2,7 +2,6 @@ struct PPU : Processor { static void Main(); void main(); void tick(); - void tick(unsigned cycles); void scanline_edge(); void frame_edge(); @@ -19,13 +18,12 @@ struct PPU : Processor { uint8 cgram_read(uint16 addr); void cgram_write(uint16 addr, uint8 data); - uint8 bus_read(uint16 addr); - bool raster_enable() const; unsigned nametable_addr() const; unsigned scrollx() const; unsigned scrolly() const; + void ly_increment(); void scrollx_increment(); void scrolly_increment(); @@ -36,7 +34,6 @@ struct PPU : Processor { bool field; unsigned ly; - unsigned lx; uint8 bus_data; diff --git a/bsnes/nes/system/system.cpp b/bsnes/nes/system/system.cpp index 342e955a..d9137275 100755 --- a/bsnes/nes/system/system.cpp +++ b/bsnes/nes/system/system.cpp @@ -11,6 +11,7 @@ void System::run() { void System::power() { cartridge.power(); cpu.power(); + apu.power(); ppu.power(); scheduler.power(); } @@ -18,6 +19,7 @@ void System::power() { void System::reset() { cartridge.reset(); cpu.reset(); + apu.reset(); ppu.reset(); scheduler.reset(); } diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 2bba24c2..7b34f1b2 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -4,7 +4,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "082.07"; + static const char Version[] = "082.08"; static const unsigned SerializerVersion = 21; } } diff --git a/bsnes/ui/Makefile b/bsnes/ui/Makefile index c7824725..4693c895 100755 --- a/bsnes/ui/Makefile +++ b/bsnes/ui/Makefile @@ -3,7 +3,7 @@ include $(snes)/Makefile include $(gameboy)/Makefile name := batch -ui_objects := ui-main ui-interface ui-utility ui-general +ui_objects := ui-main ui-config ui-interface ui-utility ui-general ui_objects += phoenix ruby ui_objects += $(if $(call streq,$(platform),win),resource) @@ -66,10 +66,11 @@ rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c) objects := $(ui_objects) $(objects) objects := $(patsubst %,obj/%.o,$(objects)) -obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/*) -obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/interface/*) -obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/utility/*) -obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/general/*) +obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/) +obj/ui-config.o: $(ui)/config/config.cpp $(call rwildcard,$(ui)/) +obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/) +obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/) +obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/) obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*) $(call compile,$(rubydef) $(rubyflags)) diff --git a/bsnes/ui/base.hpp b/bsnes/ui/base.hpp index 08722007..2473005b 100755 --- a/bsnes/ui/base.hpp +++ b/bsnes/ui/base.hpp @@ -19,6 +19,7 @@ using namespace phoenix; #include using namespace ruby; +#include "config/config.hpp" #include "interface/interface.hpp" #include "utility/utility.hpp" #include "general/general.hpp" @@ -26,6 +27,9 @@ using namespace ruby; struct Application { bool quit; + string realpath; + string userpath; + string title; string normalFont; string boldFont; diff --git a/bsnes/ui/config/config.cpp b/bsnes/ui/config/config.cpp new file mode 100755 index 00000000..1fe3cee6 --- /dev/null +++ b/bsnes/ui/config/config.cpp @@ -0,0 +1,8 @@ +#include "../base.hpp" +Config *config = 0; + +Config::Config() { +} + +Config::~Config() { +} diff --git a/bsnes/ui/config/config.hpp b/bsnes/ui/config/config.hpp new file mode 100755 index 00000000..f203f83a --- /dev/null +++ b/bsnes/ui/config/config.hpp @@ -0,0 +1,6 @@ +struct Config : public configuration { + Config(); + ~Config(); +}; + +extern Config *config; diff --git a/bsnes/ui/interface/interface.cpp b/bsnes/ui/interface/interface.cpp index 71111177..53f2602f 100755 --- a/bsnes/ui/interface/interface.cpp +++ b/bsnes/ui/interface/interface.cpp @@ -13,6 +13,14 @@ bool Interface::loaded() { } } +bool Interface::loadCartridge(const string &filename) { + if(filename.endswith(".nes")) return loadCartridgeNES(filename); + if(filename.endswith(".sfc")) return loadCartridgeSNES(filename); + if(filename.endswith(".gb" )) return loadCartridgeGameBoy(filename); + if(filename.endswith(".gbc")) return loadCartridgeGameBoy(filename); + return true; +} + bool Interface::loadCartridgeNES(const string &filename) { if(nes.loadCartridge(filename) == false) return false; utility->setMode(mode = Mode::NES); diff --git a/bsnes/ui/interface/interface.hpp b/bsnes/ui/interface/interface.hpp index 617f6e3b..57baa9cd 100755 --- a/bsnes/ui/interface/interface.hpp +++ b/bsnes/ui/interface/interface.hpp @@ -8,6 +8,7 @@ struct Interface : property { bool loaded(); + bool loadCartridge(const string &filename); bool loadCartridgeNES(const string &filename); bool loadCartridgeSNES(const string &filename); bool loadCartridgeGameBoy(const string &filename); diff --git a/bsnes/ui/interface/nes.cpp b/bsnes/ui/interface/nes.cpp index 2bef1fd8..cdb263bc 100755 --- a/bsnes/ui/interface/nes.cpp +++ b/bsnes/ui/interface/nes.cpp @@ -36,7 +36,13 @@ void InterfaceNES::video_refresh(const uint32_t *data) { } } -void InterfaceNES::audio_sample(int16_t lsample, int16_t rsample) { +void InterfaceNES::audio_sample(int16_t sample) { + dspaudio.sample(sample, sample); + while(dspaudio.pending()) { + signed lsample, rsample; + dspaudio.read(lsample, rsample); + audio.sample(lsample, rsample); + } } int16_t InterfaceNES::input_poll(bool port, unsigned device, unsigned id) { diff --git a/bsnes/ui/interface/nes.hpp b/bsnes/ui/interface/nes.hpp index 4af344a4..a9106024 100755 --- a/bsnes/ui/interface/nes.hpp +++ b/bsnes/ui/interface/nes.hpp @@ -3,6 +3,6 @@ struct InterfaceNES : NES::Interface { void unloadCartridge(); void video_refresh(const uint32_t *data); - void audio_sample(int16_t lsample, int16_t rsample); + void audio_sample(int16_t sample); int16_t input_poll(bool port, unsigned device, unsigned id); }; diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index 718d1a6f..6141a517 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -16,6 +16,14 @@ void Application::run() { Application::Application(int argc, char **argv) : quit(false) { application = this; + { + char path[PATH_MAX]; + auto unused = ::realpath(argv[0], path); + realpath = path; + unused = ::userpath(path); + userpath = path; + } + config = new Config; interface = new Interface; utility = new Utility; @@ -59,6 +67,8 @@ Application::Application(int argc, char **argv) : quit(false) { input.set(Input::Handle, mainWindow->viewport.handle()); input.init(); + if(argc == 2) interface->loadCartridge(argv[1]); + while(quit == false) { OS::processEvents(); Application::run(); @@ -70,6 +80,7 @@ Application::~Application() { delete mainWindow; delete utility; delete interface; + delete config; } int main(int argc, char **argv) {