Path: blob/master/libsnes/bsnes/snes/controller/superscope/superscope.cpp
2 views
#ifdef CONTROLLER_CPP12//The Super Scope is a light-gun: it detects the CRT beam cannon position,3//and latches the counters by toggling iobit. This only works on controller4//port 2, as iobit there is connected to the PPU H/V counter latch.5//(PIO $4201.d7)67//It is obviously not possible to perfectly simulate an IR light detecting8//a CRT beam cannon, hence this class will read the PPU raster counters.910//A Super Scope can still technically be used in port 1, however it would11//require manual polling of PIO ($4201.d6) to determine when iobit was written.12//Note that no commercial game ever utilizes a Super Scope in port 1.1314void SuperScope::enter() {15unsigned prev = 0;16while(true) {17unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();1819if(offscreen == false) {20unsigned target = y * 1364 + (x + 24) * 4;21if(next >= target && prev < target) {22//CRT raster detected, toggle iobit to latch counters23iobit(0);24iobit(1);25}26}2728if(next < prev) {29//Vcounter wrapped back to zero; update cursor coordinates for start of new frame30int nx = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);31int ny = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);32nx += x;33ny += y;34x = max(-16, min(256 + 16, nx));35y = max(-16, min(240 + 16, ny));36offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));37}3839prev = next;40step(2);41}42}4344uint2 SuperScope::data() {45if(counter >= 8) return 1;4647if(counter == 0) {48//turbo is a switch; toggle is edge sensitive49bool newturbo = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);50if(newturbo && !turbo) {51turbo = !turbo; //toggle state52turbolock = true;53} else {54turbolock = false;55}5657//trigger is a button58//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive59trigger = false;60bool newtrigger = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);61if(newtrigger && (turbo || !triggerlock)) {62trigger = true;63triggerlock = true;64} else if(!newtrigger) {65triggerlock = false;66}6768//cursor is a button; it is always level sensitive69cursor = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);7071//pause is a button; it is always edge sensitive72pause = false;73bool newpause = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);74if(newpause && !pauselock) {75pause = true;76pauselock = true;77} else if(!newpause) {78pauselock = false;79}8081offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));82}8384switch(counter++) {85case 0: return offscreen ? 0 : trigger;86case 1: return cursor;87case 2: return turbo;88case 3: return pause;89case 4: return 0;90case 5: return 0;91case 6: return offscreen;92case 7: return 0; //noise (1 = yes)93}94}9596void SuperScope::latch(bool data) {97if(latched == data) return;98latched = data;99counter = 0;100}101102void SuperScope::serialize(serializer& s) {103Processor::serialize(s);104//Save block.105unsigned char block[Controller::SaveSize] = {0};106block[0] = latched ? 1 : 0;107block[1] = counter;108block[2] = trigger ? 1 : 0;109block[3] = cursor ? 1 : 0;110block[4] = turbo ? 1 : 0;111block[5] = pause ? 1 : 0;112block[6] = offscreen ? 1 : 0;113block[7] = (unsigned short)x >> 8;114block[8] = (unsigned short)x;115block[9] = (unsigned short)y >> 8;116block[10] = (unsigned short)y;117118s.array(block, Controller::SaveSize);119if(s.mode() == nall::serializer::Load) {120latched = (block[0] != 0);121counter = block[1];122trigger = (block[2] != 0);123cursor = (block[3] != 0);124turbo = (block[4] != 0);125pause = (block[5] != 0);126offscreen = (block[6] != 0);127x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);128y = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);129}130}131132133SuperScope::SuperScope(bool port) : Controller(port) {134create(Controller::Enter, 21477272);135latched = 0;136counter = 0;137138//center cursor onscreen139x = 256 / 2;140y = 240 / 2;141142trigger = false;143cursor = false;144turbo = false;145pause = false;146offscreen = false;147148turbolock = false;149triggerlock = false;150pauselock = false;151}152153#endif154155156