libsidplayfp  2.0.2
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 2009-2014 VICE Project
6  * Copyright 2010 Antti Lankila
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22 
23 #ifndef ZERORAMBANK_H
24 #define ZERORAMBANK_H
25 
26 #include <stdint.h>
27 
28 #include "Bank.h"
29 #include "SystemRAMBank.h"
30 
31 #include "Event.h"
32 
33 #include "sidcxx11.h"
34 
35 namespace libsidplayfp
36 {
37 
41 class PLA
42 {
43 public:
44  virtual void setCpuPort(uint8_t state) =0;
45  virtual uint8_t getLastReadByte() const =0;
46  virtual event_clock_t getPhi2Time() const =0;
47 
48 protected:
49  ~PLA() {}
50 };
51 
67 template <int Bit>
68 class dataBit
69 {
70 private:
93  static event_clock_t const C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES = 350000;
94  static event_clock_t const C64_CPU8500_DATA_PORT_FALL_OFF_CYCLES = 1500000; // Curently unused
96 
97 private:
99  event_clock_t dataSetClk;
100 
102  bool isFallingOff;
103 
105  uint8_t dataSet;
106 
107 public:
108  void reset()
109  {
110  isFallingOff = false;
111  dataSet = 0;
112  }
113 
114  uint8_t readBit(event_clock_t phi2time)
115  {
116  if (isFallingOff && dataSetClk < phi2time)
117  {
118  // discharge the "capacitor"
119  reset();
120  }
121  return dataSet;
122  }
123 
124  void writeBit(event_clock_t phi2time, uint8_t value)
125  {
126  dataSetClk = phi2time + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES;
127  dataSet = value & (1 << Bit);
128  isFallingOff = true;
129  }
130 };
131 
144 class ZeroRAMBank final : public Bank
145 {
146 private:
147  // not emulated
148  static bool const tape_sense = false;
149 
150 private:
151  PLA &pla;
152 
154  SystemRAMBank &ramBank;
155 
157 
158  dataBit<6> dataBit6;
159  dataBit<7> dataBit7;
161 
163 
164  uint8_t dir;
165  uint8_t data;
167 
169  uint8_t dataRead;
170 
172  uint8_t procPortPins;
173 
174 private:
175  void updateCpuPort()
176  {
177  // Update data pins for which direction is OUTPUT
178  procPortPins = (procPortPins & ~dir) | (data & dir);
179 
180  dataRead = (data | ~dir) & (procPortPins | 0x17);
181 
182  pla.setCpuPort((data | ~dir) & 0x07);
183 
184  if ((dir & 0x20) == 0)
185  {
186  dataRead &= ~0x20;
187  }
188  if (tape_sense && (dir & 0x10) == 0)
189  {
190  dataRead &= ~0x10;
191  }
192  }
193 
194 public:
195  ZeroRAMBank(PLA &pla, SystemRAMBank &ramBank) :
196  pla(pla),
197  ramBank(ramBank)
198  {}
199 
200  void reset()
201  {
202  dataBit6.reset();
203  dataBit7.reset();
204 
205  dir = 0;
206  data = 0x3f;
207  dataRead = 0x3f;
208  procPortPins = 0x3f;
209 
210  updateCpuPort();
211  }
212 
213  uint8_t peek(uint_least16_t address) override
214  {
215  switch (address)
216  {
217  case 0:
218  return dir;
219  case 1:
220  {
221  uint8_t retval = dataRead;
222 
223  // for unused bits in input mode, the value comes from the "capacitor"
224 
225  // set real value of bit 6
226  if (!(dir & 0x40))
227  {
228  retval &= ~0x40;
229  retval |= dataBit6.readBit(pla.getPhi2Time());
230  }
231 
232  // set real value of bit 7
233  if (!(dir & 0x80))
234  {
235  retval &= ~0x80;
236  retval |= dataBit7.readBit(pla.getPhi2Time());
237  }
238 
239  return retval;
240  }
241  default:
242  return ramBank.peek(address);
243  }
244  }
245 
246  void poke(uint_least16_t address, uint8_t value) override
247  {
248  switch (address)
249  {
250  case 0:
251  // when switching an unused bit from output (where it contained a
252  // stable value) to input mode (where the input is floating), some
253  // of the charge is transferred to the floating input
254 
255  if (dir != value)
256  {
257  // check if bit 6 has flipped from 1 to 0
258  if ((dir & 0x40) && !(value & 0x40))
259  dataBit6.writeBit(pla.getPhi2Time(), data);
260 
261  // check if bit 7 has flipped from 1 to 0
262  if ((dir & 0x80) && !(value & 0x80))
263  dataBit7.writeBit(pla.getPhi2Time(), data);
264 
265  dir = value;
266  updateCpuPort();
267  }
268 
269  value = pla.getLastReadByte();
270  break;
271  case 1:
272  // when writing to an unused bit that is output, charge the "capacitor",
273  // otherwise don't touch it
274 
275  if (dir & 0x40)
276  dataBit6.writeBit(pla.getPhi2Time(), value);
277 
278  if (dir & 0x80)
279  dataBit7.writeBit(pla.getPhi2Time(), value);
280 
281  if (data != value)
282  {
283  data = value;
284  updateCpuPort();
285  }
286 
287  value = pla.getLastReadByte();
288  break;
289  default:
290  break;
291  }
292 
293  ramBank.poke(address, value);
294  }
295 };
296 
297 }
298 
299 #endif
libsidplayfp::Bank
Definition: Bank.h:36
libsidplayfp::ZeroRAMBank
Definition: ZeroRAMBank.h:145
libsidplayfp::dataBit
Definition: ZeroRAMBank.h:69
libsidplayfp::SystemRAMBank
Definition: SystemRAMBank.h:41
libsidplayfp::SystemRAMBank::peek
uint8_t peek(uint_least16_t address) override
Definition: SystemRAMBank.h:61
libsidplayfp::ZeroRAMBank::peek
uint8_t peek(uint_least16_t address) override
Definition: ZeroRAMBank.h:213
libsidplayfp::PLA
Definition: ZeroRAMBank.h:42
libsidplayfp::ZeroRAMBank::poke
void poke(uint_least16_t address, uint8_t value) override
Definition: ZeroRAMBank.h:246
libsidplayfp::SystemRAMBank::poke
void poke(uint_least16_t address, uint8_t value) override
Definition: SystemRAMBank.h:66