]> git.sur5r.net Git - c128-kasse/blob - src/c128time.c
speed up interrupt handler by directly writing to VDC RAM
[c128-kasse] / src / c128time.c
1 /*
2  * RGB2R-C128-Kassenprogramm
3  * © 2007-2017 phil_fry, sECuRE, sur5r, mxf
4  * See LICENSE for license information
5  *
6  */
7 #include <peekpoke.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <c128.h>
11 #include <stdint.h>
12 #include <6502.h>
13 #include <conio.h>
14
15 #include "bcd2dec.h"
16 #include "general.h"
17 #include "globals.h"
18 #include "vdc_util.h"
19
20 /* This file uses the CIA TOD (Complex Interface Adapter, Time of Day)
21  * for timekeeping, see https://www.c64-wiki.com/wiki/CIA and its Links section
22  * for a bit more information */
23
24 /* the Time of Day PM bit is set for hours >= 12 */
25 #define TOD_PM 0x80
26
27 /* VDC charmap starts at 0x0000; 80 chars per line.
28  * We want to draw at 72 chars on the 1st line.
29  */
30 #define CLOCK_ADDR 72
31
32 /* arbitrarly chosen stack size, should be large enough */
33 #define DAYTIME_IRQ_STACK_SIZE 32
34 uint8_t daytime_irq_stack[DAYTIME_IRQ_STACK_SIZE];
35
36 void update_time(void) {
37   uint8_t bcd_hour, hour, min, sec, dummy;
38
39   /* Read the hour register first to stop the clock from updating the external
40    * registers from the internal (still ticking!) CIA registers. */
41
42   bcd_hour = CIA1.tod_hour;
43
44   /* hour is >= 12 if TOD_PM is set */
45   if (bcd_hour & TOD_PM) {
46     hour = bcd2dec(bcd_hour ^ TOD_PM);
47     /* adjust for 24h clock, 12:??pm should still be 12:?? */
48     if (hour != 12) {
49       hour += 12;
50     }
51   } else {
52     hour = bcd2dec(bcd_hour);
53   }
54
55   sec = bcd2dec(CIA1.tod_sec);
56   min = bcd2dec(CIA1.tod_min);
57
58   /* MUST read tod_10 to enable the clock latch again */
59   dummy = CIA1.tod_10;
60
61   /* it's a new day when hour wraps */
62   if (daytime.hour > hour) {
63     daytime.day++;
64   }
65
66   daytime.hour = hour;
67   daytime.min = min;
68   daytime.sec = sec;
69 }
70
71 char *get_time(void) {
72   static char buffer[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
73   update_time();
74   sprintf(buffer, "%02d:%02d:%02d", daytime.hour, daytime.min, daytime.sec);
75   return buffer;
76 }
77
78 /* divide by 10; put quotient in high nibble, remainder in low nibble */
79 uint8_t dec2bcd(const uint8_t dec) { return (((dec / 10) << 4) | (dec % 10)); }
80
81 void set_time(uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) {
82   uint8_t bcd_hour;
83
84   /* CIA TOD will always flip the PM bit
85    * when we either want to write the 0th or 12th hour.
86    * So we need to write the hour with the PM bit inverted,
87    * for the CIA to flip it again */
88   if (hour == 0) {
89     /* the 0th hour is 12am in 12h clock format, 0x12 is BCD for 12 */
90     bcd_hour = 0x12 ^ TOD_PM;
91   } else if (hour > 12) {
92     /* convert 24h clock to 12h with PM bit set */
93     bcd_hour = dec2bcd(hour - 12);
94     bcd_hour = bcd_hour ^ TOD_PM;
95   } else {
96     /* includes 12pm since the PM bit gets automatically flipped */
97     bcd_hour = dec2bcd(hour);
98   }
99
100   daytime.day = day;
101   daytime.hour = hour;
102   daytime.min = min;
103   daytime.sec = sec;
104
105   CIA1.tod_hour = bcd_hour;
106   CIA1.tod_min = dec2bcd(min);
107   CIA1.tod_sec = dec2bcd(sec);
108
109   /* set CIA1.tod_10 and program "Control Timer A" */
110   __asm__("jsr initsystime");
111 }
112
113 uint8_t _daytime_irq(void) {
114   char *t;
115   /* We are called 60 times a second. We only want to draw a clock
116    * when we are a) on the mainscreen and b) the seconds changed */
117   if (kasse_menu == MENU_MAIN && CIA1.tod_sec != daytime.sec) {
118     t = get_time();
119     vdc_write_mem(CLOCK_ADDR, t, 8);
120   }
121   /* always call additional handlers */
122   return (IRQ_NOT_HANDLED);
123 }
124
125 void install_daytime_irq(void) {
126   SEI();
127   set_irq(&_daytime_irq, daytime_irq_stack, DAYTIME_IRQ_STACK_SIZE);
128   CLI();
129 }