fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/arch/macplus/rtc.c *
7 * Created: 2007-11-16 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2007-2019 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include "main.h"
24#include "rtc.h"
25
26#include <stdio.h>
27#include <time.h>
28
29
30static
31unsigned long mac_rtc_hms_to_time (unsigned h, unsigned m, unsigned s)
32{
33 if ((h > 23) || (m > 59) || (s > 59)) {
34 return (0);
35 }
36
37 return (60UL * (60UL * h + m) + s);
38}
39
40static
41unsigned long mac_rtc_ymd_to_time (unsigned y, unsigned m, unsigned d)
42{
43 unsigned long t;
44 unsigned i;
45 unsigned *month;
46
47 static unsigned months[2][12] = {
48 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
49 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
50 };
51
52 if ((y < 1904U) || (m > 11)) {
53 return (0);
54 }
55
56 y -= 1904U;
57
58 t = (y / 4UL) * (4UL * 365UL + 1UL);
59 y = y % 4U;
60
61 t += 365UL * y + (y > 0);
62
63 month = (y == 0) ? months[1] : months[0];
64
65 for (i = 0; i < m; i++) {
66 t += month[i];
67 }
68
69 if (d >= month[m]) {
70 return (0);
71 }
72
73 t += d;
74
75 t *= (24UL * 60UL * 60UL);
76
77 return (t);
78}
79
80static
81void mac_rtc_get_uint (const char **str, unsigned *val)
82{
83 const char *s;
84 unsigned v;
85
86 s = *str;
87 v = 0;
88
89 while ((*s < '0') || (*s > '9')) {
90 if (*s == 0) {
91 *str = s;
92 *val = 0;
93 return;
94 }
95
96 s += 1;
97 }
98
99 while ((*s >= '0') && (*s <= '9')) {
100 v = 10 * v + (*s - '0');
101 s += 1;
102 }
103
104 *str = s;
105 *val = v;
106}
107
108unsigned long mac_rtc_time_from_string (const char *str)
109{
110 unsigned i;
111 unsigned val[6];
112 unsigned long ret;
113
114 for (i = 0; i < 6; i++) {
115 mac_rtc_get_uint (&str, &val[i]);
116 }
117
118 if (val[1] > 0) {
119 val[1] -= 1;
120 }
121
122 if (val[2] > 0) {
123 val[2] -= 1;
124 }
125
126 ret = mac_rtc_ymd_to_time (val[0], val[1], val[2]);
127 ret += mac_rtc_hms_to_time (val[3], val[4], val[5]);
128
129 return (ret);
130}
131
132void mac_rtc_init (mac_rtc_t *rtc)
133{
134 rtc->set_data_ext = NULL;
135 rtc->set_data = NULL;
136 rtc->set_data_val = 0;
137
138 rtc->set_osi_ext = NULL;
139 rtc->set_osi = NULL;
140 rtc->set_osi_val = 0;
141
142 rtc->data_out = 0;
143 rtc->state = 0;
144 rtc->bitcnt = 0;
145 rtc->sigval = 0;
146
147 rtc->realtime = 0;
148 rtc->gmtoff = 0;
149 rtc->clkcnt = 0;
150
151 rtc->reg_wp = 0;
152 rtc->reg_test = 0;
153
154 rtc->clock = 0;
155 rtc->rtime = 0;
156
157 mac_rtc_set_defaults (rtc);
158}
159
160void mac_rtc_free (mac_rtc_t *rtc)
161{
162}
163
164void mac_rtc_set_data_fct (mac_rtc_t *rtc, void *ext, void *fct)
165{
166 rtc->set_data_ext = ext;
167 rtc->set_data = fct;
168}
169
170void mac_rtc_set_osi_fct (mac_rtc_t *rtc, void *ext, void *fct)
171{
172 rtc->set_osi_ext = ext;
173 rtc->set_osi = fct;
174}
175
176void mac_rtc_set_realtime (mac_rtc_t *rtc, int realtime)
177{
178 rtc->realtime = (realtime != 0);
179}
180
181void mac_rtc_set_defaults (mac_rtc_t *rtc)
182{
183 unsigned i;
184
185 for (i = 0; i < 256; i++) {
186 rtc->data[i] = 0;
187 }
188}
189
190int mac_rtc_load_file (mac_rtc_t *rtc, const char *fname)
191{
192 FILE *fp;
193
194 fp = fopen (fname, "rb");
195
196 if (fp == NULL) {
197 return (1);
198 }
199
200 if (fread (rtc->data, 1, 256, fp) != 256) {
201 fclose (fp);
202 return (1);
203 }
204
205 fclose (fp);
206
207 rtc->reg_wp = 0x80;
208 rtc->reg_test = 0x00;
209
210 return (0);
211}
212
213int mac_rtc_save_file (mac_rtc_t *rtc, const char *fname)
214{
215 FILE *fp;
216
217 fp = fopen (fname, "wb");
218
219 if (fp == NULL) {
220 return (1);
221 }
222
223 if (fwrite (rtc->data, 1, 256, fp) != 256) {
224 fclose (fp);
225 return (1);
226 }
227
228 fclose (fp);
229
230 return (0);
231}
232
233static
234unsigned long mac_rtc_get_current_time (mac_rtc_t *rtc)
235{
236 time_t ut;
237 unsigned long mt;
238
239 ut = time (NULL);
240
241 mt = (unsigned long) ut;
242
243 mt += rtc->gmtoff;
244
245 /* 1970-01-01 00:00:00 */
246 mt += 2082844800;
247
248 return (mt);
249}
250
251static
252unsigned long mac_rtc_get_timezone (mac_rtc_t *rtc)
253{
254 unsigned long tz;
255
256 tz = rtc->data[0xed];
257 tz = (tz << 8) | rtc->data[0xee];
258 tz = (tz << 8) | rtc->data[0xef];
259
260 if (tz & 0x800000) {
261 tz |= 0xff000000;
262 }
263
264 return (tz);
265}
266
267void mac_rtc_set_time (mac_rtc_t *rtc, unsigned long time, int utc)
268{
269 rtc->rtime = mac_rtc_get_current_time (rtc);
270 rtc->clock = time;
271
272 if (utc) {
273 rtc->clock += mac_rtc_get_timezone (rtc);
274 }
275}
276
277void mac_rtc_set_time_now (mac_rtc_t *rtc)
278{
279 rtc->rtime = mac_rtc_get_current_time (rtc);
280 rtc->clock = rtc->rtime;
281 rtc->clock += mac_rtc_get_timezone (rtc);
282}
283
284void mac_rtc_set_time_str (mac_rtc_t *rtc, const char *str)
285{
286 unsigned long time;
287
288 if (str == NULL) {
289 mac_rtc_set_time_now (rtc);
290 }
291 else {
292 time = mac_rtc_time_from_string (str);
293
294 mac_rtc_set_time (rtc, time, 0);
295 }
296}
297
298void mac_rtc_set_time_gmtoff (mac_rtc_t *rtc)
299{
300 struct tm *tm;
301 time_t ut;
302
303 ut = time (NULL);
304 tm = localtime (&ut);
305 rtc->gmtoff = tm->tm_gmtoff;
306}
307
308static
309void mac_rtc_set_data (mac_rtc_t *rtc, unsigned char val)
310{
311 /* data must always be set because it switches from input to output */
312
313 rtc->set_data_val = (val != 0);
314
315 if (rtc->set_data != NULL) {
316 rtc->set_data (rtc->set_data_ext, rtc->set_data_val);
317 }
318}
319
320static
321void mac_rtc_set_osi (mac_rtc_t *rtc, unsigned char val)
322{
323 val = (val != 0);
324
325 if (rtc->set_osi_val == val) {
326 return;
327 }
328
329 rtc->set_osi_val = val;
330
331 if (rtc->set_osi != NULL) {
332 rtc->set_osi (rtc->set_osi_ext, val);
333 }
334}
335
336static
337void mac_rtc_cmd1_read (mac_rtc_t *rtc)
338{
339 unsigned char reg;
340
341 reg = (rtc->cmd1 >> 2) & 0x1f;
342
343 if ((rtc->cmd1 & 0xe3) == 0x81) {
344 rtc->shift = (rtc->clock >> (8 * (reg & 3))) & 0xff;
345 }
346 else if ((rtc->cmd1 & 0xf3) == 0xa1) {
347 rtc->shift = rtc->data[8 + ((rtc->cmd1 >> 2) & 3)];
348 }
349 else if ((rtc->cmd1 & 0xc3) == 0xc1) {
350 rtc->shift = rtc->data[16 + ((rtc->cmd1 >> 2) & 15)];
351 }
352 else {
353 rtc->shift = 0x00;
354 }
355
356#ifdef DEBUG_RTC
357 mac_log_deb ("rtc: read command 1: %02X (%02X)\n",
358 rtc->cmd1, rtc->shift
359 );
360#endif
361}
362
363static
364void mac_rtc_cmd1_write (mac_rtc_t *rtc)
365{
366#ifdef DEBUG_RTC
367 mac_log_deb ("rtc: write command 1: %02X (%02X)\n",
368 rtc->cmd1, rtc->shift
369 );
370#endif
371
372 if (rtc->cmd1 == 0x35) {
373 rtc->reg_wp = rtc->shift & 0x80;
374 return;
375 }
376
377 if (rtc->reg_wp & 0x80) {
378 return;
379 }
380
381 if ((rtc->cmd1 & 0xe3) == 0x01) {
382 unsigned bit;
383 unsigned long val;
384
385 bit = 8 * ((rtc->cmd1 >> 2) & 3);
386 val = rtc->shift & 0xff;
387
388 rtc->clock &= ~(0x000000ffUL << bit);
389 rtc->clock |= val << bit;
390 }
391 else if ((rtc->cmd1 & 0xf3) == 0x21) {
392 rtc->data[8 + ((rtc->cmd1 >> 2) & 3)] = rtc->shift;
393 }
394 else if (rtc->cmd1 == 0x31) {
395 rtc->reg_test = rtc->shift;
396 }
397 else if ((rtc->cmd1 & 0xc3) == 0x41) {
398 rtc->data[16 + ((rtc->cmd1 >> 2) & 15)] = rtc->shift;
399 }
400}
401
402static
403void mac_rtc_cmd2_read (mac_rtc_t *rtc)
404{
405 unsigned addr;
406
407 addr = ((rtc->cmd1 & 7) << 5) | ((rtc->cmd2 >> 2) & 0x1f);
408
409 if (addr < 256) {
410 rtc->shift = rtc->data[addr];
411 }
412 else {
413 rtc->shift = 0;
414 }
415
416#ifdef DEBUG_RTC
417 mac_log_deb ("rtc: read command 2: %02X %02X S=%X A=%02X (%02X)\n",
418 rtc->cmd1, rtc->cmd2, rtc->cmd1 & 7, (rtc->cmd2 >> 2) & 0x1f,
419 rtc->shift
420 );
421#endif
422}
423
424static
425void mac_rtc_cmd2_write (mac_rtc_t *rtc)
426{
427 unsigned addr;
428
429#ifdef DEBUG_RTC
430 mac_log_deb ("rtc: write command 2: %02X %02X S=%X A=%02X (%02X)\n",
431 rtc->cmd1, rtc->cmd2, rtc->cmd1 & 7, (rtc->cmd2 >> 2) & 0x1f,
432 rtc->shift
433 );
434#endif
435
436 if (rtc->reg_wp & 0x80) {
437 return;
438 }
439
440 addr = ((rtc->cmd1 & 7) << 5) | ((rtc->cmd2 >> 2) & 0x1f);
441
442 if (addr < 256) {
443 rtc->data[addr] = rtc->shift;
444 }
445}
446
447void mac_rtc_set_uint8 (mac_rtc_t *rtc, unsigned char val)
448{
449 unsigned char dif;
450
451 dif = rtc->sigval ^ val;
452 rtc->sigval = val;
453
454 if (val & 0x04) {
455 /* serial disabled */
456 rtc->state = 0;
457 rtc->data_out = 0;
458 rtc->bitcnt = 0;
459 return;
460 }
461
462 if ((dif & ~val & 0x02) == 0) {
463 return;
464 }
465
466 /* clock went low */
467
468 if (rtc->data_out) {
469 /* send a byte to the cpu */
470 mac_rtc_set_data (rtc, rtc->shift & 0x80);
471
472 rtc->shift = (rtc->shift << 1) | ((rtc->shift >> 7) & 0x01);
473
474 rtc->bitcnt += 1;
475
476 if (rtc->bitcnt >= 8) {
477 rtc->bitcnt = 0;
478 rtc->data_out = 0;
479 rtc->state = 0;
480 }
481 }
482 else {
483 /* receive a byte from the cpu */
484 rtc->shift = (rtc->shift << 1) | (val & 0x01);
485
486 rtc->bitcnt += 1;
487
488 if (rtc->bitcnt >= 8) {
489 if (rtc->state == 0) {
490 /* cmd1 */
491 rtc->cmd1 = rtc->shift;
492
493 if ((rtc->cmd1 & 0x78) == 0x38) {
494 /* extended command */
495 rtc->state = 2;
496 }
497 else if (rtc->cmd1 & 0x80) {
498 /* read command */
499 mac_rtc_cmd1_read (rtc);
500 rtc->state = 0;
501 rtc->data_out = 1;
502 }
503 else {
504 rtc->state = 1;
505 }
506 }
507 else if (rtc->state == 1) {
508 /* data byte for cmd1 */
509 mac_rtc_cmd1_write (rtc);
510 rtc->state = 0;
511 }
512 else if (rtc->state == 2) {
513 /* cmd2 */
514 rtc->cmd2 = rtc->shift;
515 if (rtc->cmd1 & 0x80) {
516 mac_rtc_cmd2_read (rtc);
517 rtc->state = 0;
518 rtc->data_out = 1;
519 }
520 else {
521 rtc->state = 3;
522 }
523 }
524 else if (rtc->state == 3) {
525 /* data byte for cmd2 */
526 mac_rtc_cmd2_write (rtc);
527 rtc->state = 0;
528 }
529
530 rtc->bitcnt = 0;
531 }
532 }
533}
534
535void mac_rtc_clock (mac_rtc_t *rtc, unsigned long n)
536{
537 unsigned long clock, rtime;
538
539 clock = rtc->clock;
540
541 if (rtc->realtime) {
542 rtime = rtc->rtime;
543 rtc->rtime = mac_rtc_get_current_time (rtc);
544 rtc->clock += rtc->rtime - rtime;
545 }
546 else {
547 rtc->clkcnt += n;
548
549 if (rtc->clkcnt > MAC_CPU_CLOCK) {
550 rtc->clkcnt -= MAC_CPU_CLOCK;
551 rtc->clock += 1;
552 }
553 }
554
555 rtc->clock &= 0xffffffff;
556
557 if (rtc->clock != clock) {
558#ifdef DEBUG_RTC
559 mac_log_deb ("rtc: osi (%lu)\n", rtc->clkcnt);
560#endif
561 mac_rtc_set_osi (rtc, 1);
562 mac_rtc_set_osi (rtc, 0);
563 }
564}