1 /*
2  * ds1307.c - DS1307 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 "ds1307.h"
30 #include "lib.h"
31 #include "rtc.h"
32 #include "snapshot.h"
33 
34 #include <time.h>
35 #include <string.h>
36 
37 /* The DS1307 is an I2C based RTC, it has the following features:
38  * - Real-Time Clock Counts Seconds, Minutes, Hours, Date of the Month,
39  *   Month, Day of the Week, and Year
40  * - 56 x 8 Battery-Backed General-Purpose RAM
41  * - 24/12h mode with AM/PM indicator in 12h mode
42  * - Clock Halt flag
43  * - All clock registers are in BCD format
44  */
45 
46 /* The DS1306 has the following clock registers:
47  *
48  * register 0 : bit  7   Clock Halt
49  *              bits 6-4 10 seconds
50  *              bits 3-0 seconds
51  *
52  * register 1 : bit  7   0
53  *              bits 6-4 10 minutes
54  *              bits 3-0 minutes
55  *
56  * register 2 : bit  7   0
57  *              bit  6   12/24 hour mode flag, 0 = 24 hour mode, 1 = 12 hour mode
58  *              bit  5   in 12 hour mode this is the AM/PM flag, 0 = AM, 1 = PM
59  *                       in 24 hour mode this is the msb of the 10 hours
60  *              bit  4   lsb of 10 hours
61  *              bits 3-0 hours
62  *
63  * register 3 : bits 7-3 0
64  *              bits 2-0 days (of week)
65  *
66  * register 4 : bits 7-6 0
67  *              bits 5-4 10 days (of month)
68  *              bits 3-0 days (of month)
69  *
70  * register 5 : bits 7-5 0
71  *              bit  4   10 months
72  *              bits 3-0 months
73  *
74  * register 6 : bits 7-4 10 years
75  *              bits 3-0 years
76  *
77  * register 7 : bit 7    OUT (emulated as RAM bit)
78  *              bits 6-5 0
79  *              bit 4    SQWE (emulated as RAM bit)
80  *              bits 3-2 0
81  *              bit 1    RS1 (emulated as RAM bit)
82  *              bit 0    RS0 (emulated as RAM bit)
83  *
84  * registers 8-64: RAM
85  */
86 
87 /* This module is currently used in the following emulated hardware:
88    - userport RTC (DS1307) device
89  */
90 
91 /* ---------------------------------------------------------------------------------------------------- */
92 
93 #define DS1307_RAM_SIZE   56
94 #define DS1307_REG_SIZE   8
95 
96 struct rtc_ds1307_s {
97     int clock_halt;
98     time_t clock_halt_latch;
99     int am_pm;
100     time_t latch;
101     time_t offset;
102     time_t old_offset;
103     uint8_t *clock_regs;
104     uint8_t old_clock_regs[DS1307_REG_SIZE];
105     uint8_t clock_regs_for_read[DS1307_REG_SIZE];
106     uint8_t *ram;
107     uint8_t old_ram[DS1307_RAM_SIZE];
108     uint8_t state;
109     uint8_t reg;
110     uint8_t reg_ptr;
111     uint8_t bit;
112     uint8_t io_byte;
113     uint8_t sclk_line;
114     uint8_t data_line;
115     uint8_t clock_register;
116     char *device;
117 };
118 
119 #define DS1307_REG_SECONDS_CH      0
120 #define DS1307_REG_MINUTES         1
121 #define DS1307_REG_HOURS           2
122 #define DS1307_REG_DAYS_OF_WEEK    3
123 #define DS1307_REG_DAYS_OF_MONTH   4
124 #define DS1307_REG_MONTHS          5
125 #define DS1307_REG_YEARS           6
126 #define DS1307_REG_CONTROL         7
127 
128 #define DS1307_IDLE               0
129 #define DS1307_GET_ADDRESS        1
130 #define DS1307_GET_REG_NR         2
131 #define DS1307_READ_REGS          3
132 #define DS1307_WRITE_REGS         4
133 #define DS1307_ADDRESS_READ_ACK   5
134 #define DS1307_ADDRESS_WRITE_ACK  6
135 #define DS1307_REG_NR_ACK         7
136 #define DS1307_WRITE_ACK          8
137 #define DS1307_READ_ACK           9
138 #define DS1307_START_WAIT         10
139 
140 /* ---------------------------------------------------------------------------------------------------- */
141 
ds1307_init(char * device)142 rtc_ds1307_t *ds1307_init(char *device)
143 {
144     rtc_ds1307_t *retval = lib_calloc(1, sizeof(rtc_ds1307_t));
145     int loaded = rtc_load_context(device, DS1307_RAM_SIZE, DS1307_REG_SIZE);
146 
147     if (loaded) {
148         retval->ram = rtc_get_loaded_ram();
149         retval->offset = rtc_get_loaded_offset();
150         retval->clock_regs = rtc_get_loaded_clockregs();
151     } else {
152         retval->ram = lib_calloc(1, DS1307_RAM_SIZE);
153         retval->offset = 0;
154         retval->clock_regs = lib_calloc(1, DS1307_REG_SIZE);
155     }
156     memcpy(retval->old_ram, retval->ram, DS1307_RAM_SIZE);
157     retval->old_offset = retval->offset;
158     memcpy(retval->old_clock_regs, retval->clock_regs, DS1307_REG_SIZE);
159 
160     retval->device = lib_stralloc(device);
161     retval->state = DS1307_IDLE;
162     retval->sclk_line = 1;
163     retval->data_line = 1;
164     retval->reg_ptr = 0;
165 
166     return retval;
167 }
168 
ds1307_destroy(rtc_ds1307_t * context,int save)169 void ds1307_destroy(rtc_ds1307_t *context, int save)
170 {
171     if (save) {
172         if (memcmp(context->ram, context->old_ram, DS1307_RAM_SIZE) ||
173             memcmp(context->clock_regs, context->old_clock_regs, DS1307_REG_SIZE) ||
174             context->offset != context->old_offset) {
175             rtc_save_context(context->ram, DS1307_RAM_SIZE, context->clock_regs, DS1307_REG_SIZE, context->device, context->offset);
176         }
177     }
178     lib_free(context->ram);
179     lib_free(context->clock_regs);
180     lib_free(context->device);
181     lib_free(context);
182 }
183 
184 /* ---------------------------------------------------------------------------------------------------- */
185 
ds1307_i2c_start(rtc_ds1307_t * context)186 static void ds1307_i2c_start(rtc_ds1307_t *context)
187 {
188     uint8_t tmp;
189     time_t latch = (context->clock_halt) ? context->clock_halt_latch : rtc_get_latch(context->offset);
190 
191     tmp = context->clock_halt << 7;
192     tmp |= rtc_get_second(latch, 1);
193     context->clock_regs_for_read[DS1307_REG_SECONDS_CH] = tmp;
194     context->clock_regs_for_read[DS1307_REG_MINUTES] = rtc_get_minute(latch, 1);
195     tmp = context->am_pm << 6;
196     if (context->am_pm) {
197         tmp |= rtc_get_hour_am_pm(latch, 1);
198     } else {
199         tmp |= rtc_get_hour(latch, 1);
200     }
201     context->clock_regs_for_read[DS1307_REG_HOURS] = tmp;
202     context->clock_regs_for_read[DS1307_REG_DAYS_OF_WEEK] = rtc_get_weekday(latch) + 1;
203     context->clock_regs_for_read[DS1307_REG_DAYS_OF_MONTH] = rtc_get_day_of_month(latch, 1);
204     context->clock_regs_for_read[DS1307_REG_MONTHS] = rtc_get_month(latch, 1);
205     context->clock_regs_for_read[DS1307_REG_YEARS] = rtc_get_year(latch, 1);
206     context->clock_regs_for_read[DS1307_REG_CONTROL] = context->clock_regs[DS1307_REG_CONTROL];
207 }
208 
ds1307_read_register(rtc_ds1307_t * context,uint8_t addr)209 static uint8_t ds1307_read_register(rtc_ds1307_t *context, uint8_t addr)
210 {
211     if (addr < DS1307_REG_SIZE) {
212         return context->clock_regs_for_read[addr];
213     }
214     return context->ram[addr - DS1307_REG_SIZE];
215 }
216 
ds1307_write_register(rtc_ds1307_t * context,uint8_t addr,uint8_t val)217 static void ds1307_write_register(rtc_ds1307_t *context, uint8_t addr, uint8_t val)
218 {
219     switch (addr) {
220         case DS1307_REG_MINUTES:
221             if (context->clock_halt) {
222                 context->clock_halt_latch = rtc_set_latched_minute(val, context->clock_halt_latch, 1);
223             } else {
224                 context->offset = rtc_set_minute(val, context->offset, 1);
225             }
226             break;
227         case DS1307_REG_DAYS_OF_MONTH:
228             if (context->clock_halt) {
229                 context->clock_halt_latch = rtc_set_latched_day_of_month(val, context->clock_halt_latch, 1);
230             } else {
231                 context->offset = rtc_set_day_of_month(val, context->offset, 1);
232             }
233             break;
234         case DS1307_REG_MONTHS:
235             if (context->clock_halt) {
236                 context->clock_halt_latch = rtc_set_latched_month(val, context->clock_halt_latch, 1);
237             } else {
238                 context->offset = rtc_set_month(val, context->offset, 1);
239             }
240             break;
241         case DS1307_REG_DAYS_OF_WEEK:
242             if (context->clock_halt) {
243                 context->clock_halt_latch = rtc_set_latched_weekday(val - 1, context->clock_halt_latch);
244             } else {
245                 context->offset = rtc_set_weekday(val - 1, context->offset);
246             }
247             break;
248         case DS1307_REG_YEARS:
249             if (context->clock_halt) {
250                 context->clock_halt_latch = rtc_set_latched_year(val, context->clock_halt_latch, 1);
251             } else {
252                 context->offset = rtc_set_year(val, context->offset, 1);
253             }
254             break;
255         case DS1307_REG_CONTROL:
256             context->clock_regs[DS1307_REG_CONTROL] = (val & 0x93);
257             break;
258         case DS1307_REG_HOURS:
259             if (val & 0x40) {
260                 if (context->clock_halt) {
261                     context->clock_halt_latch = rtc_set_latched_hour_am_pm(val & 0x3f, context->clock_halt_latch, 1);
262                 } else {
263                     context->offset = rtc_set_hour_am_pm(val & 0x3f, context->offset, 1);
264                 }
265                 context->am_pm = 1;
266             } else {
267                 if (context->clock_halt) {
268                     context->clock_halt_latch = rtc_set_latched_hour(val & 0x3f, context->clock_halt_latch, 1);
269                 } else {
270                     context->offset = rtc_set_hour(val & 0x3f, context->offset, 1);
271                 }
272                 context->am_pm = 0;
273             }
274             break;
275         case DS1307_REG_SECONDS_CH:
276             if (context->clock_halt) {
277                 context->clock_halt_latch = rtc_set_latched_second(val & 0x7f, context->clock_halt_latch, 1);
278                 if (!(val & 0x80)) {
279                     context->offset = context->offset - (rtc_get_latch(0) - (context->clock_halt_latch - context->offset));
280                     context->clock_halt = 0;
281                 }
282             } else {
283                 context->offset = rtc_set_second(val & 0x7f, context->offset, 1);
284                 if (val & 0x80) {
285                     context->clock_halt = 1;
286                     context->clock_halt_latch = rtc_get_latch(context->offset);
287                 }
288             }
289             break;
290         default:
291             context->ram[addr - DS1307_REG_SIZE] = val;
292     }
293 }
294 
ds1307_next_address_bit(rtc_ds1307_t * context)295 static void ds1307_next_address_bit(rtc_ds1307_t *context)
296 {
297     context->reg |= (context->data_line << (7 - context->bit));
298     ++context->bit;
299     if (context->bit == 8) {
300         if (context->reg == 0xd0) {
301             context->state = DS1307_ADDRESS_WRITE_ACK;
302         } else if (context->reg == 0xd1) {
303             context->state = DS1307_ADDRESS_READ_ACK;
304         } else {
305             context->state = DS1307_IDLE;
306         }
307     }
308 }
309 
ds1307_next_reg_nr_bit(rtc_ds1307_t * context)310 static void ds1307_next_reg_nr_bit(rtc_ds1307_t *context)
311 {
312     context->reg |= (context->data_line << (7 - context->bit));
313     ++context->bit;
314     if (context->bit == 8) {
315         context->state = DS1307_REG_NR_ACK;
316         context->reg_ptr = context->reg & 0x3f;
317     }
318 }
319 
ds1307_next_read_bit(rtc_ds1307_t * context)320 static void ds1307_next_read_bit(rtc_ds1307_t *context)
321 {
322     ++context->bit;
323     if (context->bit == 8) {
324         context->state = DS1307_READ_ACK;
325     }
326 }
327 
ds1307_next_write_bit(rtc_ds1307_t * context)328 static void ds1307_next_write_bit(rtc_ds1307_t *context)
329 {
330     context->reg |= (context->data_line << (7 - context->bit));
331     ++context->bit;
332     if (context->bit == 8) {
333         ds1307_write_register(context, context->reg_ptr, context->reg);
334         context->state = DS1307_WRITE_ACK;
335         ++context->reg_ptr;
336         context->reg_ptr &= 0x3f;
337     }
338 }
339 
ds1307_validate_read_ack(rtc_ds1307_t * context)340 static void ds1307_validate_read_ack(rtc_ds1307_t *context)
341 {
342     if (!context->data_line) {
343         context->state = DS1307_READ_REGS;
344         context->bit = 0;
345         ++context->reg_ptr;
346         context->reg_ptr &= 0x3f;
347         context->reg = ds1307_read_register(context, context->reg_ptr);
348     } else {
349         context->state = DS1307_IDLE;
350     }
351 }
352 
353 /* ---------------------------------------------------------------------------------------------------- */
354 
ds1307_set_clk_line(rtc_ds1307_t * context,uint8_t data)355 void ds1307_set_clk_line(rtc_ds1307_t *context, uint8_t data)
356 {
357     uint8_t val = data ? 1 : 0;
358 
359     if (context->sclk_line == val) {
360         return;
361     }
362 
363     if (!val) {
364         switch (context->state) {
365             case DS1307_START_WAIT:
366                 context->state = DS1307_GET_ADDRESS;
367                 break;
368             case DS1307_GET_ADDRESS:
369                 ds1307_next_address_bit(context);
370                 break;
371             case DS1307_GET_REG_NR:
372                 ds1307_next_reg_nr_bit(context);
373                 break;
374             case DS1307_READ_REGS:
375                 ds1307_next_read_bit(context);
376                 break;
377             case DS1307_WRITE_REGS:
378                 ds1307_next_write_bit(context);
379                 break;
380             case DS1307_READ_ACK:
381                 ds1307_validate_read_ack(context);
382                 break;
383             case DS1307_ADDRESS_READ_ACK:
384                 context->state = DS1307_READ_REGS;
385                 context->reg = ds1307_read_register(context, context->reg_ptr);
386                 context->bit = 0;
387                 break;
388             case DS1307_REG_NR_ACK:
389             case DS1307_WRITE_ACK:
390                 context->state = DS1307_WRITE_REGS;
391                 context->reg = 0;
392                 context->bit = 0;
393                 break;
394             case DS1307_ADDRESS_WRITE_ACK:
395                 context->state = DS1307_GET_REG_NR;
396                 context->reg = 0;
397                 context->bit = 0;
398                 break;
399         }
400     }
401     context->sclk_line = val;
402 }
403 
ds1307_set_data_line(rtc_ds1307_t * context,uint8_t data)404 void ds1307_set_data_line(rtc_ds1307_t *context, uint8_t data)
405 {
406     uint8_t val = data ? 1 : 0;
407 
408     if (context->data_line == val) {
409         return;
410     }
411 
412     if (context->sclk_line) {
413         if (val) {
414             context->state = DS1307_IDLE;
415         } else {
416             ds1307_i2c_start(context);
417             context->state = DS1307_START_WAIT;
418             context->reg = 0;
419             context->bit = 0;
420         }
421     }
422     context->data_line = val;
423 }
424 
ds1307_read_data_line(rtc_ds1307_t * context)425 uint8_t ds1307_read_data_line(rtc_ds1307_t *context)
426 {
427 	switch (context->state) {
428         case DS1307_READ_REGS:
429             return (context->reg & (1 << (7 - context->bit))) >> (7 - context->bit);
430         case DS1307_ADDRESS_READ_ACK:
431         case DS1307_ADDRESS_WRITE_ACK:
432         case DS1307_REG_NR_ACK:
433         case DS1307_READ_ACK:
434         case DS1307_WRITE_ACK:
435             return 0;
436     }
437     return 1;
438 }
439 
440 /* ---------------------------------------------------------------------------------------------------- */
441 
442 /* RTC_DS1307 snapshot module format:
443 
444    type   | name                | description
445    ------------------------------------------
446    BYTE   | clock halt          | clock halt flag
447    DWORD  | clock halt latch hi | high DWORD of clock halt offset
448    DWORD  | clock halt latch lo | low DWORD of clock halt offset
449    BYTE   | am pm               | AM/PM flag
450    DWORD  | latch hi            | high DWORD of latch offset
451    DWORD  | latch lo            | low DWORD of latch offset
452    DWORD  | offset hi           | high DWORD of RTC offset
453    DWORD  | offset lo           | low DWORD of RTC offset
454    DWORD  | old offset hi       | high DWORD of old RTC offset
455    DWORD  | old offset lo       | low DWORD of old RTC offset
456    ARRAY  | clock regs          | 8 BYTES of register data
457    ARRAY  | old clock regs      | 8 BYTES of old register data
458    ARRAY  | clock regs for read | 8 BYTES of register read data
459    ARRAY  | RAM                 | 56 BYTES of RAM data
460    ARRAY  | old RAM             | 56 BYTES of old RAM data
461    BYTE   | state               | current state
462    BYTE   | reg                 | current register
463    BYTE   | reg ptr             | register pointer
464    BYTE   | bit                 | current bit
465    BYTE   | io byte             | current I/O BYTE
466    BYTE   | sclk                | SCLK line state
467    BYTE   | data                | DATA line state
468    BYTE   | clock register      | clock register flag
469    STRING | device              | device name STRING
470  */
471 
472 static char snap_module_name[] = "RTC_DS1307";
473 #define SNAP_MAJOR   0
474 #define SNAP_MINOR   0
475 
ds1307_write_snapshot(rtc_ds1307_t * context,snapshot_t * s)476 int ds1307_write_snapshot(rtc_ds1307_t *context, snapshot_t *s)
477 {
478     uint32_t clock_halt_latch_lo = 0;
479     uint32_t clock_halt_latch_hi = 0;
480     uint32_t latch_lo = 0;
481     uint32_t latch_hi = 0;
482     uint32_t offset_lo = 0;
483     uint32_t offset_hi = 0;
484     uint32_t old_offset_lo = 0;
485     uint32_t old_offset_hi = 0;
486     snapshot_module_t *m;
487 
488     /* time_t can be either 32bit or 64bit, so we save as 64bit */
489 #if (SIZE_OF_TIME_T == 8)
490     clock_halt_latch_hi = (uint32_t)(context->clock_halt_latch >> 32);
491     clock_halt_latch_lo = (uint32_t)(context->clock_halt_latch & 0xffffffff);
492     latch_hi = (uint32_t)(context->latch >> 32);
493     latch_lo = (uint32_t)(context->latch & 0xffffffff);
494     offset_hi = (uint32_t)(context->offset >> 32);
495     offset_lo = (uint32_t)(context->offset & 0xffffffff);
496     old_offset_hi = (uint32_t)(context->old_offset >> 32);
497     old_offset_lo = (uint32_t)(context->old_offset & 0xffffffff);
498 #else
499     clock_halt_latch_lo = (uint32_t)context->clock_halt_latch;
500     latch_lo = (uint32_t)context->latch;
501     offset_lo = (uint32_t)context->offset;
502     old_offset_lo = (uint32_t)context->old_offset;
503 #endif
504 
505     m = snapshot_module_create(s, snap_module_name, SNAP_MAJOR, SNAP_MINOR);
506 
507     if (m == NULL) {
508         return -1;
509     }
510 
511     if (0
512         || SMW_B(m, (uint8_t)context->clock_halt) < 0
513         || SMW_DW(m, clock_halt_latch_hi) < 0
514         || SMW_DW(m, clock_halt_latch_lo) < 0
515         || SMW_B(m, (uint8_t)context->am_pm) < 0
516         || SMW_DW(m, latch_hi) < 0
517         || SMW_DW(m, latch_lo) < 0
518         || SMW_DW(m, offset_hi) < 0
519         || SMW_DW(m, offset_lo) < 0
520         || SMW_DW(m, old_offset_hi) < 0
521         || SMW_DW(m, old_offset_lo) < 0
522         || SMW_BA(m, context->clock_regs, DS1307_REG_SIZE) < 0
523         || SMW_BA(m, context->old_clock_regs, DS1307_REG_SIZE) < 0
524         || SMW_BA(m, context->clock_regs_for_read, DS1307_REG_SIZE) < 0
525         || SMW_BA(m, context->ram, DS1307_RAM_SIZE) < 0
526         || SMW_BA(m, context->old_ram, DS1307_RAM_SIZE) < 0
527         || SMW_B(m, context->state) < 0
528         || SMW_B(m, context->reg) < 0
529         || SMW_B(m, context->reg_ptr) < 0
530         || SMW_B(m, context->bit) < 0
531         || SMW_B(m, context->io_byte) < 0
532         || SMW_B(m, context->sclk_line) < 0
533         || SMW_B(m, context->data_line) < 0
534         || SMW_B(m, context->clock_register) < 0
535         || SMW_STR(m, context->device) < 0) {
536         snapshot_module_close(m);
537         return -1;
538     }
539     return snapshot_module_close(m);
540 }
541 
ds1307_read_snapshot(rtc_ds1307_t * context,snapshot_t * s)542 int ds1307_read_snapshot(rtc_ds1307_t *context, snapshot_t *s)
543 {
544     uint32_t clock_halt_latch_lo = 0;
545     uint32_t clock_halt_latch_hi = 0;
546     uint32_t latch_lo = 0;
547     uint32_t latch_hi = 0;
548     uint32_t offset_lo = 0;
549     uint32_t offset_hi = 0;
550     uint32_t old_offset_lo = 0;
551     uint32_t old_offset_hi = 0;
552     uint8_t vmajor, vminor;
553     snapshot_module_t *m;
554 
555     m = snapshot_module_open(s, snap_module_name, &vmajor, &vminor);
556 
557     if (m == NULL) {
558         return -1;
559     }
560 
561     /* Do not accept versions higher than current */
562     if (vmajor > SNAP_MAJOR || vminor > SNAP_MINOR) {
563         snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
564         goto fail;
565     }
566 
567     if (0
568         || SMR_B_INT(m, &context->clock_halt) < 0
569         || SMR_DW(m, &clock_halt_latch_hi) < 0
570         || SMR_DW(m, &clock_halt_latch_lo) < 0
571         || SMR_B_INT(m, &context->am_pm) < 0
572         || SMR_DW(m, &latch_hi) < 0
573         || SMR_DW(m, &latch_lo) < 0
574         || SMR_DW(m, &offset_hi) < 0
575         || SMR_DW(m, &offset_lo) < 0
576         || SMR_DW(m, &old_offset_hi) < 0
577         || SMR_DW(m, &old_offset_lo) < 0
578         || SMR_BA(m, context->clock_regs, DS1307_REG_SIZE) < 0
579         || SMR_BA(m, context->old_clock_regs, DS1307_REG_SIZE) < 0
580         || SMR_BA(m, context->clock_regs_for_read, DS1307_REG_SIZE) < 0
581         || SMR_BA(m, context->ram, DS1307_RAM_SIZE) < 0
582         || SMR_BA(m, context->old_ram, DS1307_RAM_SIZE) < 0
583         || SMR_B(m, &context->state) < 0
584         || SMR_B(m, &context->reg) < 0
585         || SMR_B(m, &context->reg_ptr) < 0
586         || SMR_B(m, &context->bit) < 0
587         || SMR_B(m, &context->io_byte) < 0
588         || SMR_B(m, &context->sclk_line) < 0
589         || SMR_B(m, &context->data_line) < 0
590         || SMR_B(m, &context->clock_register) < 0
591         || SMR_STR(m, &context->device) < 0) {
592         goto fail;
593     }
594 
595     snapshot_module_close(m);
596 
597 #if (SIZE_OF_TIME_T == 8)
598     context->clock_halt_latch = (time_t)(clock_halt_latch_hi) << 32;
599     context->clock_halt_latch |= clock_halt_latch_lo;
600     context->latch = (time_t)(latch_hi) << 32;
601     context->latch |= latch_lo;
602     context->offset = (time_t)(offset_hi) << 32;
603     context->offset |= offset_lo;
604     context->old_offset = (time_t)(old_offset_hi) << 32;
605     context->old_offset |= old_offset_lo;
606 #else
607     context->clock_halt_latch = clock_halt_latch_lo;
608     context->latch = latch_lo;
609     context->offset = offset_lo;
610     context->old_offset = old_offset_lo;
611 #endif
612 
613     return 0;
614 
615 fail:
616     snapshot_module_close(m);
617     return -1;
618 }
619