o add missing files

This commit is contained in:
David Voswinkel
2009-05-12 22:20:41 +02:00
parent 8e877d38d4
commit 09198f5099
149 changed files with 43548 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
#ifdef SYSTEM_CPP
Audio audio;
void Audio::sample(uint16 l_sample, uint16 r_sample) {
system.interface->audio_sample(l_sample, r_sample);
}
void Audio::init() {
}
#endif

View File

@@ -0,0 +1,8 @@
class Audio {
public:
void sample(uint16 l_sample, uint16 r_sample);
void init();
};
extern Audio audio;

View File

@@ -0,0 +1,26 @@
#ifdef SYSTEM_CPP
Configuration config;
Configuration::Configuration() {
controller_port1 = Input::DeviceJoypad;
controller_port2 = Input::DeviceJoypad;
expansion_port = System::ExpansionBSX;
region = System::Autodetect;
cpu.version = 2;
cpu.ntsc_clock_rate = 21477272;
cpu.pal_clock_rate = 21281370;
cpu.alu_mul_delay = 2;
cpu.alu_div_delay = 2;
cpu.wram_init_value = 0x55;
smp.ntsc_clock_rate = 32041 * 768;
smp.pal_clock_rate = 32041 * 768;
ppu1.version = 1;
ppu2.version = 3;
}
#endif

View File

@@ -0,0 +1,33 @@
struct Configuration {
unsigned controller_port1;
unsigned controller_port2;
unsigned expansion_port;
unsigned region;
struct CPU {
unsigned version;
unsigned ntsc_clock_rate;
unsigned pal_clock_rate;
unsigned alu_mul_delay;
unsigned alu_div_delay;
unsigned wram_init_value;
} cpu;
struct SMP {
unsigned ntsc_clock_rate;
unsigned pal_clock_rate;
} smp;
struct PPU1 {
unsigned version;
} ppu1;
struct PPU2 {
unsigned version;
} ppu2;
Configuration();
};
extern Configuration config;

View File

