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