1 /*
2 * libtilemcore - Graphing calculator emulation library
3 *
4 * Copyright (C) 2009-2012 Benjamin Moody
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "tilem.h"
29 #include "z80.h"
30
set_hw_reg(TilemCalc * calc,const char * name,dword value)31 static void set_hw_reg(TilemCalc* calc, const char* name, dword value)
32 {
33 int i;
34
35 for (i = 0; i < calc->hw.nhwregs; i++) {
36 if (!strcmp(name, calc->hw.hwregnames[i])) {
37 calc->hwregs[i] = value;
38 return;
39 }
40 }
41
42 tilem_warning(calc, "Unknown hwreg %s", name);
43 }
44
get_timer_name(TilemCalc * calc,int id)45 static const char* get_timer_name(TilemCalc* calc, int id)
46 {
47 if (id == TILEM_TIMER_LCD_DELAY)
48 return "lcddelay";
49 else if (id == TILEM_TIMER_FLASH_DELAY)
50 return "flashdelay";
51 else if (id == TILEM_TIMER_LINK_ASSIST)
52 return "linkassist";
53 else if (id == TILEM_TIMER_USER1)
54 return "user1";
55 else if (id == TILEM_TIMER_USER2)
56 return "user2";
57 else if (id == TILEM_TIMER_USER3)
58 return "user3";
59 else if (id <= TILEM_NUM_SYS_TIMERS)
60 abort();
61
62 id -= TILEM_NUM_SYS_TIMERS + 1;
63 if (id < calc->hw.nhwtimers)
64 return calc->hw.hwtimernames[id];
65 else
66 return NULL;
67 }
68
set_ptimer(TilemCalc * calc,const char * name,dword value,dword period,int rt)69 static void set_ptimer(TilemCalc* calc, const char* name, dword value,
70 dword period, int rt)
71 {
72 int i;
73 const char* tname;
74
75 for (i = 1; i <= calc->z80.ntimers; i++) {
76 tname = get_timer_name(calc, i);
77 if (tname && !strcmp(name, tname)) {
78 tilem_z80_set_timer(calc, i, value, period, rt);
79 return;
80 }
81 }
82
83 tilem_warning(calc, "Unknown timer %s", name);
84 }
85
load_old_sav_file(TilemCalc * calc,FILE * savfile)86 static int load_old_sav_file(TilemCalc* calc, FILE* savfile)
87 {
88 byte b[76];
89 dword regs[19];
90 int i, le, be, c;
91 unsigned int pageA, pageB;
92
93 /* Read memory mapping */
94
95 if (fread(calc->mempagemap, 1, 4, savfile) < 4)
96 return 1;
97
98 /* Read CPU registers */
99
100 if (fread(b, 1, 76, savfile) < 76)
101 return 1;
102
103 be = le = 0;
104
105 /* determine if file is in big-endian or little-endian
106 format */
107
108 for (i = 0; i < 19; i++) {
109 if (b[i * 4] || b[i * 4 + 1])
110 le++;
111 if (b[i * 4 + 2] || b[i * 4 + 3])
112 be++;
113 }
114
115 if (le > be) {
116 for (i = 0; i < 19; i++) {
117 regs[i] = b[i * 4] + (b[i * 4 + 1] << 8);
118 }
119 }
120 else {
121 for (i = 0; i < 19; i++) {
122 regs[i] = b[i * 4 + 3] + (b[i * 4 + 2] << 8);
123 }
124 }
125
126 calc->z80.r.af.d = regs[0];
127 calc->z80.r.bc.d = regs[1];
128 calc->z80.r.de.d = regs[2];
129 calc->z80.r.hl.d = regs[3];
130 calc->z80.r.ix.d = regs[4];
131 calc->z80.r.iy.d = regs[5];
132 calc->z80.r.pc.d = regs[6];
133 calc->z80.r.sp.d = regs[7];
134 calc->z80.r.af2.d = regs[8];
135 calc->z80.r.bc2.d = regs[9];
136 calc->z80.r.de2.d = regs[10];
137 calc->z80.r.hl2.d = regs[11];
138 calc->z80.r.iff1 = regs[12] ? 1 : 0;
139 calc->z80.r.iff2 = regs[13] ? 1 : 0;
140 calc->z80.r.im = regs[15];
141 calc->z80.r.ir.b.h = regs[16];
142 calc->z80.r.ir.b.l = regs[17];
143 calc->z80.r.r7 = regs[18] & 0x80;
144
145 if (calc->hw.model_id == '2' || calc->hw.model_id == '3') {
146 if (fread(b, 1, 5, savfile) < 5)
147 return 1;
148
149 if (calc->hw.model_id == '3')
150 set_hw_reg(calc, "rom_bank", calc->mempagemap[1] & 0x08);
151 calc->hw.z80_out(calc, 0x02, b[4]);
152 }
153
154 /* Read RAM contents: old save files for TI-82/83/85 store RAM
155 pages in logical rather than physical order */
156
157 if (calc->hw.model_id == '2' || calc->hw.model_id == '3'
158 || calc->hw.model_id == '5') {
159 if (fread(calc->mem + calc->hw.romsize + 0x4000, 1,
160 0x4000, savfile) < 0x4000)
161 return 1;
162 if (fread(calc->mem + calc->hw.romsize, 1,
163 0x4000, savfile) < 0x4000)
164 return 1;
165 }
166 else {
167 if (fread(calc->mem + calc->hw.romsize, 1,
168 calc->hw.ramsize, savfile) < calc->hw.ramsize)
169 return 1;
170 }
171
172 /* Read LCD contents */
173
174 if (calc->hw.flags & TILEM_CALC_HAS_T6A04) {
175 calc->lcd.rowstride = 12; /* old save files only
176 support the visible
177 portion of the screen */
178 if (fread(calc->lcdmem, 1, 768, savfile) < 768)
179 return 1;
180 }
181
182 /* Read additional HW state */
183
184 switch (calc->hw.model_id) {
185 case '1':
186 break;
187
188 case '2':
189 case '3':
190 if ((c = fgetc(savfile)) != EOF)
191 calc->lcd.mode = c;
192 if ((c = fgetc(savfile)) != EOF)
193 calc->lcd.x = c;
194 if ((c = fgetc(savfile)) != EOF)
195 calc->lcd.y = c;
196 break;
197
198 case '5':
199 pageA = calc->mempagemap[1];
200 if (pageA >= 0x08)
201 pageA += 0x38;
202 calc->hw.z80_out(calc, 0x05, pageA);
203
204 if ((c = fgetc(savfile)) != EOF)
205 calc->hw.z80_out(calc, 0x06, c);
206 break;
207
208 case '6':
209 pageA = calc->mempagemap[1];
210 pageB = calc->mempagemap[2];
211 if (pageA >= 0x10)
212 pageA += 0x30;
213 if (pageB >= 0x10)
214 pageB += 0x30;
215
216 calc->hw.z80_out(calc, 0x05, pageA);
217 calc->hw.z80_out(calc, 0x06, pageB);
218 break;
219
220 default: /* TI-73/83+ series */
221 if ((c = fgetc(savfile)) != EOF)
222 calc->lcd.mode = c;
223 if ((c = fgetc(savfile)) != EOF)
224 calc->lcd.x = c;
225 if ((c = fgetc(savfile)) != EOF)
226 calc->lcd.y = c;
227 if ((c = fgetc(savfile)) != EOF)
228 calc->lcd.inc = c;
229
230 if ((c = fgetc(savfile)) == EOF)
231 c = 0;
232 if (c) {
233 pageA = calc->mempagemap[2];
234 pageB = calc->mempagemap[3];
235 calc->hw.z80_out(calc, 0x04, 0x77);
236 }
237 else {
238 pageA = calc->mempagemap[1];
239 pageB = calc->mempagemap[2];
240 calc->hw.z80_out(calc, 0x04, 0x76);
241 }
242
243 if (pageA >= (calc->hw.romsize >> 14))
244 pageA = ((pageA & 0x1f) | calc->hw.rampagemask);
245 if (pageB >= (calc->hw.romsize >> 14))
246 pageB = ((pageB & 0x1f) | calc->hw.rampagemask);
247
248 calc->hw.z80_out(calc, 0x06, pageA);
249 calc->hw.z80_out(calc, 0x07, pageB);
250
251 if ((c = fgetc(savfile)) != EOF)
252 calc->flash.state = c;
253 if ((c = fgetc(savfile)) != EOF)
254 calc->flash.unlock = c;
255
256 if ((c = fgetc(savfile)) != EOF)
257 calc->hw.z80_out(calc, 0x20, c);
258 if ((c = fgetc(savfile)) != EOF)
259 set_hw_reg(calc, "port21", c);
260 if ((c = fgetc(savfile)) != EOF)
261 set_hw_reg(calc, "port22", c);
262 if ((c = fgetc(savfile)) != EOF)
263 set_hw_reg(calc, "port23", c);
264 if ((c = fgetc(savfile)) != EOF)
265 calc->hw.z80_out(calc, 0x27, c);
266 if ((c = fgetc(savfile)) != EOF)
267 calc->hw.z80_out(calc, 0x28, c);
268 break;
269 }
270
271 calc->poweronhalt = calc->lcd.active = 1;
272
273 return 0;
274 }
275
read_sav_line(FILE * savfile,char ** buf)276 static int read_sav_line(FILE* savfile, char **buf)
277 {
278 int c, n, na;
279
280 tilem_free(*buf);
281
282 na = 100;
283 *buf = tilem_malloc_atomic(na);
284 n = 0;
285
286 while ((c = fgetc(savfile)) != EOF) {
287 if (c == '\r' || c == '\n')
288 break;
289
290 n++;
291 if (n >= na) {
292 na = n * 2;
293 *buf = tilem_realloc(*buf, na);
294 }
295
296 if (c == '#')
297 c = 0;
298 (*buf)[n - 1] = c;
299 }
300
301 if (n == 0 && c == EOF) {
302 tilem_free(*buf);
303 *buf = NULL;
304 return 0;
305 }
306 else {
307 (*buf)[n] = 0;
308 return 1;
309 }
310 }
311
parse_sav_definition(char * line,char ** value)312 static int parse_sav_definition(char* line, char** value)
313 {
314 char *p;
315
316 p = strchr(line, '=');
317 if (!p)
318 return 0;
319
320 while (p != line && p[-1] == ' ')
321 p--;
322 *p = 0;
323 p++;
324 while (*p == ' ' || *p == '=')
325 p++;
326 *value = p;
327 return 1;
328 }
329
load_new_sav_file(TilemCalc * calc,FILE * savfile)330 static int load_new_sav_file(TilemCalc* calc, FILE* savfile)
331 {
332 char *buf = NULL;
333 char *p, *q;
334 dword value, length;
335 byte *data;
336 int ok = 0;
337 byte digit;
338 int firstdigit;
339 dword period;
340 int rt;
341
342 while (read_sav_line(savfile, &buf)) {
343 if (!parse_sav_definition(buf, &p))
344 continue;
345
346 if (*p == '{') {
347 p++;
348 if (!strcmp(buf, "RAM")) {
349 length = calc->hw.ramsize;
350 data = calc->ram;
351 }
352 else if (!strcmp(buf, "LCD")) {
353 length = calc->hw.lcdmemsize;
354 data = calc->lcdmem;
355 }
356 else {
357 length = 0;
358 data = NULL;
359 }
360
361 value = 0;
362 firstdigit = 1;
363
364 while (*p != '}') {
365 if (*p == 0 || *p == '#') {
366 if (!read_sav_line(savfile, &buf))
367 return 1;
368 p = buf;
369 continue;
370 }
371
372 if (*p >= '0' && *p <= '9') {
373 digit = *p - '0';
374 p++;
375 }
376 else if (*p >= 'A' && *p <= 'F') {
377 digit = *p + 10 - 'A';
378 p++;
379 }
380 else if (*p >= 'a' && *p <= 'f') {
381 digit = *p + 10 - 'a';
382 p++;
383 }
384 else {
385 p++;
386 continue;
387 }
388
389 if (firstdigit) {
390 value = digit << 4;
391 firstdigit = 0;
392 }
393 else {
394 value |= digit;
395 if (length != 0) {
396 *data = value;
397 data++;
398 length--;
399 }
400 firstdigit = 1;
401 }
402 }
403
404 continue;
405 }
406
407 if (!strcmp(buf, "MODEL")) {
408 q = p;
409 while (*q >= ' ')
410 q++;
411 *q = 0;
412 if (strcmp(p, calc->hw.name)) {
413 tilem_free(buf);
414 return 1;
415 }
416 ok = 1;
417 continue;
418 }
419
420 value = strtol(p, &q, 16);
421
422 /* Persistent timers */
423 if (!strncmp(buf, "timer:", 6)) {
424 while (*q == ' ')
425 q++;
426 if (*q != ',')
427 continue;
428 q++;
429 while (*q == ' ')
430 q++;
431 period = strtol(q, &q, 16);
432
433 while (*q == ' ')
434 q++;
435 if (*q != ',')
436 continue;
437 q++;
438 while (*q == ' ')
439 q++;
440 rt = strtol(q, &q, 16);
441
442 set_ptimer(calc, buf + 6, value, period, rt);
443 continue;
444 }
445
446 /* Z80 */
447 if (!strcmp(buf, "af")) calc->z80.r.af.d = value;
448 else if (!strcmp(buf, "bc")) calc->z80.r.bc.d = value;
449 else if (!strcmp(buf, "de")) calc->z80.r.de.d = value;
450 else if (!strcmp(buf, "hl")) calc->z80.r.hl.d = value;
451 else if (!strcmp(buf, "af'")) calc->z80.r.af2.d = value;
452 else if (!strcmp(buf, "bc'")) calc->z80.r.bc2.d = value;
453 else if (!strcmp(buf, "de'")) calc->z80.r.de2.d = value;
454 else if (!strcmp(buf, "hl'")) calc->z80.r.hl2.d = value;
455 else if (!strcmp(buf, "ix")) calc->z80.r.ix.d = value;
456 else if (!strcmp(buf, "iy")) calc->z80.r.iy.d = value;
457 else if (!strcmp(buf, "pc")) calc->z80.r.pc.d = value;
458 else if (!strcmp(buf, "sp")) calc->z80.r.sp.d = value;
459 else if (!strcmp(buf, "ir")) {
460 calc->z80.r.ir.d = value;
461 calc->z80.r.r7 = value & 0x80;
462 }
463 else if (!strcmp(buf, "wz")) calc->z80.r.wz.d = value;
464 else if (!strcmp(buf, "wz'")) calc->z80.r.wz2.d = value;
465 else if (!strcmp(buf, "iff1")) calc->z80.r.iff1 = value;
466 else if (!strcmp(buf, "iff2")) calc->z80.r.iff2 = value;
467 else if (!strcmp(buf, "im")) calc->z80.r.im = value;
468 else if (!strcmp(buf, "interrupts"))
469 calc->z80.interrupts = value;
470 else if (!strcmp(buf, "clockspeed"))
471 calc->z80.clockspeed = value;
472 else if (!strcmp(buf, "halted")) calc->z80.halted = value;
473
474 /* LCD */
475 else if (!strcmp(buf, "lcd.active"))
476 calc->lcd.active = value;
477 else if (!strcmp(buf, "lcd.addr"))
478 calc->lcd.addr = value;
479 else if (!strcmp(buf, "lcd.rowshift"))
480 calc->lcd.rowshift = value;
481 else if (!strcmp(buf, "lcd.contrast"))
482 calc->lcd.contrast = value;
483 else if (!strcmp(buf, "lcd.inc"))
484 calc->lcd.inc = value;
485 else if (!strcmp(buf, "lcd.mode"))
486 calc->lcd.mode = value;
487 else if (!strcmp(buf, "lcd.x"))
488 calc->lcd.x = value;
489 else if (!strcmp(buf, "lcd.y"))
490 calc->lcd.y = value;
491 else if (!strcmp(buf, "lcd.nextbyte"))
492 calc->lcd.nextbyte = value;
493 else if (!strcmp(buf, "lcd.rowstride"))
494 calc->lcd.rowstride = value;
495 else if (!strcmp(buf, "lcd.busy"))
496 calc->lcd.busy = value;
497
498 /* Link port */
499 else if (!strcmp(buf, "linkport.lines"))
500 calc->linkport.lines = value;
501 else if (!strcmp(buf, "linkport.mode"))
502 calc->linkport.mode = value;
503 else if (!strcmp(buf, "linkport.assistflags"))
504 calc->linkport.assistflags = value;
505 else if (!strcmp(buf, "linkport.assistin"))
506 calc->linkport.assistin = value;
507 else if (!strcmp(buf, "linkport.assistinbits"))
508 calc->linkport.assistinbits = value;
509 else if (!strcmp(buf, "linkport.assistout"))
510 calc->linkport.assistout = value;
511 else if (!strcmp(buf, "linkport.assistoutbits"))
512 calc->linkport.assistoutbits = value;
513 else if (!strcmp(buf, "linkport.assistlastbyte"))
514 calc->linkport.assistlastbyte = value;
515
516 /* Keypad */
517 else if (!strcmp(buf, "keypad.group"))
518 calc->keypad.group = value;
519 else if (!strcmp(buf, "keypad.onkeyint"))
520 calc->keypad.onkeyint = value;
521
522 /* MD5 assist */
523 else if (!strcmp(buf, "md5assist.a"))
524 calc->md5assist.regs[0] = value;
525 else if (!strcmp(buf, "md5assist.b"))
526 calc->md5assist.regs[1] = value;
527 else if (!strcmp(buf, "md5assist.c"))
528 calc->md5assist.regs[2] = value;
529 else if (!strcmp(buf, "md5assist.d"))
530 calc->md5assist.regs[3] = value;
531 else if (!strcmp(buf, "md5assist.x"))
532 calc->md5assist.regs[4] = value;
533 else if (!strcmp(buf, "md5assist.t"))
534 calc->md5assist.regs[5] = value;
535 else if (!strcmp(buf, "md5assist.shift"))
536 calc->md5assist.shift = value;
537 else if (!strcmp(buf, "md5assist.mode"))
538 calc->md5assist.mode = value;
539
540 /* Programmable timers */
541 else if (!strcmp(buf, "usertimer0.frequency"))
542 calc->usertimers[0].frequency = value;
543 else if (!strcmp(buf, "usertimer0.loopvalue"))
544 calc->usertimers[0].loopvalue = value;
545 else if (!strcmp(buf, "usertimer0.status"))
546 calc->usertimers[0].status = value;
547 else if (!strcmp(buf, "usertimer1.frequency"))
548 calc->usertimers[1].frequency = value;
549 else if (!strcmp(buf, "usertimer1.loopvalue"))
550 calc->usertimers[1].loopvalue = value;
551 else if (!strcmp(buf, "usertimer1.status"))
552 calc->usertimers[1].status = value;
553 else if (!strcmp(buf, "usertimer2.frequency"))
554 calc->usertimers[2].frequency = value;
555 else if (!strcmp(buf, "usertimer2.loopvalue"))
556 calc->usertimers[2].loopvalue = value;
557 else if (!strcmp(buf, "usertimer2.status"))
558 calc->usertimers[2].status = value;
559
560 /* Main power */
561 else if (!strcmp(buf, "poweronhalt"))
562 calc->poweronhalt = value;
563
564 /* Battery */
565 else if (!strcmp(buf, "battery"))
566 calc->battery = value;
567
568 /* Memory */
569 else if (!strcmp(buf, "mempagemap0"))
570 calc->mempagemap[0] = value;
571 else if (!strcmp(buf, "mempagemap1"))
572 calc->mempagemap[1] = value;
573 else if (!strcmp(buf, "mempagemap2"))
574 calc->mempagemap[2] = value;
575 else if (!strcmp(buf, "mempagemap3"))
576 calc->mempagemap[3] = value;
577 else if (!strcmp(buf, "flash.unlock"))
578 calc->flash.unlock = value;
579 else if (!strcmp(buf, "flash.state"))
580 calc->flash.state = value;
581 else if (!strcmp(buf, "flash.busy"))
582 calc->flash.busy = value;
583 else if (!strcmp(buf, "flash.progaddr"))
584 calc->flash.progaddr = value;
585 else if (!strcmp(buf, "flash.progbyte"))
586 calc->flash.progbyte = value;
587 else if (!strcmp(buf, "flash.toggles"))
588 calc->flash.toggles = value;
589 else if (!strcmp(buf, "flash.overridegroup"))
590 calc->flash.overridegroup = value;
591
592 else
593 set_hw_reg(calc, buf, value);
594 }
595
596 tilem_free(buf);
597
598 return !ok;
599 }
600
tilem_calc_load_state(TilemCalc * calc,FILE * romfile,FILE * savfile)601 int tilem_calc_load_state(TilemCalc* calc, FILE* romfile, FILE* savfile)
602 {
603 int b;
604 int savtype = 0;
605
606 if (romfile) {
607 if (fread(calc->mem, 1, calc->hw.romsize, romfile)
608 != calc->hw.romsize)
609 return 1;
610 }
611
612 tilem_calc_reset(calc);
613
614 if (savfile) {
615 /* first byte of old save files is always zero */
616 b = fgetc(savfile);
617 fseek(savfile, 0L, SEEK_SET);
618
619 if (b == 0) {
620 if (load_old_sav_file(calc, savfile)) {
621 tilem_calc_reset(calc);
622 return 1;
623 }
624 else
625 savtype = 1;
626 }
627 else {
628 if (load_new_sav_file(calc, savfile)) {
629 tilem_calc_reset(calc);
630 return 1;
631 }
632 else
633 savtype = 2;
634 }
635 }
636
637 if (calc->hw.stateloaded)
638 (*calc->hw.stateloaded)(calc, savtype);
639
640 return 0;
641 }
642
tilem_get_sav_type(FILE * savfile)643 char tilem_get_sav_type(FILE* savfile)
644 {
645 int b;
646 char *buf = NULL, *p, *q;
647 const TilemHardware **models;
648 int nmodels, i;
649 char id = 0;
650
651 tilem_get_supported_hardware(&models, &nmodels);
652
653 /* first byte of old save files is always zero */
654 b = fgetc(savfile);
655 fseek(savfile, 0L, SEEK_SET);
656 if (b == 0)
657 return 0; /* old files give no way to detect model */
658
659 while (read_sav_line(savfile, &buf)) {
660 if (parse_sav_definition(buf, &p)
661 && !strcmp(buf, "MODEL")) {
662 q = p;
663 while (*q >= ' ')
664 q++;
665 *q = 0;
666
667 for (i = 0; i < nmodels; i++)
668 if (!strcmp(p, models[i]->name))
669 id = models[i]->model_id;
670
671 break;
672 }
673 }
674
675 fseek(savfile, 0L, SEEK_SET);
676 tilem_free(buf);
677 return id;
678 }
679
tilem_calc_save_state(TilemCalc * calc,FILE * romfile,FILE * savfile)680 int tilem_calc_save_state(TilemCalc* calc, FILE* romfile, FILE* savfile)
681 {
682 dword i;
683 dword t;
684 int j;
685 const char* tname;
686 unsigned int rowstride;
687
688 if (romfile) {
689 if (fwrite(calc->mem, 1, calc->hw.romsize, romfile)
690 != calc->hw.romsize)
691 return 1;
692 }
693
694 if (savfile) {
695 fprintf(savfile, "# Tilem II State File\n# Version: %s\n",
696 PACKAGE_VERSION);
697 fprintf(savfile, "MODEL = %s\n", calc->hw.name);
698
699 fprintf(savfile, "\n## CPU ##\n");
700 fprintf(savfile, "af = %04X\n", calc->z80.r.af.w.l);
701 fprintf(savfile, "bc = %04X\n", calc->z80.r.bc.w.l);
702 fprintf(savfile, "de = %04X\n", calc->z80.r.de.w.l);
703 fprintf(savfile, "hl = %04X\n", calc->z80.r.hl.w.l);
704 fprintf(savfile, "af' = %04X\n", calc->z80.r.af2.w.l);
705 fprintf(savfile, "bc' = %04X\n", calc->z80.r.bc2.w.l);
706 fprintf(savfile, "de' = %04X\n", calc->z80.r.de2.w.l);
707 fprintf(savfile, "hl' = %04X\n", calc->z80.r.hl2.w.l);
708 fprintf(savfile, "ix = %04X\n", calc->z80.r.ix.w.l);
709 fprintf(savfile, "iy = %04X\n", calc->z80.r.iy.w.l);
710 fprintf(savfile, "pc = %04X\n", calc->z80.r.pc.w.l);
711 fprintf(savfile, "sp = %04X\n", calc->z80.r.sp.w.l);
712 fprintf(savfile, "ir = %04X\n",
713 ((calc->z80.r.ir.w.l & ~0x80) | calc->z80.r.r7));
714 fprintf(savfile, "wz = %04X\n", calc->z80.r.wz.w.l);
715 fprintf(savfile, "wz' = %04X\n", calc->z80.r.wz2.w.l);
716 fprintf(savfile, "iff1 = %X\n", calc->z80.r.iff1);
717 fprintf(savfile, "iff2 = %X\n", calc->z80.r.iff2);
718 fprintf(savfile, "im = %X\n", calc->z80.r.im);
719 fprintf(savfile, "interrupts = %08X\n", calc->z80.interrupts);
720 fprintf(savfile, "clockspeed = %X\n", calc->z80.clockspeed);
721 fprintf(savfile, "halted = %X\n", calc->z80.halted);
722
723 fprintf(savfile, "\n## LCD Driver ##\n");
724 fprintf(savfile, "lcd.active = %X\n",
725 calc->lcd.active);
726 fprintf(savfile, "lcd.contrast = %X\n",
727 calc->lcd.contrast);
728 fprintf(savfile, "lcd.rowstride = %X\n",
729 calc->lcd.rowstride);
730 if (calc->hw.flags & TILEM_CALC_HAS_T6A04) {
731 fprintf(savfile, "lcd.rowshift = %X\n",
732 calc->lcd.rowshift);
733 fprintf(savfile, "lcd.inc = %X\n",
734 calc->lcd.inc);
735 fprintf(savfile, "lcd.mode = %X\n",
736 calc->lcd.mode);
737 fprintf(savfile, "lcd.x = %02X\n",
738 calc->lcd.x);
739 fprintf(savfile, "lcd.y = %02X\n",
740 calc->lcd.y);
741 fprintf(savfile, "lcd.nextbyte = %02X\n",
742 calc->lcd.nextbyte);
743 fprintf(savfile, "lcd.busy = %X\n",
744 calc->lcd.busy);
745 }
746 fprintf(savfile, "lcd.addr = %X\n", calc->lcd.addr);
747
748 if (calc->hw.flags & TILEM_CALC_HAS_LINK) {
749 fprintf(savfile, "\n## Link Port ##\n");
750 fprintf(savfile, "linkport.lines = %X\n",
751 calc->linkport.lines);
752 fprintf(savfile, "linkport.mode = %08X\n",
753 calc->linkport.mode);
754 }
755 if (calc->hw.flags & TILEM_CALC_HAS_LINK_ASSIST) {
756 fprintf(savfile, "linkport.assistflags = %08X\n",
757 calc->linkport.assistflags);
758 fprintf(savfile, "linkport.assistin = %02X\n",
759 calc->linkport.assistin);
760 fprintf(savfile, "linkport.assistinbits = %X\n",
761 calc->linkport.assistinbits);
762 fprintf(savfile, "linkport.assistout = %02X\n",
763 calc->linkport.assistout);
764 fprintf(savfile, "linkport.assistoutbits = %X\n",
765 calc->linkport.assistoutbits);
766 fprintf(savfile, "linkport.assistlastbyte = %02X\n",
767 calc->linkport.assistlastbyte);
768 }
769
770 fprintf(savfile, "\n## Keypad ##\n");
771 fprintf(savfile, "keypad.group = %X\n", calc->keypad.group);
772 fprintf(savfile, "keypad.onkeyint = %X\n",
773 calc->keypad.onkeyint);
774
775 fprintf(savfile, "\n## Memory mapping ##\n");
776 fprintf(savfile, "mempagemap0 = %X\n", calc->mempagemap[0]);
777 fprintf(savfile, "mempagemap1 = %X\n", calc->mempagemap[1]);
778 fprintf(savfile, "mempagemap2 = %X\n", calc->mempagemap[2]);
779 fprintf(savfile, "mempagemap3 = %X\n", calc->mempagemap[3]);
780
781 fprintf(savfile, "\n## Power ##\n");
782 fprintf(savfile, "poweronhalt = %X\n", calc->poweronhalt);
783 fprintf(savfile, "battery = %X\n", calc->battery);
784
785 if (calc->hw.flags & TILEM_CALC_HAS_FLASH) {
786 fprintf(savfile, "\n## Flash ##\n");
787 fprintf(savfile, "flash.unlock = %X\n",
788 calc->flash.unlock);
789 fprintf(savfile, "flash.state = %X\n",
790 calc->flash.state);
791 fprintf(savfile, "flash.busy = %X\n",
792 calc->flash.busy);
793 fprintf(savfile, "flash.progaddr = %X\n",
794 calc->flash.progaddr);
795 fprintf(savfile, "flash.progbyte = %X\n",
796 calc->flash.progbyte);
797 fprintf(savfile, "flash.toggles = %X\n",
798 calc->flash.toggles);
799 fprintf(savfile, "flash.overridegroup = %X\n",
800 calc->flash.overridegroup);
801 }
802
803 if (calc->hw.flags & TILEM_CALC_HAS_MD5_ASSIST) {
804 fprintf(savfile, "\n## MD5 assist ##\n");
805 fprintf(savfile, "md5assist.a = %X\n",
806 calc->md5assist.regs[0]);
807 fprintf(savfile, "md5assist.b = %X\n",
808 calc->md5assist.regs[1]);
809 fprintf(savfile, "md5assist.c = %X\n",
810 calc->md5assist.regs[2]);
811 fprintf(savfile, "md5assist.d = %X\n",
812 calc->md5assist.regs[3]);
813 fprintf(savfile, "md5assist.x = %X\n",
814 calc->md5assist.regs[4]);
815 fprintf(savfile, "md5assist.t = %X\n",
816 calc->md5assist.regs[5]);
817 fprintf(savfile, "md5assist.shift = %X\n",
818 calc->md5assist.shift);
819 fprintf(savfile, "md5assist.mode = %X\n",
820 calc->md5assist.mode);
821 }
822
823 for (j = 0; j < calc->hw.nusertimers; j++) {
824 fprintf(savfile,
825 "\n## Programmable timer %d ##\n", j);
826 fprintf(savfile, "usertimer%d.frequency = %X\n",
827 j, calc->usertimers[j].frequency);
828 fprintf(savfile, "usertimer%d.loopvalue = %X\n",
829 j, calc->usertimers[j].loopvalue);
830 fprintf(savfile, "usertimer%d.status = %X\n",
831 j, calc->usertimers[j].status);
832 }
833
834 fprintf(savfile, "\n## Model-specific ##\n");
835 for (j = 0; j < calc->hw.nhwregs; j++) {
836 fprintf(savfile, "%s = %X\n", calc->hw.hwregnames[j],
837 calc->hwregs[j]);
838 }
839
840 fprintf(savfile, "\n## Timers ##\n");
841 for (j = calc->z80.timer_cpu; j;
842 j = calc->z80.timers[j].next) {
843 tname = get_timer_name(calc, j);
844 if (tname) {
845 t = tilem_z80_get_timer_clocks(calc, j);
846 fprintf(savfile, "timer:%s = %X, %X, 0\n",
847 tname, t, calc->z80.timers[j].period);
848 }
849 }
850 for (j = calc->z80.timer_rt; j;
851 j = calc->z80.timers[j].next) {
852 tname = get_timer_name(calc, j);
853 if (tname) {
854 t = tilem_z80_get_timer_microseconds(calc, j);
855 fprintf(savfile, "timer:%s = %X, %X, 1\n",
856 tname, t, calc->z80.timers[j].period);
857 }
858 }
859
860 fprintf(savfile, "\n## RAM contents ##\n");
861 fprintf(savfile, "RAM = {\n");
862 for (i = 0; i < calc->hw.ramsize; i++) {
863 if (i % 256 == 0) {
864 fprintf(savfile, "# %02X:%04X\n",
865 (i >> 14), (i & 0x3fff));
866 }
867
868 fprintf(savfile, "%02X",
869 calc->mem[i + calc->hw.romsize]);
870 if (i % 32 == 31)
871 fprintf(savfile, "\n");
872 }
873 fprintf(savfile, "}\n## End of RAM contents ##\n");
874
875 if (calc->hw.lcdmemsize) {
876 fprintf(savfile, "\n## LCD contents ##\n");
877 fprintf(savfile, "LCD = {\n");
878 rowstride = calc->lcd.rowstride;
879 if (rowstride == 0)
880 rowstride = 32;
881
882 for (i = 0; i < calc->hw.lcdmemsize; i++) {
883 fprintf(savfile, "%02X", calc->lcdmem[i]);
884 if (i % rowstride == (rowstride - 1))
885 fprintf(savfile, "\n");
886 }
887 fprintf(savfile, "}\n## End of LCD contents ##\n");
888 }
889 }
890
891 return 0;
892 }
893