1 /*
2 * gmod3.c - Cartridge handling, GMod3 cart.
3 *
4 * Written by
5 * groepaz <groepaz@gmx.net>
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 <stdio.h>
30 #include <string.h>
31
32 #include "archdep.h"
33 #include "c64cart.h"
34 #define CARTRIDGE_INCLUDE_SLOTMAIN_API
35 #include "c64cartsystem.h"
36 #undef CARTRIDGE_INCLUDE_SLOTMAIN_API
37 #include "c64mem.h"
38 #include "c64pla.h"
39 #include "cartio.h"
40 #include "cartridge.h"
41 #include "cmdline.h"
42 #include "crt.h"
43 #include "export.h"
44 #include "lib.h"
45 #include "maincpu.h"
46 #include "monitor.h"
47 #include "resources.h"
48 #include "spi-flash.h"
49 #include "snapshot.h"
50 #include "types.h"
51 #include "util.h"
52 #include "vicii-phi1.h"
53
54 #define CARTRIDGE_INCLUDE_PRIVATE_API
55 #include "gmod3.h"
56 #undef CARTRIDGE_INCLUDE_PRIVATE_API
57
58 /*
59 GMod3 (Individual Computers)
60
61 2/4/8/16Mb serial Flash
62
63 see http://wiki.icomp.de/wiki/GMod3
64
65 IO1
66
67 on write:
68
69 $DE00 Bank 0- 255 all versions
70 $DE01 Bank 256- 511 4MB and higher versions only
71 $DE02 Bank 512- 767 8MB and higher versions only
72 $DE03 Bank 768-1023 8MB and higher versions only
73 $DE04 Bank 1024-1279 16MB version only
74 $DE05 Bank 1280-1535 16MB version only
75 $DE06 Bank 1536-1791 16MB version only
76 $DE07 Bank 1792-2047 16MB version only
77
78 the upper 3 bits of the bank are determined from the address, ie A0-A2
79
80 $DE08 bit7 1: bitbang mode enabled
81 bit6 exrom
82 bit5 hw vector replacing enabled
83
84 on read:
85
86 $DE00...$DE07 bit 0-7 - lower 8 bits of the current bank
87 $DE08...$DE0F bit 0,1,2 - upper 3 bits of the current bank
88
89 if bitbang mode is enabled:
90
91 on write:
92
93 $DE00 bit6 Flash CS
94 bit5 Flash clock
95 bit4 Flash Din
96
97 on read:
98
99 $DExx bit7 Flash Dout
100 */
101
102 /* #define DEBUGGMOD3 */
103
104 #ifdef DEBUGGMOD3
105 #define DBG(x) printf x
106 #else
107 #define DBG(x)
108 #endif
109
110 #define GMOD3_2MB_FLASH_SIZE (2*1024*1024)
111 #define GMOD3_4MB_FLASH_SIZE (4*1024*1024)
112 #define GMOD3_8MB_FLASH_SIZE (8*1024*1024)
113 #define GMOD3_16MB_FLASH_SIZE (16*1024*1024)
114
115 static uint8_t gmod3_rom[GMOD3_16MB_FLASH_SIZE]; /* FIXME, should not be static */
116 static uint32_t gmod3_flashsize = 0;
117
118 static int gmod3_enabled = 0;
119
120 static int gmod3_bitbang_enabled = 0;
121 static int gmod3_vectors_enabled = 0;
122
123 /* current GAME/EXROM mode */
124 static int gmod3_cmode = CMODE_8KGAME;
125
126 /* current bank */
127 static int gmod3_bank = 0;
128
129 static char *gmod3_filename = NULL;
130 static int gmod3_filetype = 0;
131
132 static char *gmod3_flash_filename = NULL;
133 static int gmod3_flash_write = 1;
134
135 static int eeprom_cs = 1; /* active low */
136 static int eeprom_data_in = 0;
137 static int eeprom_data_out = 0;
138 static int eeprom_clock = 0;
139
140 static const char STRING_GMOD3[] = CARTRIDGE_NAME_GMOD3;
141
142 /* ---------------------------------------------------------------------*/
143
144 /* some prototypes are needed */
145 static uint8_t gmod3_io1_read(uint16_t addr);
146 static uint8_t gmod3_io1_peek(uint16_t addr);
147 static void gmod3_io1_store(uint16_t addr, uint8_t value);
148 static int gmod3_dump(void);
149
150 static io_source_t gmod3_io1_device = {
151 CARTRIDGE_NAME_GMOD3, /* name of the device */
152 IO_DETACH_CART, /* use cartridge ID to detach the device when involved in a read-collision */
153 IO_DETACH_NO_RESOURCE, /* does not use a resource for detach */
154 0xde00, 0xdeff, 0xff, /* range for the device, address is ignored, reg:$df00, mirrors:$de01-$deff */
155 0, /* read validity is determined by the device upon a read */
156 gmod3_io1_store, /* store function */
157 NULL, /* NO poke function */
158 gmod3_io1_read, /* read function */
159 gmod3_io1_peek, /* peek function */
160 gmod3_dump, /* device state information dump function */
161 CARTRIDGE_GMOD3, /* cartridge ID */
162 IO_PRIO_NORMAL, /* normal priority, device read needs to be checked for collisions */
163 0 /* insertion order, gets filled in by the registration function */
164 };
165
166 static io_source_list_t *gmod3_io1_list_item = NULL;
167
168 static const export_resource_t export_res = {
169 CARTRIDGE_NAME_GMOD3, 1, 1, &gmod3_io1_device, NULL, CARTRIDGE_GMOD3
170 };
171
172 /* ---------------------------------------------------------------------*/
173
gmod3_io1_read(uint16_t addr)174 uint8_t gmod3_io1_read(uint16_t addr)
175 {
176 gmod3_io1_device.io_source_valid = 0;
177 /* DBG(("io1 r %04x (cs:%d)\n", addr, eeprom_cs)); */
178
179 if (gmod3_bitbang_enabled) {
180 if (eeprom_cs == 0) { /* active low */
181 gmod3_io1_device.io_source_valid = 1;
182 eeprom_data_out = spi_flash_read_data() << 7;
183 return eeprom_data_out;
184 }
185 } else {
186 if ((addr >= 0x00) && (addr <= 0x07)) {
187 gmod3_io1_device.io_source_valid = 1;
188 return gmod3_bank & 0xff;
189 } else if ((addr >= 0x08) && (addr <= 0x0f)) {
190 gmod3_io1_device.io_source_valid = 1;
191 return (gmod3_bank & 0x0700) >> 8;
192 }
193 }
194 return 0; /* FIXME */
195 }
196
gmod3_io1_peek(uint16_t addr)197 uint8_t gmod3_io1_peek(uint16_t addr)
198 {
199 if (gmod3_bitbang_enabled) {
200 if (eeprom_cs == 0) { /* active low */
201 return eeprom_data_out;
202 }
203 } else {
204 if ((addr >= 0x00) && (addr <= 0x07)) {
205 return gmod3_bank & 0xff;
206 } else if ((addr >= 0x08) && (addr <= 0x0f)) {
207 return (gmod3_bank & 0x0700) >> 8;
208 }
209 }
210 return 0;
211 }
212
gmod3_io1_store(uint16_t addr,uint8_t value)213 void gmod3_io1_store(uint16_t addr, uint8_t value)
214 {
215 int mode = CMODE_WRITE;
216
217 DBG(("io1 w %04x %02x\n", addr, value));
218
219 addr &= 0xff;
220
221 /* banking */
222 if ((addr >= 0x00) && (addr <= 0x07)) {
223 if (gmod3_bitbang_enabled) {
224 eeprom_cs = ((value >> 6) & 1); /* active low */
225 eeprom_clock = (value >> 5) & 1;
226 eeprom_data_in = (value >> 4) & 1;
227
228 DBG(("io1 w %04x %02x (cs:%d data:%d clock:%d)\n",
229 addr, value, eeprom_cs, eeprom_data_in, eeprom_clock));
230 } else {
231 gmod3_bank = value + ((addr & 0x07) << 8);
232 DBG(("io1 w %04x %02x (bank: %d)\n",
233 addr, value, gmod3_bank));
234 }
235 } else if (addr == 0x08) {
236 gmod3_bitbang_enabled = (value >> 7) & 1;
237 gmod3_vectors_enabled = (value >> 5) & 1;
238 if ((value & 0x40) == 0x00) {
239 if (gmod3_vectors_enabled) {
240 gmod3_cmode = CMODE_ULTIMAX;
241 } else {
242 gmod3_cmode = CMODE_8KGAME;
243 }
244 } else if ((value & 0x40) == 0x40) {
245 gmod3_cmode = CMODE_RAM;
246 }
247 DBG(("io1 w %04x %02x (bitbang: %d vectors: %d mode: %d)\n",
248 addr, value, gmod3_bitbang_enabled, gmod3_vectors_enabled, gmod3_cmode));
249 }
250
251 spi_flash_write_select((uint8_t)eeprom_cs);
252 if (eeprom_cs == 0) { /* active low */
253 spi_flash_write_data((uint8_t)(eeprom_data_in));
254 spi_flash_write_clock((uint8_t)(eeprom_clock));
255 }
256 cart_config_changed_slotmain(gmod3_cmode,
257 (uint8_t)(gmod3_cmode | (gmod3_bank << CMODE_BANK_SHIFT)), mode);
258 }
259
260 /* ---------------------------------------------------------------------*/
261
gmod3_roml_read(uint16_t addr)262 uint8_t gmod3_roml_read(uint16_t addr)
263 {
264 int mem_config = ((~pport.dir | pport.data) & 0x7);
265 if (!gmod3_vectors_enabled) {
266 return gmod3_rom[(addr & 0x1fff) + (gmod3_bank << 13)];
267 }
268 /* handle fake ultimax */
269 if ((mem_config == 7) || (mem_config == 3)) {
270 return gmod3_rom[(addr & 0x1fff) + (gmod3_bank << 13)];
271 }
272 return ram_read(addr);
273 }
274
275 static uint8_t vectors[8] = { 0x08, 0x00, 0x08, 0x00, 0x0c, 0x80, 0x0c, 0x00 };
276
gmod3_romh_read(uint16_t addr)277 uint8_t gmod3_romh_read(uint16_t addr)
278 {
279 DBG(("gmod3_romh_read %04x\n", addr));
280 if (addr >= 0xfff8 && addr <= 0xffff) {
281 return vectors[addr & 7];
282 }
283 return mem_read_without_ultimax(addr);
284 }
285
286 /* VIC reads */
gmod3_romh_phi1_read(uint16_t addr,uint8_t * value)287 int gmod3_romh_phi1_read(uint16_t addr, uint8_t *value)
288 {
289 return CART_READ_C64MEM;
290 }
291 /* CPU reads */
gmod3_romh_phi2_read(uint16_t addr,uint8_t * value)292 int gmod3_romh_phi2_read(uint16_t addr, uint8_t *value)
293 {
294 return CART_READ_C64MEM;
295 }
296
gmod3_peek_mem(export_t * ex,uint16_t addr,uint8_t * value)297 int gmod3_peek_mem(export_t *ex, uint16_t addr, uint8_t *value)
298 {
299 if (addr >= 0x8000 && addr <= 0x9fff) {
300 *value = gmod3_roml_read(addr);
301 return CART_READ_VALID;
302 }
303 if (gmod3_vectors_enabled) {
304 if (addr >= 0xfff8 && addr <= 0xffff) {
305 *value = gmod3_romh_read(addr);
306 return CART_READ_VALID;
307 }
308 }
309 return CART_READ_THROUGH;
310 }
311
gmod3_mmu_translate(unsigned int addr,uint8_t ** base,int * start,int * limit)312 void gmod3_mmu_translate(unsigned int addr, uint8_t **base, int *start, int *limit)
313 {
314 /* TODO */
315 *base = NULL;
316 *start = 0;
317 *limit = 0;
318 }
319
320 /* ---------------------------------------------------------------------*/
321
gmod3_dump(void)322 static int gmod3_dump(void)
323 {
324 /* FIXME: incomplete */
325 mon_out("status: %s\n", gmod3_cmode == CMODE_RAM ? "disabled" : "8k Game");
326 mon_out("ROM bank: %d\n", gmod3_bank);
327 mon_out("bitbang mode is %s\n", gmod3_bitbang_enabled ? "enabled" : "disabled");
328 mon_out("hw vectors are %s\n", gmod3_vectors_enabled ? "enabled" : "disabled");
329 mon_out("EEPROM CS: %d clock: %d data from flash: %d data to flash: %d \n",
330 eeprom_cs, eeprom_clock, eeprom_data_out, eeprom_data_in);
331
332 return 0;
333 }
334
335 /* ---------------------------------------------------------------------*/
336
gmod3_config_init(void)337 void gmod3_config_init(void)
338 {
339 gmod3_cmode = CMODE_8KGAME;
340 cart_config_changed_slotmain((uint8_t)gmod3_cmode, (uint8_t)gmod3_cmode, CMODE_READ);
341 eeprom_cs = 1; /* active low */
342 spi_flash_write_select((uint8_t)eeprom_cs);
343 }
344
gmod3_reset(void)345 void gmod3_reset(void)
346 {
347 gmod3_cmode = CMODE_8KGAME;
348 cart_config_changed_slotmain((uint8_t)gmod3_cmode, (uint8_t)gmod3_cmode, CMODE_READ);
349 eeprom_cs = 1; /* active low */
350 spi_flash_write_select((uint8_t)eeprom_cs);
351 gmod3_bitbang_enabled = 0;
352 gmod3_vectors_enabled = 0; /* FIXME: this can be enabled at reset as a factory option */
353 gmod3_bank = 0;
354 }
355
gmod3_config_setup(uint8_t * rawcart)356 void gmod3_config_setup(uint8_t *rawcart)
357 {
358 gmod3_cmode = CMODE_8KGAME;
359 cart_config_changed_slotmain((uint8_t)gmod3_cmode, (uint8_t)gmod3_cmode, CMODE_READ);
360
361 spi_flash_set_image(gmod3_rom, gmod3_flashsize);
362 memcpy(gmod3_rom, rawcart, GMOD3_16MB_FLASH_SIZE);
363 }
364
365 /* ---------------------------------------------------------------------*/
366
set_gmod3_flash_write(int val,void * param)367 static int set_gmod3_flash_write(int val, void *param)
368 {
369 gmod3_flash_write = val ? 1 : 0;
370
371 return 0;
372 }
373
374 static const resource_int_t resources_int[] = {
375 { "GMod3FlashWrite", 1, RES_EVENT_NO, NULL,
376 &gmod3_flash_write, set_gmod3_flash_write, NULL },
377 RESOURCE_INT_LIST_END
378 };
379
gmod3_resources_init(void)380 int gmod3_resources_init(void)
381 {
382 return resources_register_int(resources_int);
383 }
384
gmod3_resources_shutdown(void)385 void gmod3_resources_shutdown(void)
386 {
387 lib_free(gmod3_flash_filename);
388 }
389
390 /* ------------------------------------------------------------------------- */
391
392 static const cmdline_option_t cmdline_options[] =
393 {
394 { "-gmod3flashwrite", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
395 NULL, NULL, "GMod3FlashWrite", (resource_value_t)1,
396 NULL, "Enable saving of the GMod3 ROM at exit" },
397 { "+gmod3flashwrite", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
398 NULL, NULL, "GMod3FlashWrite", (resource_value_t)0,
399 NULL, "Disable saving of the GMod3 ROM at exit" },
400 CMDLINE_LIST_END
401 };
402
gmod3_cmdline_options_init(void)403 int gmod3_cmdline_options_init(void)
404 {
405 return cmdline_register_options(cmdline_options);
406 }
407
gmod3_common_attach(void)408 static int gmod3_common_attach(void)
409 {
410 if (export_add(&export_res) < 0) {
411 return -1;
412 }
413
414 gmod3_io1_list_item = io_source_register(&gmod3_io1_device);
415
416 gmod3_enabled = 1;
417
418 return 0;
419 }
420
gmod3_bin_attach(const char * filename,uint8_t * rawcart)421 int gmod3_bin_attach(const char *filename, uint8_t *rawcart)
422 {
423 gmod3_filetype = 0;
424 gmod3_filename = NULL;
425 gmod3_flashsize = 0;
426 memset(rawcart, 0xff, GMOD3_16MB_FLASH_SIZE);
427
428 if (util_file_load(filename, rawcart, GMOD3_16MB_FLASH_SIZE, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
429 if (util_file_load(filename, rawcart, GMOD3_8MB_FLASH_SIZE, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
430 if (util_file_load(filename, rawcart, GMOD3_4MB_FLASH_SIZE, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
431 if (util_file_load(filename, rawcart, GMOD3_2MB_FLASH_SIZE, UTIL_FILE_LOAD_SKIP_ADDRESS) < 0) {
432 return -1;
433 } else {
434 gmod3_flashsize = GMOD3_2MB_FLASH_SIZE;
435 }
436 } else {
437 gmod3_flashsize = GMOD3_4MB_FLASH_SIZE;
438 }
439 } else {
440 gmod3_flashsize = GMOD3_8MB_FLASH_SIZE;
441 }
442 } else {
443 gmod3_flashsize = GMOD3_16MB_FLASH_SIZE;
444 }
445
446 gmod3_filetype = CARTRIDGE_FILETYPE_BIN;
447 gmod3_filename = lib_strdup(filename);
448 return gmod3_common_attach();
449 }
450
gmod3_crt_attach(FILE * fd,uint8_t * rawcart,const char * filename)451 int gmod3_crt_attach(FILE *fd, uint8_t *rawcart, const char *filename)
452 {
453 crt_chip_header_t chip;
454 int i;
455
456 gmod3_filetype = 0;
457 gmod3_filename = NULL;
458 gmod3_flashsize = 0;
459 memset(rawcart, 0xff, GMOD3_16MB_FLASH_SIZE);
460
461 for (i = 0; i < (GMOD3_16MB_FLASH_SIZE / 0x2000); i++) { /* FIXME */
462 if (crt_read_chip_header(&chip, fd)) {
463 break;
464 }
465
466 if (chip.bank >= (GMOD3_16MB_FLASH_SIZE / 0x2000) || chip.size != 0x2000) {
467 return -1;
468 }
469
470 if (crt_read_chip(rawcart, chip.bank << 13, &chip, fd)) {
471 return -1;
472 }
473 }
474
475 i *= 0x2000;
476 if ((i != GMOD3_16MB_FLASH_SIZE) &&
477 (i != GMOD3_8MB_FLASH_SIZE) &&
478 (i != GMOD3_4MB_FLASH_SIZE) &&
479 (i != GMOD3_2MB_FLASH_SIZE)) {
480 return -1;
481 }
482
483 gmod3_flashsize = i;
484 gmod3_filetype = CARTRIDGE_FILETYPE_CRT;
485 gmod3_filename = lib_strdup(filename);
486
487 return gmod3_common_attach();
488 }
489
gmod3_bin_save(const char * filename)490 int gmod3_bin_save(const char *filename)
491 {
492 FILE *fd;
493
494 if (filename == NULL) {
495 return -1;
496 }
497
498 fd = fopen(filename, MODE_WRITE);
499
500 if (fd == NULL) {
501 return -1;
502 }
503
504 if (fwrite(gmod3_rom, 1, gmod3_flashsize, fd) != gmod3_flashsize) {
505 fclose(fd);
506 return -1;
507 }
508
509 fclose(fd);
510
511 return 0;
512 }
513
gmod3_crt_save(const char * filename)514 int gmod3_crt_save(const char *filename)
515 {
516 FILE *fd;
517 crt_chip_header_t chip;
518 uint8_t *data;
519 int i;
520
521 fd = crt_create(filename, CARTRIDGE_GMOD3, 1, 0, STRING_GMOD3);
522
523 if (fd == NULL) {
524 return -1;
525 }
526
527 chip.type = 2;
528 chip.size = 0x2000;
529 chip.start = 0x8000;
530
531 data = gmod3_rom;
532
533 for (i = 0; i < (gmod3_flashsize / 0x2000); i++) {
534 chip.bank = i; /* bank */
535
536 if (crt_write_chip(data, &chip, fd)) {
537 fclose(fd);
538 return -1;
539 }
540 data += 0x2000;
541 }
542
543 fclose(fd);
544 return 0;
545 }
546
gmod3_flush_image(void)547 int gmod3_flush_image(void)
548 {
549 if (gmod3_filetype == CARTRIDGE_FILETYPE_BIN) {
550 return gmod3_bin_save(gmod3_filename);
551 } else if (gmod3_filetype == CARTRIDGE_FILETYPE_CRT) {
552 return gmod3_crt_save(gmod3_filename);
553 }
554 return -1;
555 }
556
gmod3_detach(void)557 void gmod3_detach(void)
558 {
559 if (gmod3_flash_write /* && flashrom_state->flash_dirty */) {
560 gmod3_flush_image();
561 }
562
563 lib_free(gmod3_filename);
564 gmod3_filename = NULL;
565
566 export_remove(&export_res);
567 io_source_unregister(gmod3_io1_list_item);
568 gmod3_io1_list_item = NULL;
569
570 gmod3_enabled = 0;
571 }
572
573 /* ---------------------------------------------------------------------*/
574
575 static const char snap_module_name[] = "CARTGMOD3";
576 #define SNAP_MAJOR 0
577 #define SNAP_MINOR 1
578
gmod3_snapshot_write_module(snapshot_t * s)579 int gmod3_snapshot_write_module(snapshot_t *s)
580 {
581 snapshot_module_t *m;
582
583 m = snapshot_module_create(s, snap_module_name, SNAP_MAJOR, SNAP_MINOR);
584
585 if (m == NULL) {
586 return -1;
587 }
588
589 if (0
590 || SMW_B(m, (uint8_t)gmod3_cmode) < 0
591 || SMW_B(m, (uint8_t)gmod3_bank) < 0
592 || SMW_BA(m, gmod3_rom, GMOD3_16MB_FLASH_SIZE) < 0
593 ) {
594 snapshot_module_close(m);
595 return -1;
596 }
597
598 snapshot_module_close(m);
599
600 return spi_flash_snapshot_write_module(s);
601 }
602
gmod3_snapshot_read_module(snapshot_t * s)603 int gmod3_snapshot_read_module(snapshot_t *s)
604 {
605 uint8_t vmajor, vminor;
606 snapshot_module_t *m;
607
608 m = snapshot_module_open(s, snap_module_name, &vmajor, &vminor);
609
610 if (m == NULL) {
611 return -1;
612 }
613
614 /* reject snapshot modules newer than what we can handle (this VICE is too old) */
615 if (snapshot_version_is_bigger(vmajor, vminor, SNAP_MAJOR, SNAP_MINOR)) {
616 snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
617 goto fail;
618 }
619
620 /* reject snapshot modules older than what we can handle (the snapshot is too old) */
621 if (snapshot_version_is_smaller(vmajor, vminor, SNAP_MAJOR, SNAP_MINOR)) {
622 snapshot_set_error(SNAPSHOT_MODULE_INCOMPATIBLE);
623 goto fail;
624 }
625
626 if (0
627 || SMR_B_INT(m, &gmod3_cmode) < 0
628 || SMR_B_INT(m, &gmod3_bank) < 0
629 || SMR_BA(m, gmod3_rom, GMOD3_16MB_FLASH_SIZE) < 0) {
630 goto fail;
631 }
632
633 snapshot_module_close(m);
634
635 if (spi_flash_snapshot_read_module(s) < 0) {
636 return -1;
637 }
638
639 gmod3_common_attach();
640
641 /* set filetype to none */
642 gmod3_filename = NULL;
643 gmod3_filetype = 0;
644
645 return 0;
646
647 fail:
648 snapshot_module_close(m);
649 return -1;
650 }
651