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