Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/snes/controller/superscope/superscope.cpp
2 views
1
#ifdef CONTROLLER_CPP
2
3
//The Super Scope is a light-gun: it detects the CRT beam cannon position,
4
//and latches the counters by toggling iobit. This only works on controller
5
//port 2, as iobit there is connected to the PPU H/V counter latch.
6
//(PIO $4201.d7)
7
8
//It is obviously not possible to perfectly simulate an IR light detecting
9
//a CRT beam cannon, hence this class will read the PPU raster counters.
10
11
//A Super Scope can still technically be used in port 1, however it would
12
//require manual polling of PIO ($4201.d6) to determine when iobit was written.
13
//Note that no commercial game ever utilizes a Super Scope in port 1.
14
15
void SuperScope::enter() {
16
unsigned prev = 0;
17
while(true) {
18
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
19
20
if(offscreen == false) {
21
unsigned target = y * 1364 + (x + 24) * 4;
22
if(next >= target && prev < target) {
23
//CRT raster detected, toggle iobit to latch counters
24
iobit(0);
25
iobit(1);
26
}
27
}
28
29
if(next < prev) {
30
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
31
int nx = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
32
int ny = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
33
nx += x;
34
ny += y;
35
x = max(-16, min(256 + 16, nx));
36
y = max(-16, min(240 + 16, ny));
37
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
38
}
39
40
prev = next;
41
step(2);
42
}
43
}
44
45
uint2 SuperScope::data() {
46
if(counter >= 8) return 1;
47
48
if(counter == 0) {
49
//turbo is a switch; toggle is edge sensitive
50
bool newturbo = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);
51
if(newturbo && !turbo) {
52
turbo = !turbo; //toggle state
53
turbolock = true;
54
} else {
55
turbolock = false;
56
}
57
58
//trigger is a button
59
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
60
trigger = false;
61
bool newtrigger = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);
62
if(newtrigger && (turbo || !triggerlock)) {
63
trigger = true;
64
triggerlock = true;
65
} else if(!newtrigger) {
66
triggerlock = false;
67
}
68
69
//cursor is a button; it is always level sensitive
70
cursor = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);
71
72
//pause is a button; it is always edge sensitive
73
pause = false;
74
bool newpause = interface()->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);
75
if(newpause && !pauselock) {
76
pause = true;
77
pauselock = true;
78
} else if(!newpause) {
79
pauselock = false;
80
}
81
82
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
83
}
84
85
switch(counter++) {
86
case 0: return offscreen ? 0 : trigger;
87
case 1: return cursor;
88
case 2: return turbo;
89
case 3: return pause;
90
case 4: return 0;
91
case 5: return 0;
92
case 6: return offscreen;
93
case 7: return 0; //noise (1 = yes)
94
}
95
}
96
97
void SuperScope::latch(bool data) {
98
if(latched == data) return;
99
latched = data;
100
counter = 0;
101
}
102
103
void SuperScope::serialize(serializer& s) {
104
Processor::serialize(s);
105
//Save block.
106
unsigned char block[Controller::SaveSize] = {0};
107
block[0] = latched ? 1 : 0;
108
block[1] = counter;
109
block[2] = trigger ? 1 : 0;
110
block[3] = cursor ? 1 : 0;
111
block[4] = turbo ? 1 : 0;
112
block[5] = pause ? 1 : 0;
113
block[6] = offscreen ? 1 : 0;
114
block[7] = (unsigned short)x >> 8;
115
block[8] = (unsigned short)x;
116
block[9] = (unsigned short)y >> 8;
117
block[10] = (unsigned short)y;
118
119
s.array(block, Controller::SaveSize);
120
if(s.mode() == nall::serializer::Load) {
121
latched = (block[0] != 0);
122
counter = block[1];
123
trigger = (block[2] != 0);
124
cursor = (block[3] != 0);
125
turbo = (block[4] != 0);
126
pause = (block[5] != 0);
127
offscreen = (block[6] != 0);
128
x = (short)(((unsigned short)block[7] << 8) | (unsigned short)block[8]);
129
y = (short)(((unsigned short)block[9] << 8) | (unsigned short)block[10]);
130
}
131
}
132
133
134
SuperScope::SuperScope(bool port) : Controller(port) {
135
create(Controller::Enter, 21477272);
136
latched = 0;
137
counter = 0;
138
139
//center cursor onscreen
140
x = 256 / 2;
141
y = 240 / 2;
142
143
trigger = false;
144
cursor = false;
145
turbo = false;
146
pause = false;
147
offscreen = false;
148
149
turbolock = false;
150
triggerlock = false;
151
pauselock = false;
152
}
153
154
#endif
155
156