@@ -0,0 +1,355 @@
#ifdef SYSTEM_CPP
Input input;
uint8 Input::port_read(bool portnumber) {
port_t &p = port[portnumber];
switch(p.device) {
case DeviceJoypad: {
if(p.counter0 >= 16) return 1;
unsigned deviceid = (portnumber == 0 ? DeviceIDJoypad1 : DeviceIDJoypad2);
return system.interface->input_poll(deviceid, p.counter0++);
} //case DeviceJoypad
case DeviceMultitap: {
if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0
unsigned deviceidx, deviceid0, deviceid1;
if(portnumber == 0) {
if(cpu.pio() & 0x40) {
deviceidx = p.counter0;
if(deviceidx >= 16) return 3;
p.counter0++;
deviceid0 = DeviceIDMultitap1A;
deviceid1 = DeviceIDMultitap1B;
} else {
deviceidx = p.counter1;
if(deviceidx >= 16) return 3;
p.counter1++;
deviceid0 = DeviceIDMultitap1C;
deviceid1 = DeviceIDMultitap1D;
}
} else {
if(cpu.pio() & 0x80) {
deviceidx = p.counter0;
if(deviceidx >= 16) return 3;
p.counter0++;
deviceid0 = DeviceIDMultitap2A;
deviceid1 = DeviceIDMultitap2B;
} else {
deviceidx = p.counter1;
if(deviceidx >= 16) return 3;
p.counter1++;
deviceid0 = DeviceIDMultitap2C;
deviceid1 = DeviceIDMultitap2D;
}
}
return (system.interface->input_poll(deviceid0, deviceidx) << 0)
| (system.interface->input_poll(deviceid1, deviceidx) << 1);
} //case DeviceMultitap
case DeviceMouse: {
if(p.counter0 >= 32) return 1;
unsigned deviceid = (portnumber == 0 ? DeviceIDMouse1 : DeviceIDMouse2);
int position_x = system.interface->input_poll(deviceid, MouseX); //-n = left, 0 = center, +n = right
int position_y = system.interface->input_poll(deviceid, MouseY); //-n = up, 0 = center, +n = right
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
if(position_x < 0) position_x = -position_x; //abs(position_x)
if(position_y < 0) position_y = -position_y; //abs(position_x)
position_x = min(127, position_x); //range = 0 - 127
position_y = min(127, position_y); //range = 0 - 127
switch(p.counter0++) { default:
case 0: return 0;
case 1: return 0;
case 2: return 0;
case 3: return 0;
case 4: return 0;
case 5: return 0;
case 6: return 0;
case 7: return 0;
case 8: return system.interface->input_poll(deviceid, MouseRight);
case 9: return system.interface->input_poll(deviceid, MouseLeft);
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
case 11: return 0; // ||
case 12: return 0; //signature
case 13: return 0; // ||
case 14: return 0; // ||
case 15: return 1; // ||
case 16: return (direction_y) & 1;
case 17: return (position_y >> 6) & 1;
case 18: return (position_y >> 5) & 1;
case 19: return (position_y >> 4) & 1;
case 20: return (position_y >> 3) & 1;
case 21: return (position_y >> 2) & 1;
case 22: return (position_y >> 1) & 1;
case 23: return (position_y >> 0) & 1;
case 24: return (direction_x) & 1;
case 25: return (position_x >> 6) & 1;
case 26: return (position_x >> 5) & 1;
case 27: return (position_x >> 4) & 1;
case 28: return (position_x >> 3) & 1;
case 29: return (position_x >> 2) & 1;
case 30: return (position_x >> 1) & 1;
case 31: return (position_x >> 0) & 1;
}
} //case DeviceMouse
case DeviceSuperScope: {
if(portnumber == 0) break; //Super Scope in port 1 not supported ...
if(p.counter0 >= 8) return 1;
if(p.counter0 == 0) {
//turbo is a switch; toggle is edge sensitive
bool turbo = system.interface->input_poll(DeviceIDSuperScope, SuperScopeTurbo);
if(turbo && !p.superscope.turbolock) {
p.superscope.turbo = !p.superscope.turbo; //toggle state
p.superscope.turbolock = true;
} else if(!turbo) {
p.superscope.turbolock = false;
}
//trigger is a button
//if turbo is active, trigger is level sensitive; otherwise it is edge sensitive
p.superscope.trigger = false;
bool trigger = system.interface->input_poll(DeviceIDSuperScope, SuperScopeTrigger);
if(trigger && (p.superscope.turbo || !p.superscope.triggerlock)) {
p.superscope.trigger = true;
p.superscope.triggerlock = true;
} else if(!trigger) {
p.superscope.triggerlock = false;
}
//cursor is a button; it is always level sensitive
p.superscope.cursor = system.interface->input_poll(DeviceIDSuperScope, SuperScopeCursor);
//pause is a button; it is always edge sensitive
p.superscope.pause = false;
bool pause = system.interface->input_poll(DeviceIDSuperScope, SuperScopePause);
if(pause && !p.superscope.pauselock) {
p.superscope.pause = true;
p.superscope.pauselock = true;
} else if(!pause) {
p.superscope.pauselock = false;
}
p.superscope.offscreen =
p.superscope.x < 0 || p.superscope.x >= 256
|| p.superscope.y < 0 || p.superscope.y >= (ppu.overscan() ? 240 : 225);
}
switch(p.counter0++) {
case 0: return p.superscope.trigger;
case 1: return p.superscope.cursor;
case 2: return p.superscope.turbo;
case 3: return p.superscope.pause;
case 4: return 0;
case 5: return 0;
case 6: return p.superscope.offscreen;
case 7: return 0; //noise (1 = yes)
}
} //case DeviceSuperScope
case DeviceJustifier:
case DeviceJustifiers: {
if(portnumber == 0) break; //Justifier in port 1 not supported ...
if(p.counter0 >= 32) return 1;
if(p.counter0 == 0) {
p.justifier.trigger1 = system.interface->input_poll(DeviceIDJustifier1, JustifierTrigger);
p.justifier.start1 = system.interface->input_poll(DeviceIDJustifier1, JustifierStart);
if(p.device == DeviceJustifiers) {
p.justifier.trigger2 = system.interface->input_poll(DeviceIDJustifier2, JustifierTrigger);
p.justifier.start2 = system.interface->input_poll(DeviceIDJustifier2, JustifierStart);
} else {
p.justifier.x2 = -1;
p.justifier.y2 = -1;
p.justifier.trigger2 = false;
p.justifier.start2 = false;
}
}
switch(p.counter0++) {
case 0: return 0;
case 1: return 0;
case 2: return 0;
case 3: return 0;
case 4: return 0;
case 5: return 0;
case 6: return 0;
case 7: return 0;
case 8: return 0;
case 9: return 0;
case 10: return 0;
case 11: return 0;
case 12: return 1; //signature
case 13: return 1; // ||
case 14: return 1; // ||
case 15: return 0; // ||
case 16: return 0;
case 17: return 1;
case 18: return 0;
case 19: return 1;
case 20: return 0;
case 21: return 1;
case 22: return 0;
case 23: return 1;
case 24: return p.justifier.trigger1;
case 25: return p.justifier.trigger2;
case 26: return p.justifier.start1;
case 27: return p.justifier.start2;
case 28: return p.justifier.active;
case 29: return 0;
case 30: return 0;
case 31: return 0;
}
} //case DeviceJustifier(s)
} //switch(p.device)
//no device connected
return 0;
}
//scan all input; update cursor positions if needed
void Input::update() {
system.interface->input_poll();
port_t &p = port[1];
switch(p.device) {
case DeviceSuperScope: {
int x = system.interface->input_poll(DeviceIDSuperScope, SuperScopeX);
int y = system.interface->input_poll(DeviceIDSuperScope, SuperScopeY);
x += p.superscope.x;
y += p.superscope.y;
p.superscope.x = max(-16, min(256 + 16, x));
p.superscope.y = max(-16, min(240 + 16, y));
latchx = p.superscope.x;
latchy = p.superscope.y;
} break;
case DeviceJustifier:
case DeviceJustifiers: {
int x1 = system.interface->input_poll(DeviceIDJustifier1, JustifierX);
int y1 = system.interface->input_poll(DeviceIDJustifier1, JustifierY);
x1 += p.justifier.x1;
y1 += p.justifier.y1;
p.justifier.x1 = max(-16, min(256 + 16, x1));
p.justifier.y1 = max(-16, min(240 + 16, y1));
int x2 = system.interface->input_poll(DeviceIDJustifier2, JustifierX);
int y2 = system.interface->input_poll(DeviceIDJustifier2, JustifierY);
x2 += p.justifier.x2;
y2 += p.justifier.y2;
p.justifier.x2 = max(-16, min(256 + 16, x2));
p.justifier.y2 = max(-16, min(240 + 16, y2));
if(p.justifier.active == 0) {
latchx = p.justifier.x1;
latchy = p.justifier.y1;
} else {
latchx = (p.device == DeviceJustifiers ? p.justifier.x2 : -1);
latchy = (p.device == DeviceJustifiers ? p.justifier.y2 : -1);
}
} break;
}
if(latchy < 0 || latchy >= (ppu.overscan() ? 240 : 225) || latchx < 0 || latchx >= 256) {
//cursor is offscreen, set to invalid position so counters are not latched
latchx = ~0;
latchy = ~0;
} else {
//cursor is onscreen
latchx += 40; //offset trigger position to simulate hardware latching delay
latchx <<= 2; //dot -> clock conversion
latchx += 2; //align trigger on half-dot ala interrupts (speed optimization for sCPU::add_clocks)
}
}
void Input::port_set_device(bool portnumber, unsigned device) {
port_t &p = port[portnumber];
p.device = device;
p.counter0 = 0;
p.counter1 = 0;
//set iobit to true if device is capable of latching PPU counters
iobit = port[1].device == DeviceSuperScope
|| port[1].device == DeviceJustifier
|| port[1].device == DeviceJustifiers;
latchx = -1;
latchy = -1;
if(device == DeviceSuperScope) {
p.superscope.x = 256 / 2;
p.superscope.y = 240 / 2;
p.superscope.trigger = false;
p.superscope.cursor = false;
p.superscope.turbo = false;
p.superscope.pause = false;
p.superscope.offscreen = false;
p.superscope.turbolock = false;
p.superscope.triggerlock = false;
p.superscope.pauselock = false;
} else if(device == DeviceJustifier) {
p.justifier.active = 0;
p.justifier.x1 = 256 / 2;
p.justifier.y1 = 240 / 2;
p.justifier.x2 = -1;
p.justifier.y2 = -1;
p.justifier.trigger1 = false;
p.justifier.trigger2 = false;
p.justifier.start1 = false;
p.justifier.start2 = false;
} else if(device == DeviceJustifiers) {
p.justifier.active = 0;
p.justifier.x1 = 256 / 2 - 16;
p.justifier.y1 = 240 / 2;
p.justifier.x2 = 256 / 2 + 16;
p.justifier.y2 = 240 / 2;
p.justifier.trigger1 = false;
p.justifier.trigger2 = false;
p.justifier.start1 = false;
p.justifier.start2 = false;
}
}
void Input::poll() {
port[0].counter0 = 0;
port[0].counter1 = 0;
port[1].counter0 = 0;
port[1].counter1 = 0;
port[1].justifier.active = !port[1].justifier.active;
}
void Input::init() {
}
#endif

