/ MCUME_teensy / teensy64 / cia2.cpp
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  }