libsidplayfp  1.8.3
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 
35 class PLA
36 {
37 public:
38  virtual void setCpuPort(int state) =0;
39  virtual uint8_t getLastReadByte() const =0;
40  virtual event_clock_t getPhi2Time() const =0;
41 
42 protected:
43  ~PLA() {}
44 };
45 
60 class ZeroRAMBank : public Bank
61 {
62 private:
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 
90 private:
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 
126 private:
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 
146 private:
147  // prevent copying
148  ZeroRAMBank(const ZeroRAMBank&);
149  ZeroRAMBank& operator=(const ZeroRAMBank&);
150 
151 public:
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
uint8_t peek(uint_least16_t address)
Definition: SystemRAMBank.h:56
void poke(uint_least16_t address, uint8_t value)
Definition: ZeroRAMBank.h:238
Definition: Bank.h:32
Definition: SystemRAMBank.h:35
Definition: ZeroRAMBank.h:35
uint8_t peek(uint_least16_t address)
Definition: ZeroRAMBank.h:185
Definition: ZeroRAMBank.h:60
void poke(uint_least16_t address, uint8_t value)
Definition: SystemRAMBank.h:61