cia2.cpp
1 /* 2 Copyright Frank Bösing, 2017 3 4 This file is part of Teensy64. 5 6 Teensy64 is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 Teensy64 is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with Teensy64. If not, see <http://www.gnu.org/licenses/>. 18 19 Diese Datei ist Teil von Teensy64. 20 21 Teensy64 ist Freie Software: Sie können es unter den Bedingungen 22 der GNU General Public License, wie von der Free Software Foundation, 23 Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren 24 veröffentlichten Version, weiterverbreiten und/oder modifizieren. 25 26 Teensy64 wird in der Hoffnung, dass es nützlich sein wird, aber 27 OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite 28 Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. 29 Siehe die GNU General Public License für weitere Details. 30 31 Sie sollten eine Kopie der GNU General Public License zusammen mit diesem 32 Programm erhalten haben. Wenn nicht, siehe <http://www.gnu.org/licenses/>. 33 34 */ 35 36 #include "cpu.h" 37 #include "cia2.h" 38 #include <string.h> 39 40 #define DEBUGCIA2 0 41 #define RTCDEBUG 0 42 43 #define decToBcd(x) ( ( (uint8_t) (x) / 10 * 16) | ((uint8_t) (x) % 10) ) 44 #define bcdToDec(x) ( ( (uint8_t) (x) / 16 * 10) | ((uint8_t) (x) % 16) ) 45 #define tod() (cpu.cia2.TODfrozen?cpu.cia2.TODfrozenMillis:(int)((millis() - cpu.cia2.TOD) % 86400000l)) 46 47 void cia2_setAlarmTime() { 48 cpu.cia2.TODAlarm = (cpu.cia2.W[0x08] + cpu.cia2.W[0x09] * 10l + cpu.cia2.W[0x0A] * 600l + cpu.cia2.W[0x0B] * 36000l); 49 } 50 51 void cia2_write(uint32_t address, uint8_t value) { 52 53 address &= 0x0F; 54 55 switch (address) { 56 57 case 0x00 : { 58 if ((~value & 0x38)) { 59 cpu_setExactTiming(); 60 } 61 62 WRITE_ATN_CLK_DATA(value); 63 64 65 cpu.vic.bank = ((~value) & 0x03) * 16384; 66 vic_adrchange(); 67 cpu.cia2.R[address] = value; 68 } 69 break; 70 71 case 0x01 : 72 break;//Data PORTB 73 74 case 0x04 : { 75 cpu.cia2.W[address] = value; 76 } 77 break; //Timer A LO 78 case 0x05 : { 79 cpu.cia2.W[address] = value; 80 if ((cpu.cia2.R[0x0E] & 0x01) == 0) cpu.cia2.R[address] = value; 81 } 82 break; //Timer A HI 83 case 0x06 : { 84 cpu.cia2.W[address] = value; 85 } 86 break; //Timer B LO 87 case 0x07 : { 88 cpu.cia2.W[address] = value; 89 if ((cpu.cia2.R[0x0F] & 0x01) == 0) cpu.cia2.R[address] = value; 90 } 91 break; //Timer B HI 92 93 //RTC 94 case 0x08 : { 95 if ((cpu.cia2.R[0x0f] & 0x80) > 0) { 96 value &= 0x0f; 97 cpu.cia2.W[address] = value; 98 cia2_setAlarmTime(); 99 100 #if RTCDEBUG 101 Serial.print("CIA 2 Set Alarm TENTH:"); 102 Serial.println(value, HEX); 103 #endif 104 105 } else { 106 value &= 0x0f; 107 cpu.cia2.TODstopped = 0; 108 //Translate set Time to TOD: 109 cpu.cia2.TOD = (int)(millis() % 86400000l) - 110 (value * 100 + cpu.cia2.R[0x09] * 1000l + cpu.cia2.R[0x0A] * 60000l + cpu.cia2.R[0x0B] * 3600000l); 111 #if RTCDEBUG 112 Serial.print("CIA 2 Set TENTH:"); 113 Serial.println(value, HEX); 114 Serial.print("CIA 2 TOD (millis):"); 115 Serial.println(cpu.cia2.TOD); 116 #endif 117 } 118 } 119 break; //TOD-Tenth 120 case 0x09 : { 121 if ((cpu.cia2.R[0x0f] & 0x80) > 0) { 122 cpu.cia2.W[address] = bcdToDec(value); 123 cia2_setAlarmTime(); 124 #if RTCDEBUG 125 Serial.print("CIA 2 Set Alarm SEC:"); 126 Serial.println(value, HEX); 127 #endif 128 129 } else { 130 cpu.cia2.R[address] = bcdToDec(value); 131 #if RTCDEBUG 132 Serial.print("CIA 2 Set SEC:"); 133 Serial.println(value, HEX); 134 #endif 135 136 } 137 } 138 break; //TOD-Secs 139 case 0x0A : { 140 if ((cpu.cia2.R[0x0f] & 0x80) > 0) { 141 cpu.cia2.W[address] = bcdToDec(value); 142 cia2_setAlarmTime(); 143 #if RTCDEBUG 144 Serial.print("CIA 2 Set Alarm MIN:"); 145 Serial.println(value, HEX); 146 #endif 147 148 } else { 149 cpu.cia2.R[address] = bcdToDec(value); 150 #if RTCDEBUG 151 Serial.print("CIA 2 Set MIN:"); 152 Serial.println(value, HEX); 153 #endif 154 155 } 156 } 157 break; //TOD-Minutes 158 case 0x0B : { 159 if ((cpu.cia2.R[0x0f] & 0x80) > 0) { 160 cpu.cia2.W[address] = bcdToDec(value & 0x1f) + (value & 0x80 ? 12 : 0); 161 cia2_setAlarmTime(); 162 #if RTCDEBUG 163 Serial.print("CIA 2 Set Alarm HRS:"); 164 Serial.println(value, HEX); 165 #endif 166 167 } else { 168 cpu.cia2.R[address] = bcdToDec(value & 0x1f) + (value & 0x80 ? 12 : 0); 169 cpu.cia2.TODstopped = 1; 170 #if RTCDEBUG 171 Serial.print("CIA 2 Set HRS:"); 172 Serial.println(value, HEX); 173 #endif 174 } 175 } 176 break; //TOD-Hours 177 case 0x0C : { 178 cpu.cia2.R[address] = value; 179 //Fake IRQ 180 cpu.cia2.R[0x0d] |= 8 | ((cpu.cia2.W[0x0d] & 0x08) << 4); 181 cpu_nmi(); 182 } 183 break; 184 case 0x0D : { 185 if ((value & 0x80) > 0) { 186 cpu.cia2.W[address] |= value & 0x1f; 187 //ggf NMItriggern 188 if (cpu.cia2.R[address] & cpu.cia2.W[address] & 0x1f) { 189 cpu.cia2.R[address] |= 0x80; 190 cpu_nmi(); 191 }; 192 } else { 193 cpu.cia2.W[address] &= ~value; 194 } 195 } 196 break; 197 198 case 0x0E : { 199 cpu.cia2.R[address] = value & ~0x10; 200 if ((value & 0x10) > 0) { 201 cpu.cia2.R16[0x04 / 2] = cpu.cia2.W16[0x04 / 2]; 202 } 203 } 204 break; 205 case 0x0F : { 206 cpu.cia2.R[address] = value & ~0x10; 207 if ((value & 0x10) > 0) { 208 cpu.cia2.R16[0x06 / 2] = cpu.cia2.W16[0x06 / 2]; 209 } 210 } 211 break; 212 default : { 213 cpu.cia2.R[address] = value;/*if (address ==0) {Serial.print(value);Serial.print(" ");}*/ 214 } 215 break; 216 } 217 218 #if DEBUGCIA2 219 Serial.printf("%x CIA2: W %x %x\n", cpu.pc, address, value); 220 #endif 221 } 222 223 uint8_t cia2_read(uint32_t address) { 224 uint8_t ret; 225 226 address &= 0x0F; 227 228 switch (address) { 229 230 case 0x00 : { 231 ret = (cpu.cia2.R[address] & 0x3f) | READ_CLK_DATA(); 232 if ((~ret & 0x3f)) { 233 cpu_setExactTiming(); 234 } 235 236 break; 237 } 238 239 //RTC 240 case 0x08: { 241 ret = tod() % 1000 / 10; 242 cpu.cia2.TODfrozen = 0; 243 }; 244 245 #if RTCDEBUG 246 Serial.print("CIA 2 Read TENTH:"); 247 Serial.println(ret, HEX); 248 #endif 249 250 break; //Bit 0..3: Zehntelsekunden im BCD-Format ($0-$9) Bit 4..7: immer 0 251 case 0x09: { 252 ret = decToBcd(tod() / 1000 % 60); 253 }; 254 //Serial.println( tod() / 100); 255 #if RTCDEBUG 256 Serial.print("CIA 2 Read SEC:"); 257 Serial.println(ret, HEX); 258 #endif 259 260 break; //Bit 0..3: Einersekunden im BCD-Format ($0-$9) Bit 4..6: Zehnersekunden im BCD-Format ($0-$5) Bit 7: immer 0 261 case 0x0A: { 262 ret = decToBcd(tod() / (1000 * 60) % 60); 263 }; 264 #if RTCDEBUG 265 Serial.print("CIA 2 Read MIN:"); 266 Serial.println(ret, HEX); 267 #endif 268 269 break; //Bit 0..3: Einerminuten im BCD-Format( $0-$9) Bit 4..6: Zehnerminuten im BCD-Format ($0-$5) Bit 7: immer 0 270 case 0x0B: { 271 //Bit 0..3: Einerstunden im BCD-Format ($0-$9) Bit 4: Zehnerstunden im BCD-Format ($0-$1) // Bit 7: Unterscheidung AM/PM, 0=AM, 1=PM 272 //Lesen aus diesem Register friert alle TOD-Register ein (TOD läuft aber weiter), bis Register 8 (TOD 10THS) gelesen wird. 273 cpu.cia2.TODfrozen = 0; 274 cpu.cia2.TODfrozenMillis = tod(); 275 cpu.cia2.TODfrozen = 1; 276 #if RTCDEBUG 277 Serial.print("CIA 2 FrozenMillis:"); 278 Serial.println(cpu.cia2.TODfrozenMillis); 279 #endif 280 ret = cpu.cia2.TODfrozenMillis / (1000 * 3600) % 24; 281 if (ret >= 12) 282 ret = 128 | decToBcd(ret - 12); 283 else 284 ret = decToBcd(ret); 285 }; 286 #if RTCDEBUG 287 Serial.print("CIA 2 Read HRS:"); 288 Serial.println(ret, HEX); 289 #endif 290 291 break; 292 293 case 0x0D: { 294 ret = cpu.cia2.R[address] & 0x9f; 295 cpu.cia2.R[address] = 0; 296 cpu_clearNmi(); 297 }; break; 298 default: ret = cpu.cia2.R[address]; break; 299 } 300 301 #if DEBUGCIA2 302 Serial.printf("%x CIA2: R %x %x\n", cpu.pc, address, ret); 303 #endif 304 return ret; 305 } 306 307 #if 0 308 void cia2_clock(int clk) { 309 310 uint32_t cnta, cntb, cra, crb; 311 312 //Timer A 313 cra = cpu.cia2.R[0x0e]; 314 crb = cpu.cia2.R[0x0f]; 315 316 if (( cra & 0x21) == 0x01) { 317 cnta = cpu.cia2.R[0x04] | cpu.cia2.R[0x05] << 8; 318 cnta -= clk; 319 if (cnta > 0xffff) { //Underflow 320 cnta = cpu.cia2.W[0x04] | cpu.cia2.W[0x05] << 8; // Reload Timer 321 if (cra & 0x08) { // One Shot 322 cpu.cia2.R[0x0e] &= 0xfe; //Stop timer 323 } 324 325 //Interrupt: 326 cpu.cia2.R[0x0d] |= 1 | /* (cpu.cia2.W[0x0d] & 0x01) |*/ ((cpu.cia2.W[0x0d] & 0x01) << 7); 327 328 if ((crb & 0x61) == 0x41) { //Timer B counts underflows of Timer A 329 cntb = cpu.cia2.R[0x06] | cpu.cia2.R[0x07] << 8; 330 cntb--; 331 if (cntb > 0xffff) { //underflow 332 cpu.cia2.R[0x04] = cnta & 0x0f; 333 cpu.cia2.R[0x05] = cnta >> 8; 334 goto underflow_b; 335 } 336 } 337 } 338 339 cpu.cia2.R[0x04] = cnta & 0x0f; 340 cpu.cia2.R[0x05] = cnta >> 8; 341 342 } 343 344 //Timer B 345 if (( crb & 0x61) == 0x01) { 346 cntb = cpu.cia2.R[0x06] | cpu.cia2.R[0x07] << 8; 347 cntb -= clk; 348 if (cntb > 0xffff) { //underflow 349 underflow_b: 350 cntb = cpu.cia2.W[0x06] | cpu.cia2.W[0x07] << 8; // Reload Timer 351 if (crb & 0x08) { // One Shot 352 cpu.cia2.R[0x0f] &= 0xfe; //Stop timer 353 } 354 355 //Interrupt: 356 cpu.cia2.R[0x0d] |= 2 | /*(cpu.cia2.W[0x0d] & 0x02) | */ ((cpu.cia2.W[0x0d] & 0x02) << 6); 357 } 358 359 cpu.cia2.R[0x06] = cntb & 0x0f; 360 cpu.cia2.R[0x07] = cntb >> 8; 361 362 } 363 if (cpu.cia2.R[0x0d] & 0x80) cpu_nmi(); 364 } 365 366 #else 367 368 void cia2_clock(int clk) { 369 370 int32_t t; 371 uint32_t regFEDC = cpu.cia2.R32[0x0C / 4]; 372 373 // TIMER A 374 //if (((cpu.cia2.R[0x0E] & 0x01)>0) && ((cpu.cia2.R[0x0E] & 0x20)==0)) { 375 //if ((regFEDC & 0x210000)==0x10000) { 376 if (((regFEDC >> 16) & 0x21) == 0x1) { 377 378 t = cpu.cia2.R16[0x04 / 2]; 379 380 if (clk > t) { //underflow 381 t = cpu.cia2.W16[0x04 / 2] - (clk - t); //neu 382 regFEDC |= 0x00000100; 383 if ((regFEDC & 0x00080000)) regFEDC &= 0xfffeffff; 384 } 385 else { 386 t -= clk; 387 } 388 389 cpu.cia2.R16[0x04 / 2] = t; 390 } 391 392 393 // TIMER B 394 //TODO : Prüfen ob das funktioniert 395 if ( regFEDC & 0x01000000 ) { 396 //uint16_t quelle = (cpu.cia2.R[0x0F]>>5) & 0x03; 397 if ((regFEDC & 0x60000000) == 0x40000000) { 398 399 if (regFEDC & 0x00000100) //unterlauf TimerA? 400 clk = 1; 401 else 402 goto tend; 403 } 404 405 t = cpu.cia2.R16[0x06 / 2]; 406 407 if (clk > t) { //underflow 408 t = cpu.cia2.W16[0x06 / 2] - (clk - t); //Neu 409 regFEDC |= 0x00000200; 410 if ((regFEDC & 0x08000000)) regFEDC &= 0xfeffffff; 411 } else { 412 t -= clk; 413 } 414 cpu.cia2.R16[0x06 / 2] = t; 415 416 } 417 418 tend: 419 420 421 // INTERRUPT ? 422 if ( regFEDC & cpu.cia2.W32[0x0C / 4] & 0x0f00 ) { 423 regFEDC |= 0x8000; 424 cpu.cia2.R32[0x0C / 4] = regFEDC; 425 } 426 cpu.cia2.R32[0x0C / 4] = regFEDC; 427 } 428 #endif 429 430 void cia2_checkRTCAlarm() { // call every 1/10 sec minimum 431 if ((int)(millis() - cpu.cia2.TOD) % 86400000l / 100 == cpu.cia2.TODAlarm) { 432 // Serial.print("CIA2 RTC interrupt"); 433 // Interrupt 434 cpu.cia2.R[0x0d] |= 0x4 | (cpu.cia2.W[0x0d] & 4) << 5; 435 } 436 } 437 438 void resetCia2(void) { 439 memset((uint8_t*)&cpu.cia2.R, 0, sizeof(cpu.cia2.R)); 440 cpu.cia2.R[0x04] = 0xff; 441 cpu.cia2.R[0x05] = 0xff; 442 cpu.cia2.R[0x06] = 0xff; 443 cpu.cia2.R[0x07] = 0xff; 444 445 //pinMode(PIN_SERIAL_ATN, OUTPUT_OPENDRAIN); //ATN OUT (CIA2 PA3 OUT) 446 //pinMode(PIN_SERIAL_CLK, OUTPUT_OPENDRAIN); //CLK (CIA2 PA6:IN PA4: OUT) 447 //pinMode(PIN_SERIAL_DATA, OUTPUT_OPENDRAIN); //DATA (CIA2 PA7:IN PA5: OUT) 448 //digitalWriteFast(PIN_SERIAL_ATN, 1); 449 //digitalWriteFast(PIN_SERIAL_CLK, 1); 450 //digitalWriteFast(PIN_SERIAL_DATA, 1); 451 452 }