View File

@@ -0,0 +1,114 @@
class Input {
public:
enum Device {
DeviceNone,
DeviceJoypad,
DeviceMultitap,
DeviceMouse,
DeviceSuperScope,
DeviceJustifier,
DeviceJustifiers,
};
enum DeviceID {
DeviceIDNone,
DeviceIDJoypad1,
DeviceIDJoypad2,
DeviceIDMultitap1A,
DeviceIDMultitap1B,
DeviceIDMultitap1C,
DeviceIDMultitap1D,
DeviceIDMultitap2A,
DeviceIDMultitap2B,
DeviceIDMultitap2C,
DeviceIDMultitap2D,
DeviceIDMouse1,
DeviceIDMouse2,
DeviceIDSuperScope,
DeviceIDJustifier1,
DeviceIDJustifier2,
};
enum JoypadID {
JoypadB = 0, JoypadY = 1,
JoypadSelect = 2, JoypadStart = 3,
JoypadUp = 4, JoypadDown = 5,
JoypadLeft = 6, JoypadRight = 7,
JoypadA = 8, JoypadX = 9,
JoypadL = 10, JoypadR = 11,
};
enum MouseID {
MouseX = 0, MouseY = 1,
MouseLeft = 2, MouseRight = 3,
};
enum SuperScopeID {
SuperScopeX = 0, SuperScopeY = 1,
SuperScopeTrigger = 2, SuperScopeCursor = 3,
SuperScopeTurbo = 4, SuperScopePause = 5,
};
enum JustifierID {
JustifierX = 0, JustifierY = 1,
JustifierTrigger = 2, JustifierStart = 3,
};
uint8 port_read(bool port);
void port_set_device(bool port, unsigned device);
void init();
void poll();
void update();
//light guns (Super Scope, Justifier(s)) strobe IOBit whenever the CRT
//beam cannon is detected. this needs to be tested at the cycle level
//(hence inlining here for speed) to avoid 'dead space' during DRAM refresh.
//iobit is updated during port_set_device(),
//latchx, latchy are updated during update() (once per frame)
alwaysinline void tick() {
//only test if Super Scope or Justifier is connected
if(iobit && ppu.vcounter() == latchy && ppu.hcounter() == latchx) {
ppu.latch_counters();
}
}
private:
bool iobit;
uint16_t latchx, latchy;
struct port_t {
unsigned device;
unsigned counter0; //read counters
unsigned counter1;
struct superscope_t {
int x, y;
bool trigger;
bool cursor;
bool turbo;
bool pause;
bool offscreen;
bool turbolock;
bool triggerlock;
bool pauselock;
} superscope;
struct justifier_t {
bool active;
int x1, x2;
int y1, y2;
bool trigger1, trigger2;
bool start1, start2;
} justifier;
} port[2];
friend class System;
friend class Video;
};
extern Input input;

