cia1.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 "cia1.h" 38 #include <string.h> 39 40 41 #define DEBUGCIA1 0 42 #define RTCDEBUG 0 43 44 #define decToBcd(x) ( ( (uint8_t) (x) / 10 * 16) | ((uint8_t) (x) % 10) ) 45 #define bcdToDec(x) ( ( (uint8_t) (x) / 16 * 10) | ((uint8_t) (x) % 16) ) 46 #define tod() (cpu.cia1.TODfrozen?cpu.cia1.TODfrozenMillis:(int)((millis() - cpu.cia1.TOD) % 86400000l)) 47 48 void cia1_setAlarmTime() { 49 cpu.cia1.TODAlarm = (cpu.cia1.W[0x08] + cpu.cia1.W[0x09] * 10l + cpu.cia1.W[0x0A] * 600l + cpu.cia1.W[0x0B] * 36000l); 50 } 51 52 void cia1_write(uint32_t address, uint8_t value) { 53 54 address &= 0x0F; 55 56 switch (address) { 57 case 0x04 : {cpu.cia1.W[address] = value;} ;break; //Timer A LO 58 case 0x05 : {cpu.cia1.W[address] = value; if ((cpu.cia1.R[0x0E] & 0x01) == 0) cpu.cia1.R[address]=value; };break;//Timer A HI 59 case 0x06 : {cpu.cia1.W[address] = value;} ;break; //Timer B LO 60 case 0x07 : {cpu.cia1.W[address] = value; if ((cpu.cia1.R[0x0F] & 0x01) == 0) cpu.cia1.R[address]=value; };break; //Timer B HI 61 62 //RTC 63 case 0x08 : { 64 if ((cpu.cia1.R[0x0f] & 0x80)>0) { 65 value &= 0x0f; 66 cpu.cia1.W[address] = value; 67 cia1_setAlarmTime(); 68 69 #if RTCDEBUG 70 Serial.print("CIA 1 Set Alarm TENTH:"); 71 Serial.println(value,HEX); 72 #endif 73 74 } else { 75 value &= 0x0f; 76 cpu.cia1.TODstopped=0; 77 //Translate set Time to TOD: 78 cpu.cia1.TOD = (int)(millis() % 86400000l) - (value * 100 + cpu.cia1.R[0x09] * 1000l + cpu.cia1.R[0x0A] * 60000l + cpu.cia1.R[0x0B] * 3600000l); 79 #if RTCDEBUG 80 Serial.print("CIA 1 Set TENTH:"); 81 Serial.println(value,HEX); 82 Serial.print("CIA 1 TOD (millis):"); 83 Serial.println(cpu.cia1.TOD); 84 #endif 85 } 86 }; 87 break; //TOD-Tenth 88 case 0x09 : { 89 if ((cpu.cia1.R[0x0f] & 0x80)>0) { 90 cpu.cia1.W[address] = bcdToDec(value); 91 cia1_setAlarmTime(); 92 #if RTCDEBUG 93 Serial.print("CIA 1 Set Alarm SEC:"); 94 Serial.println(value,HEX); 95 #endif 96 97 } else { 98 cpu.cia1.R[address] = bcdToDec(value); 99 #if RTCDEBUG 100 Serial.print("CIA 1 Set SEC:"); 101 Serial.println(value,HEX); 102 #endif 103 104 } 105 }; 106 break; //TOD-Secs 107 case 0x0A : { 108 if ((cpu.cia1.R[0x0f] & 0x80)>0) { 109 cpu.cia1.W[address] = bcdToDec(value); 110 cia1_setAlarmTime(); 111 #if RTCDEBUG 112 Serial.print("CIA 1 Set Alarm MIN:"); 113 Serial.println(value,HEX); 114 #endif 115 116 } else { 117 cpu.cia1.R[address] = bcdToDec(value); 118 #if RTCDEBUG 119 Serial.print("CIA 1 Set MIN:"); 120 Serial.println(value,HEX); 121 #endif 122 123 } 124 };break; //TOD-Minutes 125 case 0x0B : { 126 if ((cpu.cia1.R[0x0f] & 0x80)>0) { 127 cpu.cia1.W[address] = bcdToDec(value & 0x1f) + (value & 0x80?12:0); 128 cia1_setAlarmTime(); 129 #if RTCDEBUG 130 Serial.print("CIA 1 Set Alarm HRS:"); 131 Serial.println(value,HEX); 132 #endif 133 134 } else { 135 cpu.cia1.R[address] = bcdToDec(value & 0x1f) + (value & 0x80?12:0); 136 cpu.cia1.TODstopped=1; 137 #if RTCDEBUG 138 Serial.print("CIA 1 Set HRS:"); 139 Serial.println(value,HEX); 140 #endif 141 } 142 };break; //TOD-Hours 143 144 case 0x0C : { 145 cpu.cia1.R[address] = value; 146 //Fake IRQ 147 cpu.cia1.R[0x0d] |= 8 | ((cpu.cia1.W[0x0d] & 0x08) << 4); 148 } 149 ;break; 150 case 0x0D : { 151 if ((value & 0x80)>0) { 152 cpu.cia1.W[address] |= value & 0x1f; 153 //ggf IRQ triggern 154 if (cpu.cia1.R[address] & cpu.cia1.W[address] & 0x1f) { 155 cpu.cia1.R[address] |= 0x80; 156 }; 157 } else { 158 cpu.cia1.W[address] &= ~value; 159 } 160 161 }; 162 break; 163 case 0x0E : {cpu.cia1.R[address] = value & ~0x10; 164 if ((value & 0x10)>0) { cpu.cia1.R16[0x04/2] = cpu.cia1.W16[0x04/2]; } 165 }; 166 break; 167 case 0x0F : {cpu.cia1.R[address] = value & ~0x10; if ((value & 0x10)>0) { cpu.cia1.R16[0x06/2] = cpu.cia1.W16[0x06/2]; }};break; 168 default : {cpu.cia1.R[address] = value;/*if (address ==0) {Serial.print(value);Serial.print(" ");}*/ } break; 169 } 170 171 #if DEBUGCIA1 172 if (cpu.pc < 0xa000) Serial.printf("%x CIA1: W %x %x\n", cpu.pc, address, value); 173 #endif 174 } 175 176 uint8_t cia1_read(uint32_t address) { 177 uint8_t ret; 178 179 address &= 0x0F; 180 181 switch (address) { 182 case 0x00: {ret = cia1PORTA();};break; 183 case 0x01: {ret = cia1PORTB();};break; 184 //RTC 185 case 0x08: { 186 ret = tod() % 1000 / 10; 187 cpu.cia1.TODfrozen = 0; 188 }; 189 190 #if RTCDEBUG 191 Serial.print("CIA 1 Read TENTH:"); 192 Serial.println(ret,HEX); 193 #endif 194 195 break; //Bit 0..3: Zehntelsekunden im BCD-Format ($0-$9) Bit 4..7: immer 0 196 case 0x09: { 197 ret = decToBcd(tod() / 1000 % 60); 198 }; 199 //Serial.println( tod() / 100); 200 #if RTCDEBUG 201 Serial.print("CIA 1 Read SEC:"); 202 Serial.println(ret,HEX); 203 #endif 204 205 break; //Bit 0..3: Einersekunden im BCD-Format ($0-$9) Bit 4..6: Zehnersekunden im BCD-Format ($0-$5) Bit 7: immer 0 206 case 0x0A: { 207 ret = decToBcd(tod() / (1000 * 60) % 60); 208 }; 209 #if RTCDEBUG 210 Serial.print("CIA 1 Read MIN:"); 211 Serial.println(ret,HEX); 212 #endif 213 214 break; //Bit 0..3: Einerminuten im BCD-Format( $0-$9) Bit 4..6: Zehnerminuten im BCD-Format ($0-$5) Bit 7: immer 0 215 case 0x0B: { 216 //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 217 //Lesen aus diesem Register friert alle TOD-Register ein (TOD läuft aber weiter), bis Register 8 (TOD 10THS) gelesen wird. 218 cpu.cia1.TODfrozen = 0; 219 cpu.cia1.TODfrozenMillis = tod(); 220 cpu.cia1.TODfrozen = 1; 221 #if RTCDEBUG 222 Serial.print("CIA 1 FrozenMillis:"); 223 Serial.println(cpu.cia1.TODfrozenMillis); 224 #endif 225 ret = cpu.cia1.TODfrozenMillis / (1000 * 3600) % 24; 226 if (ret>=12) 227 ret = 128 | decToBcd(ret - 12); 228 else 229 ret = decToBcd(ret); 230 }; 231 #if RTCDEBUG 232 Serial.print("CIA 1 Read HRS:"); 233 Serial.println(ret,HEX); 234 #endif 235 236 break; 237 238 case 0x0D: {ret = cpu.cia1.R[address] & 0x9f; 239 cpu.cia1.R[address]=0; 240 }; 241 break; 242 243 default: ret = cpu.cia1.R[address];break; 244 } 245 246 #if DEBUGCIA1 247 if (cpu.pc < 0xa000) Serial.printf("%x CIA1: R %x %x\n", cpu.pc, address, ret); 248 #endif 249 return ret; 250 } 251 252 #if 0 253 void cia1_clock(int clk) { 254 255 uint32_t cnta, cntb, cra, crb; 256 257 //Timer A 258 cra = cpu.cia1.R[0x0e]; 259 crb = cpu.cia1.R[0x0f]; 260 261 if (( cra & 0x21) == 0x01) { 262 cnta = cpu.cia1.R[0x04] | cpu.cia1.R[0x05] << 8; 263 cnta -= clk; 264 if (cnta > 0xffff) { //Underflow 265 cnta = cpu.cia1.W[0x04] | cpu.cia1.W[0x05] << 8; // Reload Timer 266 if (cra & 0x08) { // One Shot 267 cpu.cia1.R[0x0e] &= 0xfe; //Stop timer 268 } 269 270 //Interrupt: 271 cpu.cia1.R[0x0d] |= 1 /*| (cpu.cia1.W[0x1a] & 0x01) */| ((cpu.cia1.W[0x0d] & 0x01) << 7); 272 273 if ((crb & 0x61)== 0x41) { //Timer B counts underflows of Timer A 274 cntb = cpu.cia1.R[0x06] | cpu.cia1.R[0x07] << 8; 275 cntb--; 276 if (cntb > 0xffff) { //underflow 277 cpu.cia1.R[0x04] = cnta; 278 cpu.cia1.R[0x05] = cnta >> 8; 279 goto underflow_b; 280 } 281 } 282 } 283 284 cpu.cia1.R[0x04] = cnta; 285 cpu.cia1.R[0x05] = cnta >> 8; 286 287 } 288 289 //Timer B 290 if (( crb & 0x61) == 0x01) { 291 cntb = cpu.cia1.R[0x06] | cpu.cia1.R[0x07] << 8; 292 cntb -= clk; 293 if (cntb > 0xffff) { //underflow 294 underflow_b: 295 cntb = cpu.cia1.W[0x06] | cpu.cia1.W[0x07] << 8; // Reload Timer 296 if (crb & 0x08) { // One Shot 297 cpu.cia1.R[0x0f] &= 0xfe; //Stop timer 298 } 299 300 //Interrupt: 301 cpu.cia1.R[0x0d] |= 2 /*| (cpu.cia1.W[0x1a] & 0x02) */ | ((cpu.cia1.W[0x0d] & 0x02) << 6); 302 303 } 304 305 cpu.cia1.R[0x06] = cntb; 306 cpu.cia1.R[0x07] = cntb >> 8; 307 308 } 309 } 310 #else 311 312 void cia1_clock(int clk) { 313 314 int32_t t; 315 uint32_t regFEDC = cpu.cia1.R32[0x0C/4]; 316 317 // TIMER A 318 //if (((cpu.cia1.R[0x0E] & 0x01)>0) && ((cpu.cia1.R[0x0E] & 0x20)==0)) { 319 320 //if ((regFEDC & 0x210000)==0x10000) { 321 if (((regFEDC>>16) & 0x21)==0x1) { 322 t = cpu.cia1.R16[0x04/2]; 323 324 if (clk > t) { //underflow ? 325 t = cpu.cia1.W16[0x04/2] - (clk - t); 326 regFEDC |= 0x00000100; 327 if ((regFEDC & 0x00080000)) regFEDC &= 0xfffeffff; //One-Shot 328 } 329 else { 330 t-=clk; 331 } 332 333 cpu.cia1.R16[0x04/2] = t; 334 } 335 336 337 // TIMER B 338 //TODO : Prüfen ob das funktioniert 339 if ( regFEDC & 0x01000000 ) { 340 //uint16_t quelle = (cpu.cia1.R[0x0F]>>5) & 0x03; 341 if ((regFEDC & 0x60000000) == 0x40000000) { 342 343 if (regFEDC & 0x00000100) //unterlauf TimerA? 344 clk = 1; 345 else 346 goto tend; 347 } 348 349 t = cpu.cia1.R16[0x06/2]; 350 351 if (clk > t) { //underflow ? 352 t = cpu.cia1.W16[0x06/2] - (clk - t); 353 regFEDC |= 0x00000200; 354 if ((regFEDC & 0x08000000)) regFEDC &= 0xfeffffff; 355 } else { 356 t -= clk; 357 } 358 cpu.cia1.R16[0x06/2] = t; //One-Shot 359 360 } 361 362 tend: 363 364 365 // INTERRUPT ? 366 if ( regFEDC & cpu.cia1.W32[0x0C/4] & 0x0f00 ) { 367 regFEDC |= 0x8000; 368 cpu.cia1.R32[0x0C/4]=regFEDC; 369 } 370 else cpu.cia1.R32[0x0C/4]=regFEDC; 371 } 372 373 #endif 374 375 void cia1_checkRTCAlarm() { // call @ 1/10 sec interval minimum 376 377 if ((int)(millis() - cpu.cia1.TOD) % 86400000l/100 == cpu.cia1.TODAlarm) { 378 //Serial.print("CIA1 RTC interrupt"); 379 cpu.cia1.R[13] |= 0x4 | ((cpu.cia1.W[13] & 4) << 5); 380 } 381 } 382 383 void cia1FLAG(void) { 384 //Serial.println("CIA1 FLAG interrupt"); 385 cpu.cia1.R[13] |= 0x10 | ((cpu.cia1.W[13] & 0x10) << 3); 386 } 387 388 void resetCia1(void) { 389 memset((uint8_t*)&cpu.cia1.R, 0, sizeof(cpu.cia1.R)); 390 cpu.cia1.W[0x04] = cpu.cia1.R[0x04] = 0xff; 391 cpu.cia1.W[0x05] = cpu.cia1.R[0x05] = 0xff; 392 cpu.cia1.W[0x06] = cpu.cia1.R[0x06] = 0xff; 393 cpu.cia1.W[0x07] = cpu.cia1.R[0x07] = 0xff; 394 395 //FLAG pin CIA1 - Serial SRQ (input only) 396 //pinMode(PIN_SERIAL_SRQ, OUTPUT_OPENDRAIN); 397 //digitalWriteFast(PIN_SERIAL_SRQ, 1); 398 //attachInterrupt(digitalPinToInterrupt(PIN_SERIAL_SRQ), cia1FLAG, FALLING); 399 } 400 401