1 /*
2  * ds1216e.c - DS1216E RTC emulation.
3  *
4  * Written by
5  *  Marco van den Heuvel <blackystardust68@yahoo.com>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 #include "vice.h"
28 
29 #include "ds1216e.h"
30 #include "lib.h"
31 #include "rtc.h"
32 #include "snapshot.h"
33 
34 #include <string.h>
35 
36 /* The DS1216E is a phantom RTC module, it can be used in between a ROM
37  * socket and the actual ROM, the RTC registers are accessed by first
38  * activating the output by means of a read-bit-match-pattern, once
39  * the output has been activated the RTC registers will be serially
40  * output on the D0 pin in 64 consecutive reads. The RTC has the
41  * following features:
42  * - Real-Time Clock Counts centi-seconds, seconds, minutes, hours, date of the month,
43  *   months, and years
44  * - Watch function is transparant to ROM function
45  * - Month and year determine the amount of days in the year, leap-year compensation
46  *   valid upto 2100
47  * - All clock registers are in BCD format
48  */
49 
50 /* The DS1216E has the following clock registers:
51  *
52  * register 0 : bits 7-4 tenths of seconds
53  *              bits 3-0 hundreths of seconds
54  *
55  * register 1 : bit  7   0
56  *              bits 6-4 10 seconds
57  *              bits 3-0 seconds
58  *
59  * register 2 : bit  7   0
60  *              bits 6-4 10 minutes
61  *              bits 3-0 minutes
62  *
63  * register 3 : bit  7 12/24 hour indicator
64  *              bit  6 0
65  *              bit  5 20 hours (24 hour format) / AM/PM indicator (12 hour format)
66  *              bit  4 10 hours
67  *              bits 3-0 hours
68  *
69  * register 4 : bits 7-6 0
70  *              bit  5   oscilator control
71  *              bit  4   reset control
72  *              bit  3   0
73  *              bits 2-0 day of week (1-7, mon-sun)
74  *
75  * register 5: bits 7-6 0
76  *             bits 5-4 10 day of month
77  *             bits 3-0 day of month
78  *
79  * register 6: bits 7-5 0
80  *             bit  4   10 months
81  *             bits 3-0 months
82  *
83  * register 7: bits 7-4 10 years
84  *             bits 3-0 years
85  */
86 
87 /* This module is currently used in the following emulated hardware:
88    - IEC FD2000/4000 disk drives
89  */
90 
91 /* ---------------------------------------------------------------------------------------------------- */
92 
ds1216e_init(char * device)93 rtc_ds1216e_t *ds1216e_init(char *device)
94 {
95     rtc_ds1216e_t *retval = lib_calloc(1, sizeof(rtc_ds1216e_t));
96     int loaded = rtc_load_context(device, 0, DS1216E_REG_SIZE);
97 
98     if (loaded) {
99         retval->offset = rtc_get_loaded_offset();
100         retval->clock_regs = rtc_get_loaded_clockregs();
101     } else {
102         retval->offset = 0;
103         retval->clock_regs = lib_calloc(1, DS1216E_REG_SIZE);
104     }
105     retval->old_offset = retval->offset;
106     memcpy(retval->old_clock_regs, retval->clock_regs, DS1216E_REG_SIZE);
107 
108     retval->device = lib_stralloc(device);
109 
110     return retval;
111 }
112 
ds1216e_destroy(rtc_ds1216e_t * context,int save)113 void ds1216e_destroy(rtc_ds1216e_t *context, int save)
114 {
115     if (save) {
116         if (memcmp(context->clock_regs, context->old_clock_regs, DS1216E_REG_SIZE) ||
117             context->offset != context->old_offset) {
118             rtc_save_context(NULL, 0, context->clock_regs, DS1216E_REG_SIZE, context->device, context->offset);
119         }
120     }
121     lib_free(context->clock_regs);
122     lib_free(context->device);
123     lib_free(context);
124 }
125 
126 /* ---------------------------------------------------------------------------------------------------- */
127 
128 static uint8_t pattern[64] = {
129     1, 0, 1, 0, 0, 0, 1, 1,
130     0, 1, 0, 1, 1, 1, 0, 0,
131     1, 1, 0, 0, 0, 1, 0, 1,
132     0, 0, 1, 1, 1, 0, 1, 0,
133     1, 0, 1, 0, 0, 0, 1, 1,
134     0, 1, 0, 1, 1, 1, 0, 0,
135     1, 1, 0, 0, 0, 1, 0, 1,
136     0, 0, 1, 1, 1, 0, 1, 0
137 };
138 
ds1216e_latch_regs(rtc_ds1216e_t * context)139 static void ds1216e_latch_regs(rtc_ds1216e_t *context)
140 {
141     time_t latch = (context->inactive) ? context->latch : rtc_get_latch(context->offset);
142 
143     context->clock_regs[DS1216E_REGISTER_CENTISECONDS] = rtc_get_centisecond(1);
144     context->clock_regs[DS1216E_REGISTER_SECONDS] = rtc_get_second(latch, 1);
145     context->clock_regs[DS1216E_REGISTER_MINUTES] = rtc_get_minute(latch, 1);
146     context->clock_regs[DS1216E_REGISTER_HOURS] = (context->hours12) ? 0x80 : 0;
147     if (context->hours12) {
148         context->clock_regs[DS1216E_REGISTER_HOURS] |= rtc_get_hour_am_pm(latch, 1);
149     } else {
150         context->clock_regs[DS1216E_REGISTER_HOURS] |= rtc_get_hour(latch, 1);
151     }
152     context->clock_regs[DS1216E_REGISTER_WEEKDAYS] = (context->inactive) ? 0x20 : 0;
153     context->clock_regs[DS1216E_REGISTER_WEEKDAYS] |= (context->reset) ? 0x10 : 0;
154     context->clock_regs[DS1216E_REGISTER_WEEKDAYS] |= ((rtc_get_weekday(latch) - 1) % 7) + 1;
155     context->clock_regs[DS1216E_REGISTER_MONTHDAYS] = rtc_get_day_of_month(latch, 1);
156     context->clock_regs[DS1216E_REGISTER_MONTHS] = rtc_get_month(latch, 1);
157     context->clock_regs[DS1216E_REGISTER_YEARS] = rtc_get_year(latch, 1);
158 }
159 
ds1216e_match_pattern(rtc_ds1216e_t * context,uint16_t address)160 static void ds1216e_match_pattern(rtc_ds1216e_t *context, uint16_t address)
161 {
162     int i;
163 
164     /* check for read cycle */
165     if (address & 4) {
166         context->pattern_pos = 0;
167         context->pattern_ignore = 0;
168         return;
169     }
170 
171     /* check for pattern ignore */
172     if (context->pattern_ignore) {
173         return;
174     }
175 
176     /* check pattern bit */
177     if ((address & 1) == pattern[context->pattern_pos]) {
178         context->pattern_pos++;
179         if (context->pattern_pos == 64) {
180             context->output = 1;
181             context->output_pos = 0;
182             for (i = 0; i < 8; i++) {
183                 context->clock_regs_changed[i] = 0;
184             }
185             ds1216e_latch_regs(context);
186         }
187     } else {
188         context->pattern_ignore = 1;
189     }
190 }
191 
ds1216e_update_clock(rtc_ds1216e_t * context)192 static void ds1216e_update_clock(rtc_ds1216e_t *context)
193 {
194     int new_osc;
195     int new_reset;
196     int new_12;
197 
198     /* setting centiseconds has no effect on the offset used for the clock,
199        as this is defined in seconds, so any changes to the centiseconds
200        will just be ignored.
201      */
202 
203     /* prepare clock regs for updating, clear unused bits, fix value offsets */
204     context->clock_regs[DS1216E_REGISTER_SECONDS] &= 0x7f;
205     context->clock_regs[DS1216E_REGISTER_MINUTES] &= 0x7f;
206 
207     new_12 = context->clock_regs[DS1216E_REGISTER_HOURS] >> 7;
208     context->clock_regs[DS1216E_REGISTER_HOURS] &= 0x3f;
209 
210     new_osc = (context->clock_regs[DS1216E_REGISTER_WEEKDAYS] & 0x20) ? 1 : 0;
211     new_reset = (context->clock_regs[DS1216E_REGISTER_WEEKDAYS] & 0x10) ? 1 : 0;
212     context->clock_regs[DS1216E_REGISTER_WEEKDAYS] &= 7;
213 
214     context->clock_regs[DS1216E_REGISTER_MONTHDAYS] &= 0x3f;
215     context->clock_regs[DS1216E_REGISTER_MONTHS] &= 0x1f;
216 
217     if (context->inactive) {
218         if (context->clock_regs_changed[DS1216E_REGISTER_YEARS]) {
219             context->latch = rtc_set_latched_year(context->clock_regs[DS1216E_REGISTER_YEARS], context->latch, 1);
220         }
221         if (context->clock_regs_changed[DS1216E_REGISTER_MONTHS]) {
222             context->latch = rtc_set_latched_month(context->clock_regs[DS1216E_REGISTER_MONTHS], context->latch, 1);
223         }
224         if (context->clock_regs_changed[DS1216E_REGISTER_MONTHDAYS]) {
225             context->latch = rtc_set_latched_day_of_month(context->clock_regs[DS1216E_REGISTER_MONTHDAYS], context->latch, 1);
226         }
227         if (context->clock_regs_changed[DS1216E_REGISTER_WEEKDAYS]) {
228             context->latch = rtc_set_latched_weekday(context->clock_regs[DS1216E_REGISTER_WEEKDAYS] % 7, context->latch);
229         }
230         if (context->clock_regs_changed[DS1216E_REGISTER_HOURS]) {
231             if (new_12) {
232                 context->latch = rtc_set_latched_hour_am_pm(context->clock_regs[DS1216E_REGISTER_HOURS], context->latch, 1);
233             } else {
234                 context->latch = rtc_set_latched_hour(context->clock_regs[DS1216E_REGISTER_HOURS], context->latch, 1);
235             }
236         }
237         if (context->clock_regs_changed[DS1216E_REGISTER_MINUTES]) {
238             context->latch = rtc_set_latched_minute(context->clock_regs[DS1216E_REGISTER_MINUTES], context->latch, 1);
239         }
240         if (context->clock_regs_changed[DS1216E_REGISTER_SECONDS]) {
241             context->latch = rtc_set_latched_second(context->clock_regs[DS1216E_REGISTER_SECONDS], context->latch, 1);
242         }
243         if (!new_osc) {
244             context->offset = context->offset - (rtc_get_latch(0) - (context->latch - context->offset));
245             context->inactive = 0;
246         }
247     } else {
248         if (context->clock_regs_changed[DS1216E_REGISTER_YEARS]) {
249             context->offset = rtc_set_year(context->clock_regs[DS1216E_REGISTER_YEARS], context->offset, 1);
250         }
251         if (context->clock_regs_changed[DS1216E_REGISTER_MONTHS]) {
252             context->offset = rtc_set_month(context->clock_regs[DS1216E_REGISTER_MONTHS], context->offset, 1);
253         }
254         if (context->clock_regs_changed[DS1216E_REGISTER_MONTHDAYS]) {
255             context->offset = rtc_set_day_of_month(context->clock_regs[DS1216E_REGISTER_MONTHDAYS], context->offset, 1);
256         }
257         if (context->clock_regs_changed[DS1216E_REGISTER_WEEKDAYS]) {
258             context->offset = rtc_set_weekday(context->clock_regs[DS1216E_REGISTER_WEEKDAYS] % 7, context->offset);
259         }
260         if (context->clock_regs_changed[DS1216E_REGISTER_HOURS]) {
261             if (new_12) {
262                 context->offset = rtc_set_hour_am_pm(context->clock_regs[DS1216E_REGISTER_HOURS], context->offset, 1);
263             } else {
264                 context->offset = rtc_set_hour(context->clock_regs[DS1216E_REGISTER_HOURS], context->offset, 1);
265             }
266         }
267         if (context->clock_regs_changed[DS1216E_REGISTER_MINUTES]) {
268             context->offset = rtc_set_minute(context->clock_regs[DS1216E_REGISTER_MINUTES], context->offset, 1);
269         }
270         if (context->clock_regs_changed[DS1216E_REGISTER_SECONDS]) {
271             context->offset = rtc_set_second(context->clock_regs[DS1216E_REGISTER_SECONDS], context->offset, 1);
272         }
273         if (new_osc) {
274             context->latch = rtc_get_latch(context->offset);
275             context->inactive = new_osc;
276         }
277     }
278     context->reset = new_reset;
279     context->hours12 = new_12;
280 }
281 
inc_output_pos(rtc_ds1216e_t * context)282 static void inc_output_pos(rtc_ds1216e_t *context)
283 {
284     context->output_pos++;
285 
286     if (context->output_pos == 64) {
287         context->output = 0;
288         context->pattern_pos = 0;
289         ds1216e_update_clock(context);
290     }
291 }
292 
ds1216e_output_bit(rtc_ds1216e_t * context,uint8_t val)293 static uint8_t ds1216e_output_bit(rtc_ds1216e_t *context, uint8_t val)
294 {
295     int reg;
296     int bit;
297     uint8_t mask;
298 
299     /* clear bit 0 */
300     val &= 0xfe;
301 
302     /* decode the position */
303     reg = context->output_pos >> 3;
304     bit = context->output_pos & 7;
305     mask = 1 << bit;
306 
307     /* put the bit in place */
308     if (context->clock_regs[reg] & mask) {
309         val |= 1;
310     }
311 
312     inc_output_pos(context);
313 
314     return val;
315 }
316 
ds1216e_input_bit(rtc_ds1216e_t * context,uint16_t address)317 static void ds1216e_input_bit(rtc_ds1216e_t *context, uint16_t address)
318 {
319     int reg;
320     int bit;
321     uint8_t mask;
322 
323     /* decode the position */
324     reg = context->output_pos >> 3;
325     bit = context->output_pos & 7;
326     mask = 1 << bit;
327 
328     /* set/clear the correct bit */
329     context->clock_regs[reg] &= ~mask;
330     context->clock_regs[reg] |= (address & 1) << bit;
331 
332     /* indicate that changes have been made */
333     context->clock_regs_changed[reg] = 1;
334 
335     inc_output_pos(context);
336 }
337 
338 /* ---------------------------------------------------------------------------------------------------- */
339 
ds1216e_read(rtc_ds1216e_t * context,uint16_t address,uint8_t origbyte)340 uint8_t ds1216e_read(rtc_ds1216e_t *context, uint16_t address, uint8_t origbyte)
341 {
342     if (context->output) {
343         if (address & 4) {
344             return ds1216e_output_bit(context, origbyte);
345         } else {
346             ds1216e_input_bit(context, address);
347         }
348     } else {
349         ds1216e_match_pattern(context, address);
350     }
351     return origbyte;
352 }
353 
354 /* ---------------------------------------------------------------------------------------------------- */
355 
356 /* RTC_DS1216E snapshot module format:
357 
358    type   | name               | description
359    -------------------------------------
360    BYTE   | reset              | reset flag
361    BYTE   | inactive           | inactive flag
362    BYTE   | 12hours            | 12 hours flag
363    BYTE   | pattern pos        | pattern position
364    BYTE   | pattern ignore     | pattern ignore flag
365    BYTE   | output             | current output bit
366    BYTE   | output pos         | current output position
367    DWORD  | latch hi           | high DWORD of the latch offset
368    DWORD  | latch lo           | low DWORD of the latch offset
369    DWORD  | offset hi          | high DWORD of the RTC offset
370    DWORD  | offset lo          | low DWORD of the RTC offset
371    DWORD  | old offset hi      | high DWORD of the old RTC offset
372    DWORD  | old offset lo      | low DWORD of the old RTC offset
373    ARRAY  | clock regs         | 8 BYTES of register data
374    ARRAY  | old clock regs     | 8 BYTES of old register data
375    ARRAY  | clock regs changed | 8 BYTES of changed register data
376    STRING | device             | device name STRING
377  */
378 
379 static char snap_module_name[] = "RTC_DS1216E";
380 #define SNAP_MAJOR   0
381 #define SNAP_MINOR   0
382 
ds1216e_write_snapshot(rtc_ds1216e_t * context,snapshot_t * s)383 int ds1216e_write_snapshot(rtc_ds1216e_t *context, snapshot_t *s)
384 {
385     uint32_t latch_lo = 0;
386     uint32_t latch_hi = 0;
387     uint32_t offset_lo = 0;
388     uint32_t offset_hi = 0;
389     uint32_t old_offset_lo = 0;
390     uint32_t old_offset_hi = 0;
391     snapshot_module_t *m;
392 
393     /* time_t can be either 32bit or 64bit, so we save as 64bit */
394 #if (SIZE_OF_TIME_T == 8)
395     latch_hi = (uint32_t)(context->latch >> 32);
396     latch_lo = (uint32_t)(context->latch & 0xffffffff);
397     offset_hi = (uint32_t)(context->offset >> 32);
398     offset_lo = (uint32_t)(context->offset & 0xffffffff);
399     old_offset_hi = (uint32_t)(context->old_offset >> 32);
400     old_offset_lo = (uint32_t)(context->old_offset & 0xffffffff);
401 #else
402     latch_lo = (uint32_t)context->latch;
403     offset_lo = (uint32_t)context->offset;
404     old_offset_lo = (uint32_t)context->old_offset;
405 #endif
406 
407     m = snapshot_module_create(s, snap_module_name, SNAP_MAJOR, SNAP_MINOR);
408 
409     if (m == NULL) {
410         return -1;
411     }
412 
413     if (0
414         || SMW_B(m, (uint8_t)context->reset) < 0
415         || SMW_B(m, (uint8_t)context->inactive) < 0
416         || SMW_B(m, (uint8_t)context->hours12) < 0
417         || SMW_B(m, (uint8_t)context->pattern_pos) < 0
418         || SMW_B(m, (uint8_t)context->pattern_ignore) < 0
419         || SMW_B(m, (uint8_t)context->output) < 0
420         || SMW_B(m, (uint8_t)context->output_pos) < 0
421         || SMW_DW(m, latch_hi) < 0
422         || SMW_DW(m, latch_lo) < 0
423         || SMW_DW(m, offset_hi) < 0
424         || SMW_DW(m, offset_lo) < 0
425         || SMW_DW(m, old_offset_hi) < 0
426         || SMW_DW(m, old_offset_lo) < 0
427         || SMW_BA(m, context->clock_regs, DS1216E_REG_SIZE) < 0
428         || SMW_BA(m, context->old_clock_regs, DS1216E_REG_SIZE) < 0
429         || SMW_BA(m, context->clock_regs_changed, DS1216E_REG_SIZE) < 0
430         || SMW_STR(m, context->device) < 0) {
431         snapshot_module_close(m);
432         return -1;
433     }
434     return snapshot_module_close(m);
435 }
436 
ds1216e_read_snapshot(rtc_ds1216e_t * context,snapshot_t * s)437 int ds1216e_read_snapshot(rtc_ds1216e_t *context, snapshot_t *s)
438 {
439     uint32_t latch_lo = 0;
440     uint32_t latch_hi = 0;
441     uint32_t offset_lo = 0;
442     uint32_t offset_hi = 0;
443     uint32_t old_offset_lo = 0;
444     uint32_t old_offset_hi = 0;
445     uint8_t vmajor, vminor;
446     snapshot_module_t *m;
447 
448     m = snapshot_module_open(s, snap_module_name, &vmajor, &vminor);
449 
450     if (m == NULL) {
451         return -1;
452     }
453 
454     /* Do not accept versions higher than current */
455     if (vmajor > SNAP_MAJOR || vminor > SNAP_MINOR) {
456         snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
457         goto fail;
458     }
459 
460     if (0
461         || SMR_B_INT(m, &context->reset) < 0
462         || SMR_B_INT(m, &context->inactive) < 0
463         || SMR_B_INT(m, &context->hours12) < 0
464         || SMR_B_INT(m, &context->pattern_pos) < 0
465         || SMR_B_INT(m, &context->pattern_ignore) < 0
466         || SMR_B_INT(m, &context->output) < 0
467         || SMR_B_INT(m, &context->output_pos) < 0
468         || SMR_DW(m, &latch_hi) < 0
469         || SMR_DW(m, &latch_lo) < 0
470         || SMR_DW(m, &offset_hi) < 0
471         || SMR_DW(m, &offset_lo) < 0
472         || SMR_DW(m, &old_offset_hi) < 0
473         || SMR_DW(m, &old_offset_lo) < 0
474         || SMR_BA(m, context->clock_regs, DS1216E_REG_SIZE) < 0
475         || SMR_BA(m, context->old_clock_regs, DS1216E_REG_SIZE) < 0
476         || SMR_BA(m, context->clock_regs_changed, DS1216E_REG_SIZE) < 0
477         || SMR_STR(m, &context->device) < 0) {
478         goto fail;
479     }
480 
481 #if (SIZE_OF_TIME_T == 8)
482     context->latch = (time_t)(latch_hi) << 32;
483     context->latch |= latch_lo;
484     context->offset = (time_t)(offset_hi) << 32;
485     context->offset |= offset_lo;
486     context->old_offset = (time_t)(old_offset_hi) << 32;
487     context->old_offset |= old_offset_lo;
488 #else
489     context->latch = latch_lo;
490     context->offset = offset_lo;
491     context->old_offset = old_offset_lo;
492 #endif
493 
494     return snapshot_module_close(m);
495 
496 fail:
497     snapshot_module_close(m);
498     return -1;
499 }
500