libsidplayfp 1.0.3
|
00001 /* 00002 * This file is part of libsidplayfp, a SID player engine. 00003 * 00004 * Copyright 2012-2013 Leandro Nini <drfiemost@users.sourceforge.net> 00005 * Copyright 2010 Antti Lankila 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; either version 2 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with this program; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #ifndef ZERORAMBANK_H 00023 #define ZERORAMBANK_H 00024 00025 #include "Bank.h" 00026 #include "sidplayfp/event.h" 00027 00028 #include <stdint.h> 00029 00033 class PLA 00034 { 00035 public: 00036 virtual void setCpuPort(int state) =0; 00037 virtual uint8_t getLastReadByte() const =0; 00038 virtual event_clock_t getPhi2Time() const =0; 00039 }; 00040 00055 class ZeroRAMBank : public Bank 00056 { 00057 private: 00058 /* 00059 NOTE: fall-off cycles are heavily chip- and temperature dependent. as a 00060 consequence it is very hard to find suitable realistic values that 00061 always work and we can only tweak them based on testcases. (unless we 00062 want to make it configurable or emulate temperature over time =)) 00063 00064 it probably makes sense to tweak the values for a warmed up CPU, since 00065 this is likely how (old) programs were coded and tested :) 00066 */ 00067 00068 /* $01 bits 6 and 7 fall-off cycles (1->0), average is about 350 msec for a 6510 00069 and about 1500 msec for a 8500 */ 00070 /* NOTE: the unused bits of the 6510 seem to be much more temperature dependant 00071 and the fall-off time decreases quicker and more drastically than on a 00072 8500 00073 */ 00074 static const event_clock_t C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES = 350000; 00075 //static const event_clock_t C64_CPU8500_DATA_PORT_FALL_OFF_CYCLES = 1500000; 00076 /* 00077 cpuports.prg from the lorenz testsuite will fail when the falloff takes more 00078 than 1373 cycles. this suggests that he tested on a well warmed up c64 :) 00079 he explicitly delays by ~1280 cycles and mentions capacitance, so he probably 00080 even was aware of what happens. 00081 */ 00082 00083 static const bool tape_sense = false; 00084 00085 private: 00086 PLA* pla; 00087 00089 Bank* ramBank; 00090 00093 event_clock_t dataSetClkBit6; 00094 event_clock_t dataSetClkBit7; 00096 00101 bool dataFalloffBit6; 00102 bool dataFalloffBit7; 00104 00107 uint8_t dataSetBit6; 00108 uint8_t dataSetBit7; 00110 00113 uint8_t dir; 00114 uint8_t data; 00116 00118 uint8_t dataRead; 00119 00121 uint8_t procPortPins; 00122 00123 private: 00124 void updateCpuPort() 00125 { 00126 // Update data pins for which direction is OUTPUT 00127 procPortPins = (procPortPins & ~dir) | (data & dir); 00128 00129 dataRead = (data | ~dir) & (procPortPins | 0x17); 00130 00131 pla->setCpuPort((data | ~dir) & 0x07); 00132 00133 if ((dir & 0x20) == 0) 00134 { 00135 dataRead &= ~0x20; 00136 } 00137 if (tape_sense && (dir & 0x10) == 0) 00138 { 00139 dataRead &= ~0x10; 00140 } 00141 } 00142 00143 private: // prevent copying 00144 ZeroRAMBank(const ZeroRAMBank&); 00145 ZeroRAMBank& operator=(const ZeroRAMBank&); 00146 00147 public: 00148 ZeroRAMBank(PLA* pla, Bank* ramBank) : 00149 pla(pla), 00150 ramBank(ramBank) {} 00151 00152 void reset() 00153 { 00154 dataFalloffBit6 = false; 00155 dataFalloffBit7 = false; 00156 dir = 0; 00157 data = 0x3f; 00158 dataRead = 0x3f; 00159 procPortPins = 0x3f; 00160 updateCpuPort(); 00161 } 00162 00163 /* 00164 $00/$01 unused bits emulation, as investigated by groepaz: 00165 00166 - There are 2 different unused bits, 1) the output bits, 2) the input bits 00167 - The output bits can be (re)set when the data-direction is set to output 00168 for those bits and the output bits will not drop-off to 0. 00169 - When the data-direction for the unused bits is set to output then the 00170 unused input bits can be (re)set by writing to them, when set to 1 the 00171 drop-off timer will start which will cause the unused input bits to drop 00172 down to 0 in a certain amount of time. 00173 - When an unused input bit already had the drop-off timer running, and is 00174 set to 1 again, the drop-off timer will restart. 00175 - when a an unused bit changes from output to input, and the current output 00176 bit is 1, the drop-off timer will restart again 00177 */ 00178 00179 uint8_t peek(uint_least16_t address) 00180 { 00181 switch (address) 00182 { 00183 case 0: 00184 return dir; 00185 case 1: 00186 { 00187 /* discharge the "capacitor" */ 00188 if (dataFalloffBit6 || dataFalloffBit7) 00189 { 00190 const event_clock_t phi2time = pla->getPhi2Time(); 00191 00192 /* set real value of read bit 6 */ 00193 if (dataFalloffBit6 && dataSetClkBit6 < phi2time) 00194 { 00195 dataFalloffBit6 = false; 00196 dataSetBit6 = 0; 00197 } 00198 00199 /* set real value of read bit 7 */ 00200 if (dataFalloffBit7 && dataSetClkBit7 < phi2time) 00201 { 00202 dataFalloffBit7 = false; 00203 dataSetBit7 = 0; 00204 } 00205 } 00206 00207 uint8_t retval = dataRead; 00208 00209 /* for unused bits in input mode, the value comes from the "capacitor" */ 00210 00211 /* set real value of bit 6 */ 00212 if (!(dir & 0x40)) 00213 { 00214 retval &= ~0x40; 00215 retval |= dataSetBit6; 00216 } 00217 00218 /* set real value of bit 7 */ 00219 if (!(dir & 0x80)) 00220 { 00221 retval &= ~0x80; 00222 retval |= dataSetBit7; 00223 } 00224 00225 return retval; 00226 } 00227 default: 00228 return ramBank->peek(address); 00229 } 00230 } 00231 00232 void poke(uint_least16_t address, uint8_t value) 00233 { 00234 switch (address) 00235 { 00236 case 0: 00237 /* when switching an unused bit from output (where it contained a 00238 * stable value) to input mode (where the input is floating), some 00239 * of the charge is transferred to the floating input */ 00240 00241 /* check if bit 6 has flipped from 1 to 0 */ 00242 if ((dir & 0x40) && !(value & 0x40)) 00243 { 00244 dataSetClkBit6 = pla->getPhi2Time() + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES; 00245 dataSetBit6 = data & 0x40; 00246 dataFalloffBit6 = true; 00247 } 00248 00249 /* check if bit 7 has flipped from 1 to 0 */ 00250 if ((dir & 0x80) && !(value & 0x80)) 00251 { 00252 dataSetClkBit7 = pla->getPhi2Time() + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES; 00253 dataSetBit7 = data & 0x80; 00254 dataFalloffBit7 = true; 00255 } 00256 00257 if (dir != value) 00258 { 00259 dir = value; 00260 updateCpuPort(); 00261 } 00262 value = pla->getLastReadByte(); 00263 break; 00264 case 1: 00265 /* when writing to an unused bit that is output, charge the "capacitor", 00266 * otherwise don't touch it */ 00267 00268 if (dir & 0x40) 00269 { 00270 dataSetBit6 = value & 0x40; 00271 dataSetClkBit6 = pla->getPhi2Time() + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES; 00272 dataFalloffBit6 = true; 00273 } 00274 00275 if (dir & 0x80) 00276 { 00277 dataSetBit7 = value & 0x80; 00278 dataSetClkBit7 = pla->getPhi2Time() + C64_CPU6510_DATA_PORT_FALL_OFF_CYCLES; 00279 dataFalloffBit7 = true; 00280 } 00281 00282 if (data != value) 00283 { 00284 data = value; 00285 updateCpuPort(); 00286 } 00287 value = pla->getLastReadByte(); 00288 break; 00289 default: 00290 break; 00291 } 00292 00293 ramBank->poke(address, value); 00294 } 00295 }; 00296 00297 #endif