mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
7fc78dae07
commit
c668d10ac7
|
@ -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 {
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -17,15 +17,8 @@ void CPU::main() {
|
|||
|
||||
unsigned lpc = 0xffff;
|
||||
while(true) {
|
||||
if(status.nmi_line) {
|
||||
status.nmi_line = 0;
|
||||
interrupt(0xfffa);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(status.irq_line) {
|
||||
status.irq_line = 0;
|
||||
interrupt(0xfffe);
|
||||
if(status.interrupt_pending) {
|
||||
interrupt();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -40,21 +33,13 @@ void CPU::main() {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -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"); }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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"
|
||||
}
|
||||
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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);
|
||||
addr = prg_addr(addr & 0x4000) | (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
|
@ -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;
|
|
@ -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() {
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 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 uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
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>
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
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);
|
||||
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
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#include "../base.hpp"
|
||||
Config *config = 0;
|
||||
|
||||
Config::Config() {
|
||||
}
|
||||
|
||||
Config::~Config() {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
struct Config : public configuration {
|
||||
Config();
|
||||
~Config();
|
||||
};
|
||||
|
||||
extern Config *config;
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue