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