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