libsidplayfp 1.8.8
ZeroRAMBank.h
1/*
2 * This file is part of libsidplayfp, a SID player engine.
3 *
4 * Copyright 2012-2015 Leandro Nini <drfiemost@users.sourceforge.net>
5 * Copyright 2010 Antti Lankila
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22#ifndef ZERORAMBANK_H
23#define ZERORAMBANK_H
24
25#include <stdint.h>
26
27#include "Bank.h"
28#include "SystemRAMBank.h"
29
30#include "sidplayfp/event.h"
31
35class PLA
36{
37public:
38 virtual void setCpuPort(int state) =0;
39 virtual uint8_t getLastReadByte() const =0;
40 virtual event_clock_t getPhi2Time() const =0;
41
42protected:
43 ~PLA() {}
44};
45
60class ZeroRAMBank : public Bank
61{
62private:
63/*
64 NOTE: fall-off cycles are heavily chip- and temperature dependent. as a
65 consequence it is very hard to find suitable realistic values that
66 always work and we can only tweak them based on testcases. (unless we
67 want to make it configurable or emulate temperature over time =))
68
69 it probably makes sense to tweak the values for a warmed up CPU, since
70 this is likely how (old) programs were coded and tested :)
71*/
72
73/* $01 bits 6 and 7 fall-off cycles (1->0), average is about 350 msec for a 6510
74 and about 1500 msec for a 8500 */
75/* NOTE: the unused bits of the 6510 seem to be much more temperature dependant
76 and the fall-off time decreases quicker and more drastically than on a
77 8500
78*/
79 static const event_clock_t C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES = 350000;
80 //static const event_clock_t C64_CPU8500_DATA_PORT_FALL_OFF_CYCLES = 1500000;
81/*
82 cpuports.prg from the lorenz testsuite will fail when the falloff takes more
83 than 1373 cycles. this suggests that he tested on a well warmed up c64 :)
84 he explicitly delays by ~1280 cycles and mentions capacitance, so he probably
85 even was aware of what happens.
86*/
87
88 static const bool tape_sense = false;
89
90private:
91 PLA* pla;
92
94 SystemRAMBank* ramBank;
95
97
98 event_clock_t dataSetClkBit6;
99 event_clock_t dataSetClkBit7;
101
103
104 bool dataFalloffBit6;
105 bool dataFalloffBit7;
107
109
110 uint8_t dataSetBit6;
111 uint8_t dataSetBit7;
113
115
116 uint8_t dir;
117 uint8_t data;
119
121 uint8_t dataRead;
122
124 uint8_t procPortPins;
125
126private:
127 void updateCpuPort()
128 {
129 // Update data pins for which direction is OUTPUT
130 procPortPins = (procPortPins & ~dir) | (data & dir);
131
132 dataRead = (data | ~dir) & (procPortPins | 0x17);
133
134 pla->setCpuPort((data | ~dir) & 0x07);
135
136 if ((dir & 0x20) == 0)
137 {
138 dataRead &= ~0x20;
139 }
140 if (tape_sense && (dir & 0x10) == 0)
141 {
142 dataRead &= ~0x10;
143 }
144 }
145
146private:
147 // prevent copying
148 ZeroRAMBank(const ZeroRAMBank&);
149 ZeroRAMBank& operator=(const ZeroRAMBank&);
150
151public:
152 ZeroRAMBank(PLA* pla, SystemRAMBank* ramBank) :
153 pla(pla),
154 ramBank(ramBank) {}
155
156 void reset()
157 {
158 dataFalloffBit6 = false;
159 dataFalloffBit7 = false;
160 dataSetBit6 = 0;
161 dataSetBit7 = 0;
162 dir = 0;
163 data = 0x3f;
164 dataRead = 0x3f;
165 procPortPins = 0x3f;
166 updateCpuPort();
167 }
168
169/*
170 $00/$01 unused bits emulation, as investigated by groepaz:
171
172 - There are 2 different unused bits, 1) the output bits, 2) the input bits
173 - The output bits can be (re)set when the data-direction is set to output
174 for those bits and the output bits will not drop-off to 0.
175 - When the data-direction for the unused bits is set to output then the
176 unused input bits can be (re)set by writing to them, when set to 1 the
177 drop-off timer will start which will cause the unused input bits to drop
178 down to 0 in a certain amount of time.
179 - When an unused input bit already had the drop-off timer running, and is
180 set to 1 again, the drop-off timer will restart.
181 - when a an unused bit changes from output to input, and the current output
182 bit is 1, the drop-off timer will restart again
183*/
184
185 uint8_t peek(uint_least16_t address)
186 {
187 switch (address)
188 {
189 case 0:
190 return dir;
191 case 1:
192 {
193 /* discharge the "capacitor" */
194 if (dataFalloffBit6 || dataFalloffBit7)
195 {
196 const event_clock_t phi2time = pla->getPhi2Time();
197
198 /* set real value of read bit 6 */
199 if (dataFalloffBit6 && dataSetClkBit6 < phi2time)
200 {
201 dataFalloffBit6 = false;
202 dataSetBit6 = 0;
203 }
204
205 /* set real value of read bit 7 */
206 if (dataFalloffBit7 && dataSetClkBit7 < phi2time)
207 {
208 dataFalloffBit7 = false;
209 dataSetBit7 = 0;
210 }
211 }
212
213 uint8_t retval = dataRead;
214
215 /* for unused bits in input mode, the value comes from the "capacitor" */
216
217 /* set real value of bit 6 */
218 if (!(dir & 0x40))
219 {
220 retval &= ~0x40;
221 retval |= dataSetBit6;
222 }
223
224 /* set real value of bit 7 */
225 if (!(dir & 0x80))
226 {
227 retval &= ~0x80;
228 retval |= dataSetBit7;
229 }
230
231 return retval;
232 }
233 default:
234 return ramBank->peek(address);
235 }
236 }
237
238 void poke(uint_least16_t address, uint8_t value)
239 {
240 switch (address)
241 {
242 case 0:
243 /* when switching an unused bit from output (where it contained a
244 * stable value) to input mode (where the input is floating), some
245 * of the charge is transferred to the floating input */
246
247 /* check if bit 6 has flipped from 1 to 0 */
248 if ((dir & 0x40) && !(value & 0x40))
249 {
250 dataSetClkBit6 = pla->getPhi2Time() + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES;
251 dataSetBit6 = data & 0x40;
252 dataFalloffBit6 = true;
253 }
254
255 /* check if bit 7 has flipped from 1 to 0 */
256 if ((dir & 0x80) && !(value & 0x80))
257 {
258 dataSetClkBit7 = pla->getPhi2Time() + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES;
259 dataSetBit7 = data & 0x80;
260 dataFalloffBit7 = true;
261 }
262
263 if (dir != value)
264 {
265 dir = value;
266 updateCpuPort();
267 }
268 value = pla->getLastReadByte();
269 break;
270 case 1:
271 /* when writing to an unused bit that is output, charge the "capacitor",
272 * otherwise don't touch it */
273
274 if (dir & 0x40)
275 {
276 dataSetBit6 = value & 0x40;
277 dataSetClkBit6 = pla->getPhi2Time() + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES;
278 dataFalloffBit6 = true;
279 }
280
281 if (dir & 0x80)
282 {
283 dataSetBit7 = value & 0x80;
284 dataSetClkBit7 = pla->getPhi2Time() + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES;
285 dataFalloffBit7 = true;
286 }
287
288 if (data != value)
289 {
290 data = value;
291 updateCpuPort();
292 }
293 value = pla->getLastReadByte();
294 break;
295 default:
296 break;
297 }
298
299 ramBank->poke(address, value);
300 }
301};
302
303#endif
Definition: Bank.h:33
Definition: ZeroRAMBank.h:36
Definition: SystemRAMBank.h:36
void poke(uint_least16_t address, uint8_t value)
Definition: SystemRAMBank.h:61
uint8_t peek(uint_least16_t address)
Definition: SystemRAMBank.h:56
Definition: ZeroRAMBank.h:61
void poke(uint_least16_t address, uint8_t value)
Definition: ZeroRAMBank.h:238
uint8_t peek(uint_least16_t address)
Definition: ZeroRAMBank.h:185