View File

@@ -0,0 +1,8 @@
class Interface {
public:
virtual void video_refresh(uint16_t *data, unsigned pitch, unsigned *line, unsigned width, unsigned height) {}
virtual void audio_sample(uint16_t l_sample, uint16_t r_sample) {}
virtual void input_poll() {}
virtual int16_t input_poll(unsigned deviceid, unsigned id) { return 0; }
};

View File

@@ -0,0 +1,63 @@
#ifdef SYSTEM_CPP
Scheduler scheduler;
void threadentry_cpu() { cpu.enter(); }
void threadentry_cop() { system.coprocessor_enter(); }
void threadentry_smp() { smp.enter(); }
void threadentry_ppu() { ppu.enter(); }
void threadentry_dsp() { dsp.enter(); }
void Scheduler::enter() {
switch(clock.active) {
case THREAD_CPU: co_switch(thread_cpu); break;
case THREAD_COP: co_switch(thread_cop); break;
case THREAD_SMP: co_switch(thread_smp); break;
case THREAD_PPU: co_switch(thread_ppu); break;
case THREAD_DSP: co_switch(thread_dsp); break;
}
}
void Scheduler::exit() {
co_switch(thread_snes);
}
void Scheduler::init() {
clock.cpu_freq = system.region() == System::NTSC
? config.cpu.ntsc_clock_rate
: config.cpu.pal_clock_rate;
clock.smp_freq = system.region() == System::NTSC
? config.smp.ntsc_clock_rate
: config.smp.pal_clock_rate;
clock.active = THREAD_CPU;
clock.cpucop = 0;
clock.cpuppu = 0;
clock.cpusmp = 0;
clock.smpdsp = 0;
if(thread_cpu) co_delete(thread_cpu);
if(thread_cop) co_delete(thread_cop);
if(thread_smp) co_delete(thread_smp);
if(thread_ppu) co_delete(thread_ppu);
if(thread_dsp) co_delete(thread_dsp);
thread_snes = co_active();
thread_cpu = co_create(65536 * sizeof(void*), threadentry_cpu);
thread_cop = co_create(65536 * sizeof(void*), threadentry_cop);
thread_smp = co_create(65536 * sizeof(void*), threadentry_smp);
thread_ppu = co_create(65536 * sizeof(void*), threadentry_ppu);
thread_dsp = co_create(65536 * sizeof(void*), threadentry_dsp);
}
Scheduler::Scheduler() {
thread_snes = 0;
thread_cpu = 0;
thread_cop = 0;
thread_smp = 0;
thread_ppu = 0;
thread_dsp = 0;
}
#endif

View File

@@ -0,0 +1,140 @@
//scheduler thread relationships:
//S-PPU <-> S-CPU <-> cartridge co-processor
// <|>
// S-SMP <-> S-DSP
class Scheduler {
public:
cothread_t thread_snes;
cothread_t thread_cpu; //S-CPU (5a22)
cothread_t thread_cop; //cartridge co-processor (SuperFX, SA-1, ...)
cothread_t thread_smp; //S-SMP (SPC700)
cothread_t thread_ppu; //S-PPU
cothread_t thread_dsp; //S-DSP
enum ActiveThread {
THREAD_CPU,
THREAD_COP,
THREAD_SMP,
THREAD_PPU,
THREAD_DSP,
};
struct {
unsigned cpu_freq;
unsigned smp_freq;
ActiveThread active;
int64_t cpucop;
int64_t cpuppu;
int64_t cpusmp;
int64_t smpdsp;
} clock;
//==========
//CPU <> COP
//==========
alwaysinline void sync_cpucop() {
if(clock.cpucop < 0) {
clock.active = THREAD_COP;
co_switch(thread_cop);
}
}
alwaysinline void sync_copcpu() {
if(clock.cpucop >= 0) {
clock.active = THREAD_CPU;
co_switch(thread_cpu);
}
}
//==========
//CPU <> PPU
//==========
alwaysinline void sync_cpuppu() {
if(clock.cpuppu < 0) {
clock.active = THREAD_PPU;
co_switch(thread_ppu);
}
}
alwaysinline void sync_ppucpu() {
if(clock.cpuppu >= 0) {
clock.active = THREAD_CPU;
co_switch(thread_cpu);
}
}
//==========
//CPU <> SMP
//==========
alwaysinline void sync_cpusmp() {
if(clock.cpusmp < 0) {
clock.active = THREAD_SMP;
co_switch(thread_smp);
}
}
alwaysinline void sync_smpcpu() {
if(clock.cpusmp >= 0) {
clock.active = THREAD_CPU;
co_switch(thread_cpu);
}
}
//==========
//SMP <> DSP
//==========
alwaysinline void sync_smpdsp() {
if(clock.smpdsp < 0) {
clock.active = THREAD_DSP;
co_switch(thread_dsp);
}
}
alwaysinline void sync_dspsmp() {
if(clock.smpdsp >= 0) {
clock.active = THREAD_SMP;
co_switch(thread_smp);
}
}
//==========
//add clocks
//==========
alwaysinline void addclocks_cpu(unsigned clocks) {
clock.cpucop -= clocks;
clock.cpuppu -= clocks;
clock.cpusmp -= clocks * (uint64_t)clock.smp_freq;
}
alwaysinline void addclocks_cop(unsigned clocks) {
clock.cpucop += clocks;
}
alwaysinline void addclocks_ppu(unsigned clocks) {
clock.cpuppu += clocks;
}
alwaysinline void addclocks_smp(unsigned clocks) {
clock.cpusmp += clocks * (uint64_t)clock.cpu_freq;
clock.smpdsp -= clocks;
}
alwaysinline void addclocks_dsp(unsigned clocks) {
clock.smpdsp += clocks;
}
void enter();
void exit();
void init();
Scheduler();
};
extern Scheduler scheduler;

188
tools/bsnes/system/system.cpp Executable file
View File

@@ -0,0 +1,188 @@
#include <../base.hpp>
#define SYSTEM_CPP
namespace SNES {
System system;
BUSCORE bus;
CPUCORE cpu;
SMPCORE smp;
DSPCORE dsp;
PPUCORE ppu;
#include "scheduler/scheduler.cpp"
#include "tracer/tracer.cpp"
#include "config/config.cpp"
#include "video/video.cpp"
#include "audio/audio.cpp"
#include "input/input.cpp"
void System::coprocessor_enter() {
if(cartridge.mode() == Cartridge::ModeSuperGameBoy) sgb.enter();
if(cartridge.has_sa1()) sa1.enter();
while(true) {
scheduler.addclocks_cop(64 * 1024 * 1024);
scheduler.sync_copcpu();
}
}
void System::run() {
}
void System::runtoframe() {
scheduler.enter();
}
void System::init(Interface *interface_) {
interface = interface_;
assert(interface != 0);
sgb.init();
sa1.init();
bsxbase.init();
bsxcart.init();
bsxflash.init();
srtc.init();
sdd1.init();
spc7110.init();
cx4.init();
dsp1.init();
dsp2.init();
dsp3.init();
dsp4.init();
obc1.init();
st010.init();
video.init();
audio.init();
input.init();
}
void System::term() {
}
void System::power() {
snes_region = max(0, min(2, config.region));
snes_expansion = max(0, min(1, config.expansion_port));
if(snes_region == Autodetect) {
snes_region = (cartridge.region() == Cartridge::NTSC ? NTSC : PAL);
}
scheduler.init();
ppu.PPUcounter::reset();
cpu.power();
smp.power();
dsp.power();
ppu.power();
bus.power();
if(expansion() == ExpansionBSX) bsxbase.power();
if(memory::bsxflash.data()) bsxflash.power();
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.power();
if(cartridge.mode() == Cartridge::ModeSuperGameBoy) sgb.power();
if(cartridge.has_sa1()) sa1.power();
if(cartridge.has_srtc()) srtc.power();
if(cartridge.has_sdd1()) sdd1.power();
if(cartridge.has_spc7110()) spc7110.power();
if(cartridge.has_cx4()) cx4.power();
if(cartridge.has_dsp1()) dsp1.power();
if(cartridge.has_dsp2()) dsp2.power();
if(cartridge.has_dsp3()) dsp3.power();
if(cartridge.has_dsp4()) dsp4.power();
if(cartridge.has_obc1()) obc1.power();
if(cartridge.has_st010()) st010.power();
for(unsigned i = 0x2100; i <= 0x213f; i++) memory::mmio.map(i, ppu);
for(unsigned i = 0x2140; i <= 0x217f; i++) memory::mmio.map(i, cpu);
for(unsigned i = 0x2180; i <= 0x2183; i++) memory::mmio.map(i, cpu);
for(unsigned i = 0x4016; i <= 0x4017; i++) memory::mmio.map(i, cpu);
for(unsigned i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu);
for(unsigned i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu);
if(expansion() == ExpansionBSX) bsxbase.enable();
if(memory::bsxflash.data()) bsxflash.enable();
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.enable();
if(cartridge.mode() == Cartridge::ModeSuperGameBoy) sgb.enable();
if(cartridge.has_sa1()) sa1.enable();
if(cartridge.has_srtc()) srtc.enable();
if(cartridge.has_sdd1()) sdd1.enable();
if(cartridge.has_spc7110()) spc7110.enable();
if(cartridge.has_cx4()) cx4.enable();
if(cartridge.has_dsp1()) dsp1.enable();
if(cartridge.has_dsp2()) dsp2.enable();
if(cartridge.has_dsp3()) dsp3.enable();
if(cartridge.has_dsp4()) dsp4.enable();
if(cartridge.has_obc1()) obc1.enable();
if(cartridge.has_st010()) st010.enable();
input.port_set_device(0, config.controller_port1);
input.port_set_device(1, config.controller_port2);
input.update();
video.update();
}
void System::reset() {
scheduler.init();
ppu.PPUcounter::reset();
cpu.reset();
smp.reset();
dsp.reset();
ppu.reset();
bus.reset();
if(expansion() == ExpansionBSX) bsxbase.reset();
if(memory::bsxflash.data()) bsxflash.reset();
if(cartridge.mode() == Cartridge::ModeBsx) bsxcart.reset();
if(cartridge.mode() == Cartridge::ModeSuperGameBoy) sgb.reset();
if(cartridge.has_sa1()) sa1.reset();
if(cartridge.has_srtc()) srtc.reset();
if(cartridge.has_sdd1()) sdd1.reset();
if(cartridge.has_spc7110()) spc7110.reset();
if(cartridge.has_cx4()) cx4.reset();
if(cartridge.has_dsp1()) dsp1.reset();
if(cartridge.has_dsp2()) dsp2.reset();
if(cartridge.has_dsp3()) dsp3.reset();
if(cartridge.has_dsp4()) dsp4.reset();
if(cartridge.has_obc1()) obc1.reset();
if(cartridge.has_st010()) st010.reset();
input.port_set_device(0, config.controller_port1);
input.port_set_device(1, config.controller_port2);
input.update();
video.update();
}
void System::scanline() {
video.scanline();
if(ppu.vcounter() == 241) {
input.update();
video.update();
scheduler.exit();
}
}
void System::frame() {
}
System::Region System::region() const {
return (System::Region)snes_region;
}
System::ExpansionPortDevice System::expansion() const {
return (System::ExpansionPortDevice)snes_expansion;
}
System::System() : interface(0), snes_region(NTSC), snes_expansion(ExpansionNone) {
}
};

48
tools/bsnes/system/system.hpp Executable file
View File

@@ -0,0 +1,48 @@
#include "config/config.hpp"
#include "interface/interface.hpp"
#include "scheduler/scheduler.hpp"
#include "tracer/tracer.hpp"
#include "video/video.hpp"
#include "audio/audio.hpp"
#include "input/input.hpp"
class System {
public:
void coprocessor_enter();
enum Region { NTSC = 0, PAL = 1 };
enum RegionAutodetect { Autodetect = 2 };
enum ExpansionPortDevice { ExpansionNone = 0, ExpansionBSX = 1 };
//system functions
virtual void run();
virtual void runtoframe();
virtual void init(Interface*);
virtual void term();
virtual void power();
virtual void reset();
virtual void frame();
virtual void scanline();
//return *active* region / expansion port device information
//settings cached upon power-on
Region region() const;
ExpansionPortDevice expansion() const;
System();
virtual ~System() {}
private:
Interface *interface;
unsigned snes_region;
unsigned snes_expansion;
friend class Video;
friend class Audio;
friend class Input;
};
extern System system;

View File

@@ -0,0 +1,93 @@
#ifdef SYSTEM_CPP
Tracer tracer;
void tprintf(const char *s, ...) {
if(tracer.enabled() == false) return;
char str[4096];
va_list args;
va_start(args, s);
vsprintf(str, s, args);
va_end(args);
fprintf(tracer.fp, "%s\r\n", str);
}
void Tracer::trace_cpuop() {
if(enabled() == false) return;
if(cpuop_enabled() == false) return;
if(cpuopmask_enabled() == true) {
unsigned addr = cpu.regs.pc.d;
if(settings.cpuopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) return;
settings.cpuopmasktbl[addr >> 3] |= 0x80 >> (addr & 7);
}
char t[1024];
cpu.disassemble_opcode(t);
fprintf(fp, "%s\r\n", t);
}
void Tracer::trace_smpop() {
if(enabled() == false) return;
if(smpop_enabled() == false) return;
if(smpopmask_enabled() == true) {
unsigned addr = smp.regs.pc;
if(settings.smpopmasktbl[addr >> 3] & 0x80 >> (addr & 7)) return;
settings.smpopmasktbl[addr >> 3] |= 0x80 >> (addr & 7);
}
char t[1024];
smp.disassemble_opcode(t);
fprintf(fp, "%s\r\n", t);
}
void Tracer::enable(bool en) {
if(en == true && enabled() == false) {
fp = fopen("trace.log", "wb");
} else if(en == false && enabled() == true) {
fclose(fp);
fp = 0;
}
settings.enabled = en;
}
void Tracer::cpuopmask_enable(bool en) {
if(en == true && cpuopmask_enabled() == false) {
settings.cpuopmasktbl = new(zeromemory) uint8_t[0x200000];
} else if(en == false && cpuopmask_enabled() == true) {
delete[] settings.cpuopmasktbl;
}
settings.cpuopmask = en;
}
void Tracer::smpopmask_enable(bool en) {
if(en == true && smpopmask_enabled() == false) {
settings.smpopmasktbl = new(zeromemory) uint8_t[0x2000];
} else if(en == false && smpopmask_enabled() == true) {
delete[] settings.smpopmasktbl;
}
settings.smpopmask = en;
}
Tracer::Tracer() {
fp = 0;
settings.cpuop = false;
settings.cpuopmask = false;
settings.cpuopmasktbl = 0;
settings.smpop = false;
settings.smpopmask = false;
settings.smpopmasktbl = 0;
}
Tracer::~Tracer() {
}
#endif

View File

@@ -0,0 +1,45 @@
void tprintf(const char *s, ...);
class Tracer {
private:
FILE *fp;
struct {
bool enabled;
bool cpuop;
bool cpuopmask;
uint8 *cpuopmasktbl;
bool smpop;
bool smpopmask;
uint8 *smpopmasktbl;
} settings;
public:
void enable(bool en);
bool enabled() { return settings.enabled; }
void cpuop_enable(bool en) { settings.cpuop = en; }
bool cpuop_enabled() { return settings.cpuop; }
void cpuopmask_enable(bool en);
bool cpuopmask_enabled() { return settings.cpuopmask; }
void smpop_enable(bool en) { settings.smpop = en; }
bool smpop_enabled() { return settings.smpop; }
void smpopmask_enable(bool en);
bool smpopmask_enabled() { return settings.smpopmask; }
void trace_cpuop();
void trace_smpop();
Tracer();
~Tracer();
friend void tprintf(const char *s, ...);
};
extern Tracer tracer;

View File

@@ -0,0 +1,106 @@
#ifdef SYSTEM_CPP
Video video;
const uint8_t Video::cursor[15 * 15] = {
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
1,2,2,1,1,2,2,2,2,2,1,1,2,2,1,
1,2,1,0,0,1,1,2,1,1,0,0,1,2,1,
0,1,2,1,0,0,1,2,1,0,0,1,2,1,0,
0,1,2,1,0,0,0,1,0,0,0,1,2,1,0,
0,0,1,2,1,1,0,1,0,1,1,2,1,0,0,
0,0,0,1,2,2,1,2,1,2,2,1,0,0,0,
0,0,0,0,1,1,2,2,2,1,1,0,0,0,0,
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,
};
void Video::draw_cursor(uint16_t color, int x, int y) {
for(int cy = 0; cy < 15; cy++) {
int vy = y + cy - 7;
if(vy <= 0 || vy >= 240) continue; //do not draw offscreen
bool hires = (pline_width[vy] == 512);
for(int cx = 0; cx < 15; cx++) {
int vx = x + cx - 7;
if(vx < 0 || vx >= 256) continue; //do not draw offscreen
uint8_t pixel = cursor[cy * 15 + cx];
if(pixel == 0) continue;
uint16_t pixelcolor = (pixel == 1) ? 0 : color;
if(hires == false) {
*((uint16_t*)ppu.output + vy * 1024 + 0 + vx) = pixelcolor;
*((uint16_t*)ppu.output + vy * 1024 + 512 + vx) = pixelcolor;
} else {
*((uint16_t*)ppu.output + vy * 1024 + 0 + vx * 2 + 0) = pixelcolor;
*((uint16_t*)ppu.output + vy * 1024 + 512 + vx * 2 + 0) = pixelcolor;
*((uint16_t*)ppu.output + vy * 1024 + 0 + vx * 2 + 1) = pixelcolor;
*((uint16_t*)ppu.output + vy * 1024 + 512 + vx * 2 + 1) = pixelcolor;
}
}
}
}
void Video::update() {
uint16_t *data = (uint16_t*)ppu.output;
unsigned width, height;
switch(input.port[1].device) {
case Input::DeviceSuperScope: draw_cursor(0x001f, input.port[1].superscope.x, input.port[1].superscope.y); break;
case Input::DeviceJustifiers: draw_cursor(0x02e0, input.port[1].justifier.x2, input.port[1].justifier.y2); //fallthrough
case Input::DeviceJustifier: draw_cursor(0x001f, input.port[1].justifier.x1, input.port[1].justifier.y1); break;
}
unsigned yoffset = 1; //scanline 0 is always black, skip this line for video output
if(mode == ModeNTSC && ppu.overscan()) yoffset += 8; //NTSC overscan centers x240 height image
switch(mode) { default:
case ModeNTSC: { width = 256; height = 224; } break;
case ModePAL: { width = 256; height = 239; } break;
}
if(frame_hires) width <<= 1;
if(frame_interlace) height <<= 1;
system.interface->video_refresh(
data + yoffset * 1024,
/* pitch = */ height <= 240 ? 2048 : 1024,
/* line[] = */ height <= 240 ? (pline_width + yoffset) : (iline_width + yoffset * 2),
width, height
);
frame_hires = false;
frame_interlace = false;
}
void Video::scanline() {
unsigned y = ppu.vcounter();
if(y >= 240) return;
unsigned width = (ppu.hires() == false ? 256 : 512);
pline_width[y] = width;
iline_width[y * 2 + (int)ppu.field()] = width;
frame_hires |= ppu.hires();
frame_interlace |= ppu.interlace();
}
void Video::set_mode(Mode mode_) {
mode = mode_;
}
void Video::init() {
for(unsigned i = 0; i < 240; i++) pline_width[i] = 256;
for(unsigned i = 0; i < 480; i++) iline_width[i] = 256;
frame_hires = false;
frame_interlace = false;
set_mode(ModeNTSC);
}
#endif

View File

@@ -0,0 +1,28 @@
class Video {
public:
enum Mode {
ModeNTSC,
ModePAL,
};
void set_mode(Mode);
private:
Mode mode;
bool frame_hires;
bool frame_interlace;
unsigned pline_width[240]; //progressive
unsigned iline_width[480]; //interlace
void update();
void scanline();
void init();
static const uint8_t cursor[15 * 15];
void draw_cursor(uint16_t color, int x, int y);
friend class System;
};
extern Video video;