Update to v079r06 release.

byuu says:

It does add some more code to the CPU::step() function, so performance
probably went down actually, by about 1%. Removing the input.tick() call
didn't compensate as much as I'd hoped.
Hooked up Super Scope and Justifier support. The good news is that the
Justifier alignment doesn't get fucked up anymore when you go
off-screen. Never could fix that in the old version.
The bad news is that it takes a major speed hit for the time being.
I need to figure out how to run the CPU and input threads out of order.
Every time I try, the input gets thrown off by most of a scanline.
Right now, I'm forced to sync constantly to get the latching position
really accurate. But worst case, I can cut the syncs down by skipping
large chunks around the cursor position, +/-40 clock cycles. So it's
only temporarily slow.
Lastly, killed the old Input class, merged Controllers class into it.
I actually like Controllers as a name better, but it doesn't jive with
video/audio/input, so oh well.
This commit is contained in:
Tim Allen 2011-06-25 22:56:32 +10:00
parent cf09d41669
commit 927c97eb06
24 changed files with 205 additions and 530 deletions

View File

@ -9,7 +9,7 @@ void Link::Enter() { link.enter(); }
void Link::enter() {
while(true) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
unsigned clocks = 1;
if(link_run) clocks = link_run();
step(clocks);

View File

@ -1,16 +1,16 @@
#ifdef NECDSP_CPP
uint8 NECDSP::sr_read(unsigned) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
return regs.sr >> 8;
}
void NECDSP::sr_write(unsigned, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
}
uint8 NECDSP::dr_read(unsigned) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
if(regs.sr.drc == 0) {
//16-bit
if(regs.sr.drs == 0) {
@ -29,7 +29,7 @@ uint8 NECDSP::dr_read(unsigned) {
}
void NECDSP::dr_write(unsigned, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
if(regs.sr.drc == 0) {
//16-bit
if(regs.sr.drs == 0) {
@ -48,7 +48,7 @@ void NECDSP::dr_write(unsigned, uint8 data) {
}
uint8 NECDSP::dp_read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
bool hi = addr & 1;
addr = (addr >> 1) & 2047;
@ -60,7 +60,7 @@ uint8 NECDSP::dp_read(unsigned addr) {
}
void NECDSP::dp_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
bool hi = addr & 1;
addr = (addr >> 1) & 2047;

View File

@ -7,12 +7,12 @@ unsigned SA1::CPUIRAM::size() const {
}
uint8 SA1::CPUIRAM::read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
return sa1.iram.read(addr);
}
void SA1::CPUIRAM::write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
sa1.iram.write(addr, data);
}
@ -21,13 +21,13 @@ unsigned SA1::CPUBWRAM::size() const {
}
uint8 SA1::CPUBWRAM::read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
if(dma) return sa1.dma_cc1_read(addr);
return cartridge.ram.read(addr);
}
void SA1::CPUBWRAM::write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
cartridge.ram.write(addr, data);
}

View File

@ -178,13 +178,13 @@ void SA1::mmc_write(unsigned addr, uint8 data) {
}
uint8 SA1::mmc_cpu_read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
return cpubwram.read(addr);
}
void SA1::mmc_cpu_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
cpubwram.write(addr, data);
}

View File

@ -444,7 +444,7 @@ uint8 SA1::mmio_r230e() {
}
uint8 SA1::mmio_read(unsigned addr) {
(co_active() == cpu.thread ? cpu.synchronize_coprocessor() : synchronize_cpu());
(co_active() == cpu.thread ? cpu.synchronize_coprocessors() : synchronize_cpu());
addr &= 0xffff;
switch(addr) {
@ -469,7 +469,7 @@ uint8 SA1::mmio_read(unsigned addr) {
}
void SA1::mmio_write(unsigned addr, uint8 data) {
(co_active() == cpu.thread ? cpu.synchronize_coprocessor() : synchronize_cpu());
(co_active() == cpu.thread ? cpu.synchronize_coprocessors() : synchronize_cpu());
addr &= 0xffff;
switch(addr) {

View File

@ -59,7 +59,7 @@ void Serial::write(uint8 data) {
}
uint8 Serial::mmio_read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
switch(addr & 1) { default:
case 0: return cpu.mmio_read(addr);
case 1: return cpu.mmio_read(addr);
@ -67,7 +67,7 @@ uint8 Serial::mmio_read(unsigned addr) {
}
void Serial::mmio_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
switch(addr & 1) { default:
case 0: cpu.mmio_write(addr, data); break;
case 1: cpu.mmio_write(addr, data); break;

View File

@ -1,7 +1,7 @@
#ifdef SUPERFX_CPP
uint8 SuperFX::mmio_read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
addr &= 0xffff;
if(addr >= 0x3100 && addr <= 0x32ff) {
@ -53,7 +53,7 @@ uint8 SuperFX::mmio_read(unsigned addr) {
}
void SuperFX::mmio_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
addr &= 0xffff;
if(addr >= 0x3100 && addr <= 0x32ff) {

View File

@ -8,7 +8,28 @@ namespace SNES {
#include "mouse/mouse.cpp"
#include "superscope/superscope.cpp"
#include "justifier/justifier.cpp"
Controllers controllers;
void Controller::Enter() {
if(co_active() == input.port1->thread) input.port1->enter();
if(co_active() == input.port2->thread) input.port2->enter();
}
void Controller::enter() {
while(true) step(1);
}
void Controller::step(unsigned clocks) {
clock += clocks * (uint64)cpu.frequency;
synchronize_cpu();
}
void Controller::synchronize_cpu() {
if(CPU::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
} else {
while(clock >= 0) cpu.enter();
}
}
bool Controller::iobit() {
switch(port) {
@ -19,45 +40,13 @@ bool Controller::iobit() {
void Controller::iobit(bool data) {
switch(port) {
case Controller::Port1: break;
case Controller::Port2: break;
case Controller::Port1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); break;
case Controller::Port2: bus.write(0x4201, (cpu.pio() & ~0x80) | (data << 7)); break;
}
}
Controller::Controller(bool port) : port(port) {
}
void Controllers::connect(bool port, Input::Device id) {
Controller *&controller = (port == Controller::Port1 ? port1 : port2);
if(controller) {
delete controller;
controller = 0;
}
switch(id) { default:
case Input::Device::None: controller = new Controller(port); break;
case Input::Device::Joypad: controller = new Gamepad(port); break;
case Input::Device::Multitap: controller = new Multitap(port); break;
case Input::Device::Mouse: controller = new Mouse(port); break;
case Input::Device::SuperScope: controller = new SuperScope(port); break;
case Input::Device::Justifier: controller = new Justifier(port, false); break;
case Input::Device::Justifiers: controller = new Justifier(port, true); break;
}
switch(port) {
case Controller::Port1: config.controller_port1 = id; break;
case Controller::Port2: config.controller_port2 = id; break;
}
}
Controllers::Controllers() {
connect(Controller::Port1, Input::Device::Joypad);
connect(Controller::Port2, Input::Device::Joypad);
}
Controllers::~Controllers() {
if(port1) delete port1;
if(port2) delete port2;
if(!thread) create(Controller::Enter, 1);
}
}

View File

@ -15,6 +15,11 @@ struct Controller : Processor {
enum : bool { Port1 = 0, Port2 = 1 };
const bool port;
static void Enter();
virtual void enter();
void step(unsigned clocks);
void synchronize_cpu();
bool iobit();
void iobit(bool data);
virtual uint2 data() { return 0; }
@ -22,17 +27,6 @@ struct Controller : Processor {
Controller(bool port);
};
struct Controllers {
Controller *port1;
Controller *port2;
void connect(bool port, Input::Device id);
Controllers();
~Controllers();
};
extern Controllers controllers;
#include "gamepad/gamepad.hpp"
#include "multitap/multitap.hpp"
#include "mouse/mouse.hpp"

View File

@ -1,5 +1,45 @@
#ifdef CONTROLLER_CPP
void Justifier::enter() {
while(true) {
static unsigned prev = 0;
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
if(next >= target && prev < target) {
iobit(0);
iobit(1);
}
if(next < prev) {
int nx1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
int ny1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
nx1 += x1;
ny1 += y1;
x1 = max(-16, min(256 + 16, nx1));
y1 = max(-16, min(240 + 16, ny1));
if(chained == true) {
int nx2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
int ny2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
nx2 += x2;
ny2 += y2;
x2 = max(-16, min(256 + 16, nx2));
y2 = max(-16, min(240 + 16, ny2));
}
if(active == 0) {
bool offscreen = (x1 < 0 || y1 < 0 || x1 >= 256 || y1 >= (ppu.overscan() ? 240 : 225));
target = offscreen ? -1 : y1 * 1364 + (x1 + 24) * 4;
} else {
bool offscreen = (x2 < 0 || y2 < 0 || x2 >= 256 || y2 >= (ppu.overscan() ? 240 : 225));
target = offscreen ? -1 : y2 * 1364 + (x2 + 24) * 4;
}
}
prev = next;
step(4);
}
}
uint2 Justifier::data() {
if(counter >= 32) return 1;
@ -56,25 +96,11 @@ void Justifier::latch(bool data) {
if(latched == data) return;
latched = data;
counter = 0;
if(chained) active = !active; //toggle between both controllers when chained
int nx1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
int ny1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
nx1 += x1;
ny1 += y1;
x1 = max(-16, min(256 + 16, nx1));
y1 = max(-16, min(240 + 16, ny1));
if(chained == false) return;
int nx2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
int ny2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
nx2 += x2;
ny2 += y2;
x2 = max(-16, min(256 + 16, nx2));
y2 = max(-16, min(240 + 16, ny2));
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
}
Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) {
create(Controller::Enter, 21477272);
latched = 0;
counter = 0;
active = 0;
@ -91,6 +117,8 @@ Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chaine
y2 = 240 / 2;
}
target = -1;
trigger1 = false;
trigger2 = false;

View File

@ -1,4 +1,5 @@
struct Justifier : Controller {
void enter();
uint2 data();
void latch(bool data);
Justifier(bool port, bool chained);
@ -9,8 +10,9 @@ struct Justifier : Controller {
unsigned counter;
bool active;
int x1, x2;
int y1, y2;
signed x1, x2;
signed y1, y2;
signed target;
bool trigger1, trigger2;
bool start1, start2;
};

View File

@ -9,6 +9,32 @@
//require manual polling of PIO ($4201.d6) to determine when iobit was written.
//Note that no commercial game ever utilizes a Super Scope in port 1.
void SuperScope::enter() {
while(true) {
static unsigned prev = 0;
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
if(next >= target && prev < target) {
iobit(0);
iobit(1);
}
if(next < prev) {
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
int nx = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
int ny = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
nx += x;
ny += y;
x = max(-16, min(256 + 16, nx));
y = max(-16, min(240 + 16, ny));
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
target = offscreen ? -1 : y * 1364 + (x + 24) * 4;
}
prev = next;
step(4);
}
}
uint2 SuperScope::data() {
if(counter >= 8) return 1;
@ -50,7 +76,7 @@ uint2 SuperScope::data() {
}
switch(counter++) {
case 0: return trigger;
case 0: return offscreen ? 0 : trigger;
case 1: return cursor;
case 2: return turbo;
case 3: return pause;
@ -65,22 +91,17 @@ void SuperScope::latch(bool data) {
if(latched == data) return;
latched = data;
counter = 0;
int nx = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
int ny = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
nx += x;
ny += y;
x = max(-16, min(256 + 16, nx));
y = max(-16, min(240 + 16, ny));
}
SuperScope::SuperScope(bool port) : Controller(port) {
create(Controller::Enter, 21477272);
latched = 0;
counter = 0;
//center cursor onscreen
x = 256 / 2;
y = 240 / 2;
target = -1;
trigger = false;
cursor = false;

View File

@ -1,4 +1,5 @@
struct SuperScope : Controller {
void enter();
uint2 data();
void latch(bool data);
SuperScope(bool port);
@ -7,7 +8,7 @@ struct SuperScope : Controller {
bool latched;
unsigned counter;
int x, y;
signed x, y, target;
bool trigger;
bool cursor;

View File

@ -23,6 +23,9 @@ void CPU::step(unsigned clocks) {
Processor &chip = *coprocessors[i];
chip.clock -= clocks * (uint64)chip.frequency;
}
input.port1->clock -= clocks * (uint64)input.port1->frequency;
input.port2->clock -= clocks * (uint64)input.port2->frequency;
synchronize_controllers();
}
void CPU::synchronize_smp() {
@ -41,13 +44,18 @@ void CPU::synchronize_ppu() {
}
}
void CPU::synchronize_coprocessor() {
void CPU::synchronize_coprocessors() {
for(unsigned i = 0; i < coprocessors.size(); i++) {
Processor &chip = *coprocessors[i];
if(chip.clock < 0) co_switch(chip.thread);
}
}
void CPU::synchronize_controllers() {
if(input.port1->clock < 0) co_switch(input.port1->thread);
if(input.port2->clock < 0) co_switch(input.port2->thread);
}
void CPU::Enter() { cpu.enter(); }
void CPU::enter() {

View File

@ -7,7 +7,8 @@ public:
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_smp();
void synchronize_ppu();
void synchronize_coprocessor();
void synchronize_coprocessors();
void synchronize_controllers();
uint8 port_read(uint2 port) const;
void port_write(uint2 port, uint8 data);

View File

@ -33,13 +33,8 @@ void CPU::mmio_w2183(uint8 data) {
//strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored.
void CPU::mmio_w4016(uint8 data) {
//bool old_latch = status.joypad_strobe_latch;
//bool new_latch = data & 1;
//status.joypad_strobe_latch = new_latch;
//if(old_latch != new_latch) input.poll();
controllers.port1->latch(data & 1);
controllers.port2->latch(data & 1);
input.port1->latch(data & 1);
input.port2->latch(data & 1);
}
//JOYSER0
@ -47,8 +42,7 @@ void CPU::mmio_w4016(uint8 data) {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4016() {
uint8 r = regs.mdr & 0xfc;
//r |= input.port_read(0) & 3;
r |= controllers.port1->data();
r |= input.port1->data();
return r;
}
@ -58,8 +52,7 @@ uint8 CPU::mmio_r4016() {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4017() {
uint8 r = (regs.mdr & 0xe0) | 0x1c;
//r |= input.port_read(1) & 3;
r |= controllers.port2->data();
r |= input.port2->data();
return r;
}

View File

@ -6,18 +6,17 @@ void CPU::step_auto_joypad_poll() {
status.auto_joypad_active = status.auto_joypad_counter <= 15;
if(status.auto_joypad_active && status.auto_joypad_poll) {
synchronize_controllers();
if(status.auto_joypad_counter == 0) {
//input.poll();
controllers.port1->latch(1);
controllers.port2->latch(1);
controllers.port1->latch(0);
controllers.port2->latch(0);
input.port1->latch(1);
input.port2->latch(1);
input.port1->latch(0);
input.port2->latch(0);
}
//uint8 port0 = input.port_read(0);
//uint8 port1 = input.port_read(1);
uint2 port0 = controllers.port1->data();
uint2 port1 = controllers.port2->data();
uint2 port0 = input.port1->data();
uint2 port1 = input.port2->data();
status.joy1 = (status.joy1 << 1) | (bool)(port0 & 1);
status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1);

View File

@ -40,7 +40,7 @@ void CPU::scanline() {
//forcefully sync S-CPU to other processors, in case chips are not communicating
synchronize_ppu();
synchronize_smp();
synchronize_coprocessor();
synchronize_coprocessors();
system.scanline();
if(vcounter() == 0) {

View File

@ -1,344 +1,38 @@
#ifdef SYSTEM_CPP
Input input1;
Input input;
uint8 Input::port_read(bool portnumber) {
if(cartridge.has_serial() && portnumber == 1) {
return (serial.data2() << 1) | (serial.data1() << 0);
void Input::connect(bool port, Input::Device id) {
Controller *&controller = (port == Controller::Port1 ? port1 : port2);
if(controller) {
delete controller;
controller = 0;
}
port_t &p = port[portnumber];
switch(p.device) {
case Device::Joypad: {
if(cpu.joylatch() == 0) {
if(p.counter0 >= 16) return 1;
return system.interface->input_poll(portnumber, p.device, 0, p.counter0++);
} else {
return system.interface->input_poll(portnumber, p.device, 0, 0);
}
} //case Device::Joypad
case Device::Multitap: {
if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0
unsigned deviceidx, deviceindex0, deviceindex1;
uint8 mask = (portnumber == 0 ? 0x40 : 0x80);
if(cpu.pio() & mask) {
deviceidx = p.counter0;
if(deviceidx >= 16) return 3;
p.counter0++;
deviceindex0 = 0; //controller 1
deviceindex1 = 1; //controller 2
} else {
deviceidx = p.counter1;
if(deviceidx >= 16) return 3;
p.counter1++;
deviceindex0 = 2; //controller 3
deviceindex1 = 3; //controller 4
}
return (system.interface->input_poll(portnumber, p.device, deviceindex0, deviceidx) << 0)
| (system.interface->input_poll(portnumber, p.device, deviceindex1, deviceidx) << 1);
} //case Device::Multitap
case Device::Mouse: {
if(p.counter0 >= 32) return 1;
int position_x = system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::X); //-n = left, 0 = center, +n = right
int position_y = system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Y); //-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(portnumber, p.device, 0, (unsigned)MouseID::Right);
case 9: return system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Left);
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 Device::Mouse
case Device::SuperScope: {
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(portnumber, p.device, 0, (unsigned)SuperScopeID::Turbo);
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(portnumber, p.device, 0, (unsigned)SuperScopeID::Trigger);
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(portnumber, p.device, 0, (unsigned)SuperScopeID::Cursor);
//pause is a button; it is always edge sensitive
p.superscope.pause = false;
bool pause = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Pause);
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 Device::SuperScope
case Device::Justifier:
case Device::Justifiers: {
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(portnumber, p.device, 0, (unsigned)JustifierID::Trigger);
p.justifier.start1 = system.interface->input_poll(portnumber, p.device, 0, (unsigned)JustifierID::Start);
if(p.device == Device::Justifiers) {
p.justifier.trigger2 = system.interface->input_poll(portnumber, p.device, 1, (unsigned)JustifierID::Trigger);
p.justifier.start2 = system.interface->input_poll(portnumber, p.device, 1, (unsigned)JustifierID::Start);
} 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 Device::Justifier(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 Device::SuperScope: {
int x = system.interface->input_poll(1, p.device, 0, (unsigned)SuperScopeID::X);
int y = system.interface->input_poll(1, p.device, 0, (unsigned)SuperScopeID::Y);
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 Device::Justifier:
case Device::Justifiers: {
int x1 = system.interface->input_poll(1, p.device, 0, (unsigned)JustifierID::X);
int y1 = system.interface->input_poll(1, p.device, 0, (unsigned)JustifierID::Y);
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(1, p.device, 1, (unsigned)JustifierID::X);
int y2 = system.interface->input_poll(1, p.device, 1, (unsigned)JustifierID::Y);
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 == Device::Justifiers ? p.justifier.x2 : -1);
latchy = (p.device == Device::Justifiers ? p.justifier.y2 : -1);
}
} break;
switch(id) { default:
case Device::None: controller = new Controller(port); break;
case Device::Joypad: controller = new Gamepad(port); break;
case Device::Multitap: controller = new Multitap(port); break;
case Device::Mouse: controller = new Mouse(port); break;
case Device::SuperScope: controller = new SuperScope(port); break;
case Device::Justifier: controller = new Justifier(port, false); break;
case Device::Justifiers: controller = new Justifier(port, true); 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)
switch(port) {
case Controller::Port1: config.controller_port1 = id; break;
case Controller::Port2: config.controller_port2 = id; break;
}
}
void Input::port_set_device_(bool portnumber, Device 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 == Device::SuperScope
|| port[1].device == Device::Justifier
|| port[1].device == Device::Justifiers;
latchx = -1;
latchy = -1;
if(device == Device::SuperScope) {
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 == Device::Justifier) {
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 == Device::Justifiers) {
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;
}
Input::Input() {
connect(Controller::Port1, Input::Device::Joypad);
connect(Controller::Port2, Input::Device::Joypad);
}
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() {
Input::~Input() {
if(port1) delete port1;
if(port2) delete port2;
}
#endif

View File

@ -1,5 +1,4 @@
class Input {
public:
struct Input {
enum class Device : unsigned {
None,
Joypad,
@ -28,61 +27,12 @@ public:
X = 0, Y = 1, Trigger = 2, Start = 3,
};
uint8 port_read(bool port);
void port_set_device_(bool port, Device device);
void init();
void poll();
void update();
Controller *port1;
Controller *port2;
//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 && cpu.vcounter() == latchy && cpu.hcounter() == latchx) {
ppu.latch_counters();
}
}
private:
bool iobit;
int16_t latchx, latchy;
struct port_t {
Device 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;
friend class CPU;
void connect(bool port, Input::Device id);
Input();
~Input();
};
extern Input input1;
extern Input input;

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "079.05";
static const char Version[] = "079.06";
static const unsigned SerializerVersion = 21;
}
}
@ -130,8 +130,8 @@ namespace SNES {
#include "profile-performance.hpp"
#endif
#include <snes/system/system.hpp>
#include <snes/controller/controller.hpp>
#include <snes/system/system.hpp>
#include <snes/chip/chip.hpp>
#include <snes/cartridge/cartridge.hpp>
#include <snes/cheat/cheat.hpp>

View File

@ -21,7 +21,6 @@ void System::run() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
//input.update();
video.update();
}
}
@ -59,7 +58,6 @@ void System::runthreadtosave() {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
//input.update();
video.update();
}
}
@ -89,10 +87,9 @@ void System::init(Interface *interface_) {
video.init();
audio.init();
//input.init();
controllers.connect(0, config.controller_port1);
controllers.connect(1, config.controller_port2);
input.connect(0, config.controller_port1);
input.connect(1, config.controller_port2);
}
void System::term() {
@ -199,9 +196,8 @@ void System::power() {
if(cartridge.has_link()) cpu.coprocessors.append(&link);
scheduler.init();
controllers.connect(0, config.controller_port1);
controllers.connect(1, config.controller_port2);
//input.update();
input.connect(0, config.controller_port1);
input.connect(1, config.controller_port2);
}
void System::reset() {
@ -240,9 +236,8 @@ void System::reset() {
if(cartridge.has_link()) cpu.coprocessors.append(&link);
scheduler.init();
controllers.connect(0, config.controller_port1);
controllers.connect(1, config.controller_port2);
//input.update();
input.connect(0, config.controller_port1);
input.connect(1, config.controller_port2);
}
void System::scanline() {

View File

@ -49,15 +49,15 @@ void Video::draw_cursor(uint16_t color, int x, int y) {
void Video::update() {
switch(config.controller_port2) {
case Input::Device::SuperScope:
if(dynamic_cast<SuperScope*>(controllers.port2)) {
SuperScope &device = (SuperScope&)*controllers.port2;
if(dynamic_cast<SuperScope*>(input.port2)) {
SuperScope &device = (SuperScope&)*input.port2;
draw_cursor(0x7c00, device.x, device.y);
}
break;
case Input::Device::Justifier:
case Input::Device::Justifiers:
if(dynamic_cast<Justifier*>(controllers.port2)) {
Justifier &device = (Justifier&)*controllers.port2;
if(dynamic_cast<Justifier*>(input.port2)) {
Justifier &device = (Justifier&)*input.port2;
draw_cursor(0x001f, device.x1, device.y1);
if(device.chained == false) break;
draw_cursor(0x02e0, device.x2, device.y2);

View File

@ -40,20 +40,20 @@ void Utility::showMessage(const string &text) {
void Utility::setControllers() {
switch(config.controller.port1) {
case 0: SNES::controllers.connect(0, SNES::Input::Device::None); break;
case 1: SNES::controllers.connect(0, SNES::Input::Device::Joypad); break;
case 2: SNES::controllers.connect(0, SNES::Input::Device::Multitap); break;
case 3: SNES::controllers.connect(0, SNES::Input::Device::Mouse); break;
case 0: SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::None); break;
case 1: SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad); break;
case 2: SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Multitap); break;
case 3: SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Mouse); break;
}
switch(config.controller.port2) {
case 0: SNES::controllers.connect(1, SNES::Input::Device::None); break;
case 1: SNES::controllers.connect(1, SNES::Input::Device::Joypad); break;
case 2: SNES::controllers.connect(1, SNES::Input::Device::Multitap); break;
case 3: SNES::controllers.connect(1, SNES::Input::Device::Mouse); break;
case 4: SNES::controllers.connect(1, SNES::Input::Device::SuperScope); break;
case 5: SNES::controllers.connect(1, SNES::Input::Device::Justifier); break;
case 6: SNES::controllers.connect(1, SNES::Input::Device::Justifiers); break;
case 0: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::None); break;
case 1: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Joypad); break;
case 2: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Multitap); break;
case 3: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Mouse); break;
case 4: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::SuperScope); break;
case 5: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Justifier); break;
case 6: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Justifiers); break;
}
}