1 //both timer functions can call eachother define them here
2 static void timer1(uint8_t reason, double sysclks);
3 static void timer2(uint8_t reason, double sysclks);
4 
timer1(uint8_t reason,double sysclks)5 static void timer1(uint8_t reason, double sysclks){
6    uint16_t timer1Control = registerArrayRead16(TCTL1);
7    uint16_t timer1Compare = registerArrayRead16(TCMP1);
8    double timer1OldCount = timerCycleCounter[0];
9    double timer1Prescaler = (registerArrayRead16(TPRER1) & 0x00FF) + 1;
10    bool timer1Enabled = timer1Control & 0x0001;
11 
12    if(timer1Enabled){
13       switch((timer1Control & 0x000E) >> 1){
14          case 0x0000://stop counter
15             //do nothing
16             return;
17 
18          case 0x0001://SYSCLK / timer prescaler
19             if(reason != DBVZ_TIMER_REASON_SYSCLK)
20                return;
21             timerCycleCounter[0] += sysclks / timer1Prescaler;
22             break;
23 
24          case 0x0002://SYSCLK / 16 / timer prescaler
25             if(reason != DBVZ_TIMER_REASON_SYSCLK)
26                return;
27             timerCycleCounter[0] += sysclks / 16.0 / timer1Prescaler;
28             break;
29 
30          case 0x0003://TIN/TOUT pin / timer prescaler, the other timer can be attached to TIN/TOUT
31             if(reason != DBVZ_TIMER_REASON_TIN)
32                return;
33             timerCycleCounter[0] += 1.0 / timer1Prescaler;
34             break;
35 
36          default://CLK32 / timer prescaler
37             if(reason != DBVZ_TIMER_REASON_CLK32)
38                return;
39             timerCycleCounter[0] += 1.0 / timer1Prescaler;
40             break;
41       }
42 
43       if(timer1OldCount < timer1Compare && timerCycleCounter[0] >= timer1Compare){
44          //the comparison against the old value is to prevent an interrupt on every increment in free running mode
45          //the timer is not cycle accurate and may not hit the value in the compare register perfectly so check if it would have during in the emulated time
46          uint8_t pcrTinToutConfig = registerArrayRead8(PCR) & 0x03;//TIN/TOUT seems not to be physicaly connected but cascaded timers still need to be supported
47 
48          //interrupt enabled
49          if(timer1Control & 0x0010)
50             setIprIsrBit(DBVZ_INT_TMR1);
51          //checkInterrupts() is run when the clock that called this function is finished
52 
53          //set timer triggered bit
54          registerArrayWrite16(TSTAT1, registerArrayRead16(TSTAT1) | 0x0001);
55          timerStatusReadAcknowledge[0] &= 0xFFFE;//lock bit until next read
56 
57          //increment other timer if enabled
58          if(pcrTinToutConfig == 0x03)
59             timer2(DBVZ_TIMER_REASON_TIN, 0);
60 
61          //not free running, reset to 0, to prevent loss of ticks after compare event just subtract timerXCompare
62          if(!(timer1Control & 0x0100))
63             timerCycleCounter[0] -= timer1Compare;
64       }
65 
66       if(timerCycleCounter[0] > 0xFFFF)
67          timerCycleCounter[0] -= 0xFFFF;
68       registerArrayWrite16(TCN1, (uint16_t)timerCycleCounter[0]);
69    }
70 }
71 
timer2(uint8_t reason,double sysclks)72 static void timer2(uint8_t reason, double sysclks){
73    uint16_t timer2Control = registerArrayRead16(TCTL2);
74    uint16_t timer2Compare = registerArrayRead16(TCMP2);
75    double timer2OldCount = timerCycleCounter[1];
76    double timer2Prescaler = (registerArrayRead16(TPRER2) & 0x00FF) + 1;
77    bool timer2Enabled = timer2Control & 0x0001;
78 
79    if(timer2Enabled){
80       switch((timer2Control & 0x000E) >> 1){
81          case 0x0000://stop counter
82             //do nothing
83             return;
84 
85          case 0x0001://SYSCLK / timer prescaler
86             if(reason != DBVZ_TIMER_REASON_SYSCLK)
87                return;
88             timerCycleCounter[1] += sysclks / timer2Prescaler;
89             break;
90 
91          case 0x0002://SYSCLK / 16 / timer prescaler
92             if(reason != DBVZ_TIMER_REASON_SYSCLK)
93                return;
94             timerCycleCounter[1] += sysclks / 16.0 / timer2Prescaler;
95             break;
96 
97          case 0x0003://TIN/TOUT pin / timer prescaler, the other timer can be attached to TIN/TOUT
98             if(reason != DBVZ_TIMER_REASON_TIN)
99                return;
100             timerCycleCounter[1] += 1.0 / timer2Prescaler;
101             break;
102 
103          default://CLK32 / timer prescaler
104             if(reason != DBVZ_TIMER_REASON_CLK32)
105                return;
106             timerCycleCounter[1] += 1.0 / timer2Prescaler;
107             break;
108       }
109 
110       if(timer2OldCount < timer2Compare && timerCycleCounter[1] >= timer2Compare){
111          //the comparison against the old value is to prevent an interrupt on every increment in free running mode
112          //the timer is not cycle accurate and may not hit the value in the compare register perfectly so check if it would have during in the emulated time
113          uint8_t pcrTinToutConfig = registerArrayRead8(PCR) & 0x03;//TIN/TOUT seems not to be physicaly connected but cascaded timers still need to be supported
114 
115          //interrupt enabled
116          if(timer2Control & 0x0010)
117             setIprIsrBit(DBVZ_INT_TMR2);
118          //checkInterrupts() is run when the clock that called this function is finished
119 
120          //set timer triggered bit
121          registerArrayWrite16(TSTAT2, registerArrayRead16(TSTAT2) | 0x0001);
122          timerStatusReadAcknowledge[1] &= 0xFFFE;//lock bit until next read
123 
124          //increment other timer if enabled
125          if(pcrTinToutConfig == 0x02)
126             timer1(DBVZ_TIMER_REASON_TIN, 0);
127 
128          //not free running, reset to 0, to prevent loss of ticks after compare event just subtract timerXCompare
129          if(!(timer2Control & 0x0100))
130             timerCycleCounter[1] -= timer2Compare;
131       }
132 
133       if(timerCycleCounter[1] > 0xFFFF)
134          timerCycleCounter[1] -= 0xFFFF;
135       registerArrayWrite16(TCN2, (uint16_t)timerCycleCounter[1]);
136    }
137 }
138 
dmaclksPerClk32(void)139 static double dmaclksPerClk32(void){
140    uint16_t pllcr = registerArrayRead16(PLLCR);
141    uint16_t pllfsr = registerArrayRead16(PLLFSR);
142    uint8_t p = pllfsr & 0x00FF;
143    uint8_t q = pllfsr >> 8 & 0x000F;
144    double dmaclks = 2.0 * (14.0 * (p + 1.0) + q + 1.0);
145 
146    //prescaler 1 enabled, divide by 2
147    if(pllcr & 0x0080)
148       dmaclks /= 2.0;
149 
150    //prescaler 2 enabled, divides value from prescaler 1 by 2
151    if(pllcr & 0x0020)
152       dmaclks /= 2.0;
153 
154    return dmaclks;
155 }
156 
sysclksPerClk32(void)157 static double sysclksPerClk32(void){
158    uint8_t sysclkSelect = registerArrayRead16(PLLCR) >> 8 & 0x0007;
159 
160    //>= 4 means run at full speed, no divider
161    if(sysclkSelect >= 4)
162       return dmaclksPerClk32();
163 
164    //divide DMACLK by 2 to the power of PLLCR SYSCLKSEL
165    return dmaclksPerClk32() / (2 << sysclkSelect);
166 }
167 
rtiInterruptClk32(void)168 static void rtiInterruptClk32(void){
169    //this function is part of endClk32();
170    uint16_t triggeredRtiInterrupts = 0x0000;
171 
172    if(clk32Counter % (M5XX_CRYSTAL_FREQUENCY / 512) == 0){
173       //RIS7 - 512HZ
174       triggeredRtiInterrupts |= 0x8000;
175    }
176    if(clk32Counter % (M5XX_CRYSTAL_FREQUENCY / 256) == 0){
177       //RIS6 - 256HZ
178       triggeredRtiInterrupts |= 0x4000;
179    }
180    if(clk32Counter % (M5XX_CRYSTAL_FREQUENCY / 128) == 0){
181       //RIS5 - 128HZ
182       triggeredRtiInterrupts |= 0x2000;
183    }
184    if(clk32Counter % (M5XX_CRYSTAL_FREQUENCY / 64) == 0){
185       //RIS4 - 64HZ
186       triggeredRtiInterrupts |= 0x1000;
187    }
188    if(clk32Counter % (M5XX_CRYSTAL_FREQUENCY / 32) == 0){
189       //RIS3 - 32HZ
190       triggeredRtiInterrupts |= 0x0800;
191    }
192    if(clk32Counter % (M5XX_CRYSTAL_FREQUENCY / 16) == 0){
193       //RIS2 - 16HZ
194       triggeredRtiInterrupts |= 0x0400;
195    }
196    if(clk32Counter % (M5XX_CRYSTAL_FREQUENCY / 8) == 0){
197       //RIS1 - 8HZ
198       triggeredRtiInterrupts |= 0x0200;
199    }
200    if(clk32Counter % (M5XX_CRYSTAL_FREQUENCY / 4) == 0){
201       //RIS0 - 4HZ
202       triggeredRtiInterrupts |= 0x0100;
203    }
204 
205    triggeredRtiInterrupts &= registerArrayRead16(RTCIENR);
206    if(triggeredRtiInterrupts){
207       registerArrayWrite16(RTCISR, registerArrayRead16(RTCISR) | triggeredRtiInterrupts);
208       setIprIsrBit(DBVZ_INT_RTI);
209    }
210 }
211 
watchdogSecondTickClk32(void)212 static void watchdogSecondTickClk32(void){
213    //this function is part of endClk32();
214    uint16_t watchdogState = registerArrayRead16(WATCHDOG);
215 
216    if(watchdogState & 0x0001){
217       //watchdog enabled
218       watchdogState += 0x0100;//add second to watchdog timer
219       watchdogState &= 0x0383;//cap overflow
220       if((watchdogState & 0x0200) == 0x0200){
221          //time expired
222          if(watchdogState & 0x0002){
223             //interrupt
224             watchdogState |= 0x0080;
225             setIprIsrBit(DBVZ_INT_WDT);
226          }
227          else{
228             //reset
229             debugLog("Watchdog reset triggered, PC:0x%08X\n", flx68000GetPc());
230             emulatorSoftReset();
231             return;
232          }
233       }
234       registerArrayWrite16(WATCHDOG, watchdogState);
235    }
236 }
237 
rtcAddSecondClk32(void)238 static void rtcAddSecondClk32(void){
239    //this function is part of endClk32();
240    if(registerArrayRead16(RTCCTL) & 0x0080){
241       //RTC enable bit set
242       uint16_t rtcInterruptEvents = 0x0000;
243       uint32_t newRtcTime;
244       uint32_t oldRtcTime = registerArrayRead32(RTCTIME);
245       uint32_t rtcAlrm = registerArrayRead32(RTCALRM);
246       uint16_t dayAlrm = registerArrayRead16(DAYALRM);
247       uint16_t days = registerArrayRead16(DAYR);
248       uint8_t hours = oldRtcTime >> 24;
249       uint8_t minutes = oldRtcTime >> 16 & 0x0000003F;
250       uint8_t seconds = oldRtcTime & 0x0000003F;
251 
252       if(palmSyncRtc && palmGetRtcFromHost){
253          //get new RTC value from system
254          uint16_t stopwatch = registerArrayRead16(STPWCH);
255          uint8_t alarmHours = rtcAlrm >> 24;
256          uint8_t alarmMinutes = rtcAlrm >> 16 & 0x0000003F;
257          uint8_t alarmSeconds = rtcAlrm & 0x0000003F;
258          uint8_t time[3];
259 
260          palmGetRtcFromHost(time);
261 
262          //day rollover happened
263          if(hours > time[0]){
264             days++;
265             rtcInterruptEvents |= 0x0008;
266          }
267 
268          if(time[0] != hours)
269             rtcInterruptEvents |= 0x0020;
270 
271          if(time[1] != minutes)
272             rtcInterruptEvents |= 0x0002;
273 
274          if(time[2] != seconds)
275             rtcInterruptEvents |= 0x0010;
276 
277          if(stopwatch != 0x003F){
278             stopwatch -= FAST_ABS(time[1] - minutes);
279 
280             if(stopwatch <= 0x0000)
281                stopwatch = 0x003F;
282             registerArrayWrite16(STPWCH, stopwatch);
283          }
284 
285          //if stopwatch ran out above or was enabled with 0x003F in the register trigger interrupt
286          if(stopwatch == 0x003F)
287             rtcInterruptEvents |= 0x0001;
288 
289          newRtcTime = time[2];//seconds
290          newRtcTime |= time[1] << 16;//minutes
291          newRtcTime |= time[0] << 24;//hours
292 
293          //check alarm range to see if it triggered in the time that has passed
294          if(days == dayAlrm){
295             if(hours < alarmHours || hours == alarmHours && minutes < alarmMinutes || hours == alarmHours && minutes == alarmMinutes && seconds < alarmSeconds){
296                //old time is before alarm
297                if(time[0] > alarmHours || time[0] == alarmHours && time[1] > alarmMinutes || time[0] == alarmHours && time[1] == alarmMinutes && time[0] >= alarmSeconds){
298                   //new time is after alarm
299                   rtcInterruptEvents |= 0x0040;
300                }
301             }
302          }
303       }
304       else{
305          //standard frame based time increment
306 
307          seconds++;
308          rtcInterruptEvents |= 0x0010;
309          if(seconds >= 60){
310             uint16_t stopwatch = registerArrayRead16(STPWCH);
311 
312             if(stopwatch != 0x003F){
313                if(stopwatch == 0x0000)
314                   stopwatch = 0x003F;
315                else
316                   stopwatch--;
317                registerArrayWrite16(STPWCH, stopwatch);
318             }
319 
320             //if stopwatch ran out above or was enabled with 0x003F in the register trigger interrupt
321             if(stopwatch == 0x003F)
322                rtcInterruptEvents |= 0x0001;
323 
324             minutes++;
325             seconds = 0;
326             rtcInterruptEvents |= 0x0002;
327             if(minutes >= 60){
328                hours++;
329                minutes = 0;
330                rtcInterruptEvents |= 0x0020;
331                if(hours >= 24){
332                   hours = 0;
333                   days++;
334                   rtcInterruptEvents |= 0x0008;
335                }
336             }
337          }
338 
339          newRtcTime = seconds;
340          newRtcTime |= minutes << 16;
341          newRtcTime |= hours << 24;
342 
343          if(newRtcTime == rtcAlrm && days == dayAlrm)
344             rtcInterruptEvents |= 0x0040;
345       }
346 
347       rtcInterruptEvents &= registerArrayRead16(RTCIENR);
348       if(rtcInterruptEvents){
349          registerArrayWrite16(RTCISR, registerArrayRead16(RTCISR) | rtcInterruptEvents);
350          setIprIsrBit(DBVZ_INT_RTC);
351       }
352 
353       registerArrayWrite32(RTCTIME, newRtcTime);
354       registerArrayWrite16(DAYR, days & 0x01FF);
355    }
356 
357    watchdogSecondTickClk32();
358 }
359 
dbvzBeginClk32(void)360 static void dbvzBeginClk32(void){
361    dbvzClk32Sysclks = 0.0;
362 }
363 
dbvzEndClk32(void)364 static void dbvzEndClk32(void){
365    //second position counter
366    if(clk32Counter >= M5XX_CRYSTAL_FREQUENCY - 1){
367       clk32Counter = 0;
368       rtcAddSecondClk32();
369    }
370    else{
371       clk32Counter++;
372    }
373 
374    //disabled if both the watchdog timer AND the RTC timer are disabled
375    if(registerArrayRead16(RTCCTL) & 0x0080 || registerArrayRead16(WATCHDOG) & 0x01)
376       rtiInterruptClk32();
377 
378    timer1(DBVZ_TIMER_REASON_CLK32, 0);
379    timer2(DBVZ_TIMER_REASON_CLK32, 0);
380    samplePwm1(true/*forClk32*/, 0.0);
381 
382    //PLLCR sleep wait
383    if(pllSleepWait != -1){
384       if(pllSleepWait == 0){
385          //disable PLL and CPU
386          dbvzSysclksPerClk32 = 0.0;
387          debugLog("PLL disabled, CPU is off!\n");
388       }
389       pllSleepWait--;
390    }
391 
392    //PLLCR wake select wait
393    if(pllWakeWait != -1){
394       if(pllWakeWait == 0){
395          //reenable PLL and CPU
396          registerArrayWrite16(PLLCR, registerArrayRead16(PLLCR) & 0xFFF7);
397          dbvzSysclksPerClk32 = sysclksPerClk32();
398          debugLog("PLL reenabled, CPU is on!\n");
399       }
400       pllWakeWait--;
401    }
402 
403    //UART1/2, these are polled to remain thread safe
404    updateUart1Interrupt();
405    updateUart2Interrupt();
406 
407    checkInterrupts();
408 }
409 
dbvzAddSysclks(double count)410 static void dbvzAddSysclks(double count){
411    timer1(DBVZ_TIMER_REASON_SYSCLK, count);
412    timer2(DBVZ_TIMER_REASON_SYSCLK, count);
413    samplePwm1(false/*forClk32*/, count);
414 
415    checkInterrupts();
416    dbvzClk32Sysclks += count;
417 }
418 
audioGetFramePercentIncrementFromClk32s(int32_t count)419 static int32_t audioGetFramePercentIncrementFromClk32s(int32_t count){
420    return (double)count / ((double)M5XX_CRYSTAL_FREQUENCY / EMU_FPS) * DBVZ_AUDIO_END_OF_FRAME;
421 }
422 
audioGetFramePercentIncrementFromSysclks(double count)423 static int32_t audioGetFramePercentIncrementFromSysclks(double count){
424    return count / (dbvzSysclksPerClk32 * ((double)M5XX_CRYSTAL_FREQUENCY / EMU_FPS)) * DBVZ_AUDIO_END_OF_FRAME;
425 }
426 
audioGetFramePercentage(void)427 static int32_t audioGetFramePercentage(void){
428    //returns how much of the frame has executed
429    //0% = 0, 100% = DBVZ_AUDIO_END_OF_FRAME
430    return audioGetFramePercentIncrementFromClk32s(dbvzFrameClk32s) + (dbvzIsPllOn() ? audioGetFramePercentIncrementFromSysclks(dbvzClk32Sysclks) : 0);
431 }
432