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.
This commit is contained in:
Tim Allen 2011-09-12 20:30:44 +10:00
parent 7fc78dae07
commit c668d10ac7
41 changed files with 821 additions and 259 deletions

View File

@ -30,6 +30,9 @@ namespace nall {
inline uint_t() : data(0) {}
inline uint_t(const unsigned i) : data(uclip<bits>(i)) {}
template<int s> inline unsigned operator=(const uint_t<s> &i) { return data = uclip<bits>((unsigned)i); }
template<int s> inline uint_t(const uint_t<s> &i) : data(uclip<bits>(i)) {}
};
template<unsigned bits> class int_t {

View File

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

99
bsnes/nes/apu/apu.cpp Executable file
View File

@ -0,0 +1,99 @@
#include <nes/nes.hpp>
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;
}
}
}

45
bsnes/nes/apu/apu.hpp Executable file
View File

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

View File

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

View File

@ -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<Cartridge> {
void load(const string &xml, const uint8_t *data, unsigned size);
void unload();
@ -29,8 +11,6 @@ struct Cartridge : property<Cartridge> {
//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<Cartridge> {
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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::*op)()>
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

30
bsnes/nes/mapper/mapper.cpp Executable file
View File

@ -0,0 +1,30 @@
#include <nes/nes.hpp>
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"
}
}

22
bsnes/nes/mapper/mapper.hpp Executable file
View File

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

View File

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

View File

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

39
bsnes/nes/mapper/none/none.cpp Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <nes/interface/interface.hpp>
#include <nes/system/system.hpp>
#include <nes/scheduler/scheduler.hpp>
#include <nes/mapper/mapper.hpp>
#include <nes/cartridge/cartridge.hpp>
#include <nes/memory/memory.hpp>
#include <nes/cpu/cpu.hpp>
#include <nes/apu/apu.hpp>
#include <nes/ppu/ppu.hpp>
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,6 +19,7 @@ using namespace phoenix;
#include <ruby/ruby.hpp>
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;

8
bsnes/ui/config/config.cpp Executable file
View File

@ -0,0 +1,8 @@
#include "../base.hpp"
Config *config = 0;
Config::Config() {
}
Config::~Config() {
}

6
bsnes/ui/config/config.hpp Executable file
View File

@ -0,0 +1,6 @@
struct Config : public configuration {
Config();
~Config();
};
extern Config *config;

View File

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

View File

@ -8,6 +8,7 @@ struct Interface : property<Interface> {
bool loaded();
bool loadCartridge(const string &filename);
bool loadCartridgeNES(const string &filename);
bool loadCartridgeSNES(const string &filename);
bool loadCartridgeGameBoy(const string &filename);

View File

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

View File

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

View File

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