1 /*
2  * c64tpi.c - IEEE488 interface for the C64.
3  *
4  * Written by
5  *  Andre Fachat <a.fachat@physik.tu-chemnitz.de>
6  *  Andreas Boose <viceteam@t-online.de>
7  *
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include "archdep.h"
34 #include "c64.h"
35 #include "c64cart.h" /* for export_t */
36 #define CARTRIDGE_INCLUDE_SLOT0_API
37 #include "c64cartsystem.h"
38 #undef CARTRIDGE_INCLUDE_SLOT0_API
39 #include "c64mem.h"
40 #include "cartio.h"
41 #include "cartridge.h"
42 #include "cmdline.h"
43 #include "drive.h"
44 #include "export.h"
45 #include "lib.h"
46 #include "log.h"
47 #include "parallel.h"
48 #include "maincpu.h"
49 #include "monitor.h"
50 #include "resources.h"
51 #include "tpi.h"
52 #include "types.h"
53 #include "util.h"
54 #include "crt.h"
55 
56 #define CARTRIDGE_INCLUDE_PRIVATE_API
57 #include "c64tpi.h"
58 #undef CARTRIDGE_INCLUDE_PRIVATE_API
59 
60 /*
61     IEEE488 interface for c64 and c128
62 
63     - 4kb ROM, mapped to $8000 in 8k game config
64 
65     - the hardware uses a TPI at $DF00-$DF07 (mirrored through $DF08-$DFFF)
66 
67     TODO: register description
68 */
69 
70 /* #define DEBUGTPI */
71 
72 #ifdef DEBUGTPI
73 #define DBG(x) printf x
74 #else
75 #define DBG(x)
76 #endif
77 
78 #define mytpi_init tpi_init
79 #define mytpi_set_int tpi_set_int
80 
81 /* 4 KB ROM */
82 #define TPI_ROM_SIZE 0x1000
83 static uint8_t *tpi_rom = NULL;
84 
85 static tpi_context_t *tpi_context;
86 
87 /* ---------------------------------------------------------------------*/
88 static void tpi_io2_store(uint16_t addr, uint8_t data);
89 static uint8_t tpi_io2_read(uint16_t addr);
90 static uint8_t tpi_io2_peek(uint16_t addr);
91 static int tpi_io2_dump(void);
92 
93 static io_source_t tpi_io2_device = {
94     CARTRIDGE_NAME_IEEE488,
95     IO_DETACH_CART,
96     NULL,
97     0xdf00, 0xdfff, 0x07,
98     1, /* read is always valid */
99     tpi_io2_store,
100     tpi_io2_read,
101     tpi_io2_peek,
102     tpi_io2_dump,
103     CARTRIDGE_IEEE488,
104     0,
105     0
106 };
107 
108 static io_source_list_t *tpi_list_item = NULL;
109 
110 static const export_resource_t export_res = {
111     CARTRIDGE_NAME_IEEE488, 0, 1, NULL, &tpi_io2_device, CARTRIDGE_IEEE488
112 };
113 
114 /* ---------------------------------------------------------------------*/
115 
116 static int ieee488_enabled = 0;
117 
118 static int tpi_extexrom = 0;
119 static int tpi_extgame = 0;
120 
121 static int rom_enabled = 1;
122 
tpi_cart_enabled(void)123 int tpi_cart_enabled(void)
124 {
125     return ieee488_enabled;
126 }
127 
128 /* ---------------------------------------------------------------------*/
129 
tpi_io2_store(uint16_t addr,uint8_t data)130 static void tpi_io2_store(uint16_t addr, uint8_t data)
131 {
132     DBG(("TPI io2 w %02x (%02x)\n", addr, data));
133     tpicore_store(tpi_context, addr, data);
134 }
135 
tpi_io2_read(uint16_t addr)136 static uint8_t tpi_io2_read(uint16_t addr)
137 {
138     DBG(("TPI io2 r %02x\n", addr));
139     return tpicore_read(tpi_context, addr);
140 }
141 
tpi_io2_peek(uint16_t addr)142 static uint8_t tpi_io2_peek(uint16_t addr)
143 {
144     return tpicore_peek(tpi_context, addr);
145 }
146 
tpi_io2_dump(void)147 static int tpi_io2_dump(void)
148 {
149     mon_out("TPI\n");
150     tpicore_dump(tpi_context);
151     return 0;
152 }
153 /* ---------------------------------------------------------------------*/
154 
tpi_roml_read(uint16_t addr,uint8_t * value)155 int tpi_roml_read(uint16_t addr, uint8_t *value)
156 {
157     if (rom_enabled) {
158         *value = tpi_rom[addr & 0xfff];
159         return CART_READ_VALID;
160     }
161     return CART_READ_THROUGH;
162 }
163 
tpi_peek_mem(uint16_t addr,uint8_t * value)164 int tpi_peek_mem(uint16_t addr, uint8_t *value)
165 {
166     if ((addr >= 0x8000) && (addr <= 0x9fff)) {
167         if (rom_enabled) {
168             *value = tpi_rom[addr & 0xfff];
169             return CART_READ_VALID;
170         }
171     }
172     return CART_READ_THROUGH;
173 }
174 
175 /* ---------------------------------------------------------------------*/
176 
177 /*
178     Port A (ieee control)
179 
180     Port B (ieee data)
181 
182     Port C
183 
184     bit 7  in  passthrough port exrom line
185     bit 6  out (unused ?)
186     bit 5  out (unused ?)
187     bit 4  out ROML enable
188     bit 3  out expansionport exrom line
189     bit 2  out (unused ?)
190     bit 1  out IEEE (U4, Pin 12)
191     bit 0  out IEEE (U4, Pin 18)
192 */
193 
set_int(unsigned int int_num,int value)194 static void set_int(unsigned int int_num, int value)
195 {
196 }
197 
restore_int(unsigned int int_num,int value)198 static void restore_int(unsigned int int_num, int value)
199 {
200 }
201 
set_ca(tpi_context_t * tpi_ctx,int a)202 static void set_ca(tpi_context_t *tpi_ctx, int a)
203 {
204 }
205 
set_cb(tpi_context_t * tpi_ctx,int a)206 static void set_cb(tpi_context_t *tpi_ctx, int a)
207 {
208 }
209 
210 static int ieee_is_dev = 1;
211 static uint8_t ieee_is_out = 1;
212 
reset(tpi_context_t * tpi_ctx)213 static void reset(tpi_context_t *tpi_ctx)
214 {
215     /* assuming input after reset */
216     parallel_cpu_set_atn(0);
217     parallel_cpu_set_ndac(0);
218     parallel_cpu_set_nrfd(0);
219     parallel_cpu_set_dav(0);
220     parallel_cpu_set_eoi(0);
221     parallel_cpu_set_bus(0xff);
222 
223     ieee_is_dev = 1;
224     ieee_is_out = 1;
225 }
226 
store_pa(tpi_context_t * tpi_ctx,uint8_t byte)227 static void store_pa(tpi_context_t *tpi_ctx, uint8_t byte)
228 {
229     if (byte != tpi_ctx->oldpa) {
230         uint8_t tmp = ~byte;
231 
232         ieee_is_dev = byte & 0x01;
233         ieee_is_out = byte & 0x02;
234 
235         parallel_cpu_set_bus((uint8_t)(ieee_is_out ? tpi_ctx->oldpb : 0xff));
236 
237         if (ieee_is_out) {
238             parallel_cpu_set_ndac(0);
239             parallel_cpu_set_nrfd(0);
240             parallel_cpu_set_dav((uint8_t)(tmp & 0x10));
241             parallel_cpu_set_eoi((uint8_t)(tmp & 0x20));
242         } else {
243             parallel_cpu_set_nrfd((uint8_t)(tmp & 0x80));
244             parallel_cpu_set_ndac((uint8_t)(tmp & 0x40));
245             parallel_cpu_set_dav(0);
246             parallel_cpu_set_eoi(0);
247         }
248         if (ieee_is_dev) {
249             parallel_cpu_set_atn(0);
250         } else {
251             parallel_cpu_set_atn((uint8_t)(tmp & 0x08));
252         }
253     }
254 }
255 
store_pb(tpi_context_t * tpi_ctx,uint8_t byte)256 static void store_pb(tpi_context_t *tpi_ctx, uint8_t byte)
257 {
258     parallel_cpu_set_bus((uint8_t)(ieee_is_out ? byte : 0xff));
259 }
260 
undump_pa(tpi_context_t * tpi_ctx,uint8_t byte)261 static void undump_pa(tpi_context_t *tpi_ctx, uint8_t byte)
262 {
263     uint8_t tmp = ~byte;
264     ieee_is_dev = byte & 0x01;
265     ieee_is_out = byte & 0x02;
266 
267     parallel_cpu_set_bus((uint8_t)(ieee_is_out ? tpi_ctx->oldpb : 0xff));
268 
269     if (ieee_is_out) {
270         parallel_cpu_set_ndac(0);
271         parallel_cpu_set_nrfd(0);
272         parallel_cpu_set_dav((uint8_t)(tmp & 0x10));
273         parallel_cpu_set_eoi((uint8_t)(tmp & 0x20));
274     } else {
275         parallel_cpu_set_nrfd((uint8_t)(tmp & 0x80));
276         parallel_cpu_set_ndac((uint8_t)(tmp & 0x40));
277         parallel_cpu_set_dav(0);
278         parallel_cpu_set_eoi(0);
279     }
280     if (ieee_is_dev) {
281         parallel_cpu_restore_atn(0);
282     } else {
283         parallel_cpu_restore_atn((uint8_t)(tmp & 0x08));
284     }
285 }
286 
undump_pb(tpi_context_t * tpi_ctx,uint8_t byte)287 static void undump_pb(tpi_context_t *tpi_ctx, uint8_t byte)
288 {
289     parallel_cpu_set_bus((uint8_t)(ieee_is_out ? byte : 0xff));
290 }
291 
store_pc(tpi_context_t * tpi_ctx,uint8_t byte)292 static void store_pc(tpi_context_t *tpi_ctx, uint8_t byte)
293 {
294     int exrom = ((byte & 0x08) ? 0 : 1); /* bit 3, 1 = active */
295     rom_enabled = ((byte & 0x10) ? 1 : 0); /* bit 4, 1 = active */
296     /* passthrough support */
297     DBG(("TPI store_pc %02x (rom enabled: %d exrom: %d game: %d)\n",
298                 byte, rom_enabled, exrom ^ 1, tpi_extgame));
299     cart_config_changed_slot0((uint8_t)((exrom << 1) | tpi_extgame),
300             (uint8_t)((exrom << 1) | tpi_extgame), CMODE_READ);
301 }
302 
undump_pc(tpi_context_t * tpi_ctx,uint8_t byte)303 static void undump_pc(tpi_context_t *tpi_ctx, uint8_t byte)
304 {
305 }
306 
read_pa(tpi_context_t * tpi_ctx)307 static uint8_t read_pa(tpi_context_t *tpi_ctx)
308 {
309     uint8_t byte;
310 
311     drive_cpu_execute_all(maincpu_clk);
312 
313     byte = 0xff;
314     if (ieee_is_out) {
315         if (parallel_nrfd) {
316             byte &= 0x7f;
317         }
318         if (parallel_ndac) {
319             byte &= 0xbf;
320         }
321     } else {
322         if (parallel_dav) {
323             byte &= 0xef;
324         }
325         if (parallel_eoi) {
326             byte &= 0xdf;
327         }
328     }
329     if (ieee_is_dev) {
330         if (parallel_atn) {
331             byte &= 0xf7;
332         }
333     }
334 
335     byte = (byte & ~(tpi_ctx->c_tpi)[TPI_DDPA])
336         | (tpi_ctx->c_tpi[TPI_PA] & tpi_ctx->c_tpi[TPI_DDPA]);
337 
338     return byte;
339 }
340 
read_pb(tpi_context_t * tpi_ctx)341 static uint8_t read_pb(tpi_context_t *tpi_ctx)
342 {
343     uint8_t byte;
344 
345     drive_cpu_execute_all(maincpu_clk);
346 
347     byte = ieee_is_out ? 0xff : parallel_bus;
348     byte = (byte & ~(tpi_ctx->c_tpi)[TPI_DDPB])
349         | (tpi_ctx->c_tpi[TPI_PB] & tpi_ctx->c_tpi[TPI_DDPB]);
350 
351     return byte;
352 }
353 
read_pc(tpi_context_t * tpi_ctx)354 static uint8_t read_pc(tpi_context_t *tpi_ctx)
355 {
356     uint8_t byte = 0xff;
357 
358     if (tpi_extexrom) {
359         byte &= ~(1 << 7);
360     }
361     byte = (byte & ~(tpi_ctx->c_tpi)[TPI_DDPC])
362         | (tpi_ctx->c_tpi[TPI_PC] & tpi_ctx->c_tpi[TPI_DDPC]);
363     return byte;
364 }
365 
366 /* ---------------------------------------------------------------------*/
367 
tpi_reset(void)368 void tpi_reset(void)
369 {
370     DBG(("TPI: tpi_reset\n"));
371     tpicore_reset(tpi_context);
372     cart_config_changed_slot0(CMODE_8KGAME, CMODE_8KGAME, CMODE_READ);
373     rom_enabled = 1;
374 }
375 
tpi_init(void)376 void tpi_init(void)
377 {
378     tpi_context->log = log_open(tpi_context->myname);
379 }
380 
tpi_shutdown(void)381 void tpi_shutdown(void)
382 {
383     tpicore_shutdown(tpi_context);
384 }
385 
tpi_setup_context(machine_context_t * machine_ctx)386 void tpi_setup_context(machine_context_t *machine_ctx)
387 {
388     tpi_context = lib_malloc(sizeof(tpi_context_t));
389 
390     tpi_context->prv = NULL;
391 
392     tpi_context->context = (void *)machine_ctx;
393 
394     tpi_context->rmw_flag = &maincpu_rmw_flag;
395     tpi_context->clk_ptr = &maincpu_clk;
396 
397     tpi_context->myname = lib_msprintf("TPI");
398 
399     tpicore_setup_context(tpi_context);
400 
401     tpi_context->store_pa = store_pa;
402     tpi_context->store_pb = store_pb;
403     tpi_context->store_pc = store_pc;
404     tpi_context->read_pa = read_pa;
405     tpi_context->read_pb = read_pb;
406     tpi_context->read_pc = read_pc;
407     tpi_context->undump_pa = undump_pa;
408     tpi_context->undump_pb = undump_pb;
409     tpi_context->undump_pc = undump_pc;
410     tpi_context->reset = reset;
411     tpi_context->set_ca = set_ca;
412     tpi_context->set_cb = set_cb;
413     tpi_context->set_int = set_int;
414     tpi_context->restore_int = restore_int;
415 }
416 
tpi_passthrough_changed(export_t * ex)417 void tpi_passthrough_changed(export_t *ex)
418 {
419     tpi_extexrom = ex->exrom;
420     tpi_extgame = ex->game;
421     DBG(("IEEE488 passthrough changed exrom: %d game: %d\n", tpi_extexrom, tpi_extgame));
422 
423     cart_set_port_game_slot0(tpi_extgame);
424     cart_port_config_changed_slot0();
425 }
426 
427 /* ---------------------------------------------------------------------*/
428 
429 static char *ieee488_filename = NULL;
430 
set_ieee488_enabled(int value,void * param)431 static int set_ieee488_enabled(int value, void *param)
432 {
433     int val = value ? 1 : 0;
434 
435     DBG(("IEEE: set_enabled: (%p) '%s' %d to %d\n", param, ieee488_filename, ieee488_enabled, val));
436     if (ieee488_enabled && !val) {
437         cart_power_off();
438 #ifdef DEBUGTPI
439         if (tpi_list_item == NULL) {
440             DBG(("IEEE: BUG: ieee488_enabled == 1 and tpi_list_item == NULL ?!\n"));
441         }
442 #endif
443         lib_free(tpi_rom);
444         tpi_rom = NULL;
445         export_remove(&export_res);
446         io_source_unregister(tpi_list_item);
447         tpi_list_item = NULL;
448         ieee488_enabled = 0;
449         DBG(("IEEE: set_enabled unregistered\n"));
450     } else if (!ieee488_enabled && val) {
451         if (tpi_rom == NULL) {
452             tpi_rom = lib_malloc(TPI_ROM_SIZE);
453         }
454         if (param) {
455             /* if the param is != NULL, then we should load the default image file */
456             if (ieee488_filename) {
457                 if (*ieee488_filename) {
458                     DBG(("IEEE: attach default image\n"));
459                     if (cartridge_attach_image(CARTRIDGE_IEEE488, ieee488_filename) < 0) {
460                         DBG(("IEEE: set_enabled did not register\n"));
461                         lib_free(tpi_rom);
462                         tpi_rom = NULL;
463                         return -1;
464                     }
465                     /* ieee488_enabled = 1; */ /* cartridge_attach_image will end up calling set_ieee488_enabled again */
466                     return 0;
467                 }
468             }
469         } else {
470             cart_power_off();
471             /* if the param is == NULL, then we should actually set the resource */
472             if (export_add(&export_res) < 0) {
473                 DBG(("IEEE: set_enabled did not register\n"));
474                 lib_free(tpi_rom);
475                 tpi_rom = NULL;
476                 return -1;
477             } else {
478                 DBG(("IEEE: set_enabled registered\n"));
479                 tpi_list_item = io_source_register(&tpi_io2_device);
480                 ieee488_enabled = 1;
481             }
482         }
483     }
484 
485     DBG(("IEEE: set_enabled done: '%s' %d : %d\n", ieee488_filename, val, ieee488_enabled));
486     return 0;
487 }
488 
set_ieee488_filename(const char * name,void * param)489 static int set_ieee488_filename(const char *name, void *param)
490 {
491     int enabled;
492 
493     if (name != NULL && *name != '\0') {
494         if (util_check_filename_access(name) < 0) {
495             return -1;
496         }
497     }
498     DBG(("IEEE: set_name: %d '%s'\n", ieee488_enabled, ieee488_filename));
499 
500     util_string_set(&ieee488_filename, name);
501     resources_get_int("IEEE488", &enabled);
502 
503     if (set_ieee488_enabled(enabled, (void*)1) < 0) {
504         lib_free(ieee488_filename);
505         ieee488_filename = NULL;
506         DBG(("IEEE: set_name done: %d '%s'\n", ieee488_enabled, ieee488_filename));
507         return -1;
508     }
509 
510     DBG(("IEEE: set_name done: %d '%s'\n", ieee488_enabled, ieee488_filename));
511     return 0;
512 }
513 
514 static const resource_string_t resources_string[] = {
515     { "IEEE488Image", "", RES_EVENT_NO, NULL,
516       &ieee488_filename, set_ieee488_filename, NULL },
517     RESOURCE_STRING_LIST_END
518 };
519 
520 static const resource_int_t resources_int[] = {
521     { "IEEE488", 0, RES_EVENT_SAME, NULL,
522       &ieee488_enabled, set_ieee488_enabled, (void *)1 },
523     RESOURCE_INT_LIST_END
524 };
525 
tpi_resources_init(void)526 int tpi_resources_init(void)
527 {
528     if (resources_register_string(resources_string) < 0) {
529         return -1;
530     }
531     return resources_register_int(resources_int);
532 }
533 
tpi_resources_shutdown(void)534 void tpi_resources_shutdown(void)
535 {
536     lib_free(ieee488_filename);
537     ieee488_filename = NULL;
538 }
539 
540 /* ------------------------------------------------------------------------- */
541 
542 static const cmdline_option_t cmdline_options[] =
543 {
544     { "-ieee488", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
545       NULL, NULL, "IEEE488", (resource_value_t)1,
546       NULL, "Enable the IEEE488 interface emulation" },
547     { "+ieee488", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
548       NULL, NULL, "IEEE488", (resource_value_t)0,
549       NULL, "Disable the IEEE488 interface emulation" },
550     { "-ieee488image", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
551       NULL, NULL, "IEEE488Image", NULL,
552       "<Name>", "specify IEEE488 interface image name" },
553     CMDLINE_LIST_END
554 };
555 
tpi_cmdline_options_init(void)556 int tpi_cmdline_options_init(void)
557 {
558     return cmdline_register_options(cmdline_options);
559 }
560 
561 /* ---------------------------------------------------------------------*/
562 
tpi_get_file_name(void)563 const char *tpi_get_file_name(void)
564 {
565     return ieee488_filename;
566 }
567 
tpi_config_setup(uint8_t * rawcart)568 void tpi_config_setup(uint8_t *rawcart)
569 {
570     DBG(("TPI: config_setup\n"));
571     memcpy(tpi_rom, rawcart, TPI_ROM_SIZE);
572 }
573 
tpi_mmu_translate(unsigned int addr,uint8_t ** base,int * start,int * limit)574 int tpi_mmu_translate(unsigned int addr, uint8_t **base, int *start, int *limit)
575 {
576     if (rom_enabled) {
577         switch (addr & 0xf000) {
578             case 0x9000:
579                 *base = tpi_rom - 0x9000;
580                 *start = 0x9000;
581                 *limit = 0x9ffd;
582                 return CART_READ_VALID;
583             case 0x8000:
584                 *base = tpi_rom - 0x8000;
585                 *start = 0x8000;
586                 *limit = 0x8ffd;
587                 return CART_READ_VALID;
588             default:
589                 break;
590         }
591     }
592     return CART_READ_THROUGH;
593 }
594 
tpi_config_init(export_t * ex)595 void tpi_config_init(export_t *ex)
596 {
597     DBG(("TPI: tpi_config_init\n"));
598 
599     tpi_extexrom = ex->exrom;
600     tpi_extgame = ex->game;
601 
602     cart_set_port_exrom_slot0(1);
603     cart_set_port_game_slot0(tpi_extgame);
604     cart_port_config_changed_slot0();
605     rom_enabled = 1;
606 }
607 
tpi_common_attach(void)608 static int tpi_common_attach(void)
609 {
610     DBG(("TPI: tpi_common_attach\n"));
611     return set_ieee488_enabled(1, NULL);
612 }
613 
tpi_bin_attach(const char * filename,uint8_t * rawcart)614 int tpi_bin_attach(const char *filename, uint8_t *rawcart)
615 {
616     DBG(("TPI: tpi_bin_attach\n"));
617 
618     if (util_file_load(filename, rawcart, TPI_ROM_SIZE, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
619         return -1;
620     }
621     return tpi_common_attach();
622 }
623 
tpi_crt_attach(FILE * fd,uint8_t * rawcart)624 int tpi_crt_attach(FILE *fd, uint8_t *rawcart)
625 {
626     crt_chip_header_t chip;
627 
628     if (crt_read_chip_header(&chip, fd)) {
629         return -1;
630     }
631 
632     if (chip.size != TPI_ROM_SIZE) {
633         return -1;
634     }
635 
636     if (crt_read_chip(rawcart, 0, &chip, fd)) {
637         return -1;
638     }
639 
640     return tpi_common_attach();
641 }
642 
tpi_detach(void)643 void tpi_detach(void)
644 {
645     set_ieee488_enabled(0, NULL);
646 }
647 
tpi_enable(void)648 int tpi_enable(void)
649 {
650     return set_ieee488_enabled(1, (void*)1);
651 }
652 
tpi_disable(void)653 int tpi_disable(void)
654 {
655     return set_ieee488_enabled(0, (void*)1);
656 }
657 
658 
659 /* ---------------------------------------------------------------------*/
660 
tpi_snapshot_read_module(struct snapshot_s * s)661 int tpi_snapshot_read_module(struct snapshot_s *s)
662 {
663     if (tpicore_snapshot_read_module(tpi_context, s) < 0) {
664         ieee488_enabled = 0;
665         return -1;
666     } else {
667         ieee488_enabled = 1;
668     }
669     return 0;
670 }
671 
tpi_snapshot_write_module(struct snapshot_s * s)672 int tpi_snapshot_write_module(struct snapshot_s *s)
673 {
674     if (tpicore_snapshot_write_module(tpi_context, s) < 0) {
675         return -1;
676     }
677     return 0;
678 }
679