893 lines
23 KiB
C
893 lines
23 KiB
C
/*
|
|
* libtilemcore - Graphing calculator emulation library
|
|
*
|
|
* Copyright (C) 2009-2012 Benjamin Moody
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation; either version 2.1 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "tilem.h"
|
|
#include "z80.h"
|
|
|
|
static void set_hw_reg(TilemCalc* calc, const char* name, dword value)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < calc->hw.nhwregs; i++) {
|
|
if (!strcmp(name, calc->hw.hwregnames[i])) {
|
|
calc->hwregs[i] = value;
|
|
return;
|
|
}
|
|
}
|
|
|
|
tilem_warning(calc, "Unknown hwreg %s", name);
|
|
}
|
|
|
|
static const char* get_timer_name(TilemCalc* calc, int id)
|
|
{
|
|
if (id == TILEM_TIMER_LCD_DELAY)
|
|
return "lcddelay";
|
|
else if (id == TILEM_TIMER_FLASH_DELAY)
|
|
return "flashdelay";
|
|
else if (id == TILEM_TIMER_LINK_ASSIST)
|
|
return "linkassist";
|
|
else if (id == TILEM_TIMER_USER1)
|
|
return "user1";
|
|
else if (id == TILEM_TIMER_USER2)
|
|
return "user2";
|
|
else if (id == TILEM_TIMER_USER3)
|
|
return "user3";
|
|
else if (id <= TILEM_NUM_SYS_TIMERS)
|
|
abort();
|
|
|
|
id -= TILEM_NUM_SYS_TIMERS + 1;
|
|
if (id < calc->hw.nhwtimers)
|
|
return calc->hw.hwtimernames[id];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static void set_ptimer(TilemCalc* calc, const char* name, dword value,
|
|
dword period, int rt)
|
|
{
|
|
int i;
|
|
const char* tname;
|
|
|
|
for (i = 1; i <= calc->z80.ntimers; i++) {
|
|
tname = get_timer_name(calc, i);
|
|
if (tname && !strcmp(name, tname)) {
|
|
tilem_z80_set_timer(calc, i, value, period, rt);
|
|
return;
|
|
}
|
|
}
|
|
|
|
tilem_warning(calc, "Unknown timer %s", name);
|
|
}
|
|
|
|
static int load_old_sav_file(TilemCalc* calc, FILE* savfile)
|
|
{
|
|
byte b[76];
|
|
dword regs[19];
|
|
int i, le, be, c;
|
|
unsigned int pageA, pageB;
|
|
|
|
/* Read memory mapping */
|
|
|
|
if (fread(calc->mempagemap, 1, 4, savfile) < 4)
|
|
return 1;
|
|
|
|
/* Read CPU registers */
|
|
|
|
if (fread(b, 1, 76, savfile) < 76)
|
|
return 1;
|
|
|
|
be = le = 0;
|
|
|
|
/* determine if file is in big-endian or little-endian
|
|
format */
|
|
|
|
for (i = 0; i < 19; i++) {
|
|
if (b[i * 4] || b[i * 4 + 1])
|
|
le++;
|
|
if (b[i * 4 + 2] || b[i * 4 + 3])
|
|
be++;
|
|
}
|
|
|
|
if (le > be) {
|
|
for (i = 0; i < 19; i++) {
|
|
regs[i] = b[i * 4] + (b[i * 4 + 1] << 8);
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < 19; i++) {
|
|
regs[i] = b[i * 4 + 3] + (b[i * 4 + 2] << 8);
|
|
}
|
|
}
|
|
|
|
calc->z80.r.af.d = regs[0];
|
|
calc->z80.r.bc.d = regs[1];
|
|
calc->z80.r.de.d = regs[2];
|
|
calc->z80.r.hl.d = regs[3];
|
|
calc->z80.r.ix.d = regs[4];
|
|
calc->z80.r.iy.d = regs[5];
|
|
calc->z80.r.pc.d = regs[6];
|
|
calc->z80.r.sp.d = regs[7];
|
|
calc->z80.r.af2.d = regs[8];
|
|
calc->z80.r.bc2.d = regs[9];
|
|
calc->z80.r.de2.d = regs[10];
|
|
calc->z80.r.hl2.d = regs[11];
|
|
calc->z80.r.iff1 = regs[12] ? 1 : 0;
|
|
calc->z80.r.iff2 = regs[13] ? 1 : 0;
|
|
calc->z80.r.im = regs[15];
|
|
calc->z80.r.ir.b.h = regs[16];
|
|
calc->z80.r.ir.b.l = regs[17];
|
|
calc->z80.r.r7 = regs[18] & 0x80;
|
|
|
|
if (calc->hw.model_id == '2' || calc->hw.model_id == '3') {
|
|
if (fread(b, 1, 5, savfile) < 5)
|
|
return 1;
|
|
|
|
if (calc->hw.model_id == '3')
|
|
set_hw_reg(calc, "rom_bank", calc->mempagemap[1] & 0x08);
|
|
calc->hw.z80_out(calc, 0x02, b[4]);
|
|
}
|
|
|
|
/* Read RAM contents: old save files for TI-82/83/85 store RAM
|
|
pages in logical rather than physical order */
|
|
|
|
if (calc->hw.model_id == '2' || calc->hw.model_id == '3'
|
|
|| calc->hw.model_id == '5') {
|
|
if (fread(calc->mem + calc->hw.romsize + 0x4000, 1,
|
|
0x4000, savfile) < 0x4000)
|
|
return 1;
|
|
if (fread(calc->mem + calc->hw.romsize, 1,
|
|
0x4000, savfile) < 0x4000)
|
|
return 1;
|
|
}
|
|
else {
|
|
if (fread(calc->mem + calc->hw.romsize, 1,
|
|
calc->hw.ramsize, savfile) < calc->hw.ramsize)
|
|
return 1;
|
|
}
|
|
|
|
/* Read LCD contents */
|
|
|
|
if (calc->hw.flags & TILEM_CALC_HAS_T6A04) {
|
|
calc->lcd.rowstride = 12; /* old save files only
|
|
support the visible
|
|
portion of the screen */
|
|
if (fread(calc->lcdmem, 1, 768, savfile) < 768)
|
|
return 1;
|
|
}
|
|
|
|
/* Read additional HW state */
|
|
|
|
switch (calc->hw.model_id) {
|
|
case '1':
|
|
break;
|
|
|
|
case '2':
|
|
case '3':
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->lcd.mode = c;
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->lcd.x = c;
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->lcd.y = c;
|
|
break;
|
|
|
|
case '5':
|
|
pageA = calc->mempagemap[1];
|
|
if (pageA >= 0x08)
|
|
pageA += 0x38;
|
|
calc->hw.z80_out(calc, 0x05, pageA);
|
|
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->hw.z80_out(calc, 0x06, c);
|
|
break;
|
|
|
|
case '6':
|
|
pageA = calc->mempagemap[1];
|
|
pageB = calc->mempagemap[2];
|
|
if (pageA >= 0x10)
|
|
pageA += 0x30;
|
|
if (pageB >= 0x10)
|
|
pageB += 0x30;
|
|
|
|
calc->hw.z80_out(calc, 0x05, pageA);
|
|
calc->hw.z80_out(calc, 0x06, pageB);
|
|
break;
|
|
|
|
default: /* TI-73/83+ series */
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->lcd.mode = c;
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->lcd.x = c;
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->lcd.y = c;
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->lcd.inc = c;
|
|
|
|
if ((c = fgetc(savfile)) == EOF)
|
|
c = 0;
|
|
if (c) {
|
|
pageA = calc->mempagemap[2];
|
|
pageB = calc->mempagemap[3];
|
|
calc->hw.z80_out(calc, 0x04, 0x77);
|
|
}
|
|
else {
|
|
pageA = calc->mempagemap[1];
|
|
pageB = calc->mempagemap[2];
|
|
calc->hw.z80_out(calc, 0x04, 0x76);
|
|
}
|
|
|
|
if (pageA >= (calc->hw.romsize >> 14))
|
|
pageA = ((pageA & 0x1f) | calc->hw.rampagemask);
|
|
if (pageB >= (calc->hw.romsize >> 14))
|
|
pageB = ((pageB & 0x1f) | calc->hw.rampagemask);
|
|
|
|
calc->hw.z80_out(calc, 0x06, pageA);
|
|
calc->hw.z80_out(calc, 0x07, pageB);
|
|
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->flash.state = c;
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->flash.unlock = c;
|
|
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->hw.z80_out(calc, 0x20, c);
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
set_hw_reg(calc, "port21", c);
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
set_hw_reg(calc, "port22", c);
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
set_hw_reg(calc, "port23", c);
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->hw.z80_out(calc, 0x27, c);
|
|
if ((c = fgetc(savfile)) != EOF)
|
|
calc->hw.z80_out(calc, 0x28, c);
|
|
break;
|
|
}
|
|
|
|
calc->poweronhalt = calc->lcd.active = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int read_sav_line(FILE* savfile, char **buf)
|
|
{
|
|
int c, n, na;
|
|
|
|
tilem_free(*buf);
|
|
|
|
na = 100;
|
|
*buf = tilem_malloc_atomic(na);
|
|
n = 0;
|
|
|
|
while ((c = fgetc(savfile)) != EOF) {
|
|
if (c == '\r' || c == '\n')
|
|
break;
|
|
|
|
n++;
|
|
if (n >= na) {
|
|
na = n * 2;
|
|
*buf = tilem_realloc(*buf, na);
|
|
}
|
|
|
|
if (c == '#')
|
|
c = 0;
|
|
(*buf)[n - 1] = c;
|
|
}
|
|
|
|
if (n == 0 && c == EOF) {
|
|
tilem_free(*buf);
|
|
*buf = NULL;
|
|
return 0;
|
|
}
|
|
else {
|
|
(*buf)[n] = 0;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int parse_sav_definition(char* line, char** value)
|
|
{
|
|
char *p;
|
|
|
|
p = strchr(line, '=');
|
|
if (!p)
|
|
return 0;
|
|
|
|
while (p != line && p[-1] == ' ')
|
|
p--;
|
|
*p = 0;
|
|
p++;
|
|
while (*p == ' ' || *p == '=')
|
|
p++;
|
|
*value = p;
|
|
return 1;
|
|
}
|
|
|
|
static int load_new_sav_file(TilemCalc* calc, FILE* savfile)
|
|
{
|
|
char *buf = NULL;
|
|
char *p, *q;
|
|
dword value, length;
|
|
byte *data;
|
|
int ok = 0;
|
|
byte digit;
|
|
int firstdigit;
|
|
dword period;
|
|
int rt;
|
|
|
|
while (read_sav_line(savfile, &buf)) {
|
|
if (!parse_sav_definition(buf, &p))
|
|
continue;
|
|
|
|
if (*p == '{') {
|
|
p++;
|
|
if (!strcmp(buf, "RAM")) {
|
|
length = calc->hw.ramsize;
|
|
data = calc->ram;
|
|
}
|
|
else if (!strcmp(buf, "LCD")) {
|
|
length = calc->hw.lcdmemsize;
|
|
data = calc->lcdmem;
|
|
}
|
|
else {
|
|
length = 0;
|
|
data = NULL;
|
|
}
|
|
|
|
value = 0;
|
|
firstdigit = 1;
|
|
|
|
while (*p != '}') {
|
|
if (*p == 0 || *p == '#') {
|
|
if (!read_sav_line(savfile, &buf))
|
|
return 1;
|
|
p = buf;
|
|
continue;
|
|
}
|
|
|
|
if (*p >= '0' && *p <= '9') {
|
|
digit = *p - '0';
|
|
p++;
|
|
}
|
|
else if (*p >= 'A' && *p <= 'F') {
|
|
digit = *p + 10 - 'A';
|
|
p++;
|
|
}
|
|
else if (*p >= 'a' && *p <= 'f') {
|
|
digit = *p + 10 - 'a';
|
|
p++;
|
|
}
|
|
else {
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
if (firstdigit) {
|
|
value = digit << 4;
|
|
firstdigit = 0;
|
|
}
|
|
else {
|
|
value |= digit;
|
|
if (length != 0) {
|
|
*data = value;
|
|
data++;
|
|
length--;
|
|
}
|
|
firstdigit = 1;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(buf, "MODEL")) {
|
|
q = p;
|
|
while (*q >= ' ')
|
|
q++;
|
|
*q = 0;
|
|
if (strcmp(p, calc->hw.name)) {
|
|
tilem_free(buf);
|
|
return 1;
|
|
}
|
|
ok = 1;
|
|
continue;
|
|
}
|
|
|
|
value = strtol(p, &q, 16);
|
|
|
|
/* Persistent timers */
|
|
if (!strncmp(buf, "timer:", 6)) {
|
|
while (*q == ' ')
|
|
q++;
|
|
if (*q != ',')
|
|
continue;
|
|
q++;
|
|
while (*q == ' ')
|
|
q++;
|
|
period = strtol(q, &q, 16);
|
|
|
|
while (*q == ' ')
|
|
q++;
|
|
if (*q != ',')
|
|
continue;
|
|
q++;
|
|
while (*q == ' ')
|
|
q++;
|
|
rt = strtol(q, &q, 16);
|
|
|
|
set_ptimer(calc, buf + 6, value, period, rt);
|
|
continue;
|
|
}
|
|
|
|
/* Z80 */
|
|
if (!strcmp(buf, "af")) calc->z80.r.af.d = value;
|
|
else if (!strcmp(buf, "bc")) calc->z80.r.bc.d = value;
|
|
else if (!strcmp(buf, "de")) calc->z80.r.de.d = value;
|
|
else if (!strcmp(buf, "hl")) calc->z80.r.hl.d = value;
|
|
else if (!strcmp(buf, "af'")) calc->z80.r.af2.d = value;
|
|
else if (!strcmp(buf, "bc'")) calc->z80.r.bc2.d = value;
|
|
else if (!strcmp(buf, "de'")) calc->z80.r.de2.d = value;
|
|
else if (!strcmp(buf, "hl'")) calc->z80.r.hl2.d = value;
|
|
else if (!strcmp(buf, "ix")) calc->z80.r.ix.d = value;
|
|
else if (!strcmp(buf, "iy")) calc->z80.r.iy.d = value;
|
|
else if (!strcmp(buf, "pc")) calc->z80.r.pc.d = value;
|
|
else if (!strcmp(buf, "sp")) calc->z80.r.sp.d = value;
|
|
else if (!strcmp(buf, "ir")) {
|
|
calc->z80.r.ir.d = value;
|
|
calc->z80.r.r7 = value & 0x80;
|
|
}
|
|
else if (!strcmp(buf, "wz")) calc->z80.r.wz.d = value;
|
|
else if (!strcmp(buf, "wz'")) calc->z80.r.wz2.d = value;
|
|
else if (!strcmp(buf, "iff1")) calc->z80.r.iff1 = value;
|
|
else if (!strcmp(buf, "iff2")) calc->z80.r.iff2 = value;
|
|
else if (!strcmp(buf, "im")) calc->z80.r.im = value;
|
|
else if (!strcmp(buf, "interrupts"))
|
|
calc->z80.interrupts = value;
|
|
else if (!strcmp(buf, "clockspeed"))
|
|
calc->z80.clockspeed = value;
|
|
else if (!strcmp(buf, "halted")) calc->z80.halted = value;
|
|
|
|
/* LCD */
|
|
else if (!strcmp(buf, "lcd.active"))
|
|
calc->lcd.active = value;
|
|
else if (!strcmp(buf, "lcd.addr"))
|
|
calc->lcd.addr = value;
|
|
else if (!strcmp(buf, "lcd.rowshift"))
|
|
calc->lcd.rowshift = value;
|
|
else if (!strcmp(buf, "lcd.contrast"))
|
|
calc->lcd.contrast = value;
|
|
else if (!strcmp(buf, "lcd.inc"))
|
|
calc->lcd.inc = value;
|
|
else if (!strcmp(buf, "lcd.mode"))
|
|
calc->lcd.mode = value;
|
|
else if (!strcmp(buf, "lcd.x"))
|
|
calc->lcd.x = value;
|
|
else if (!strcmp(buf, "lcd.y"))
|
|
calc->lcd.y = value;
|
|
else if (!strcmp(buf, "lcd.nextbyte"))
|
|
calc->lcd.nextbyte = value;
|
|
else if (!strcmp(buf, "lcd.rowstride"))
|
|
calc->lcd.rowstride = value;
|
|
else if (!strcmp(buf, "lcd.busy"))
|
|
calc->lcd.busy = value;
|
|
|
|
/* Link port */
|
|
else if (!strcmp(buf, "linkport.lines"))
|
|
calc->linkport.lines = value;
|
|
else if (!strcmp(buf, "linkport.mode"))
|
|
calc->linkport.mode = value;
|
|
else if (!strcmp(buf, "linkport.assistflags"))
|
|
calc->linkport.assistflags = value;
|
|
else if (!strcmp(buf, "linkport.assistin"))
|
|
calc->linkport.assistin = value;
|
|
else if (!strcmp(buf, "linkport.assistinbits"))
|
|
calc->linkport.assistinbits = value;
|
|
else if (!strcmp(buf, "linkport.assistout"))
|
|
calc->linkport.assistout = value;
|
|
else if (!strcmp(buf, "linkport.assistoutbits"))
|
|
calc->linkport.assistoutbits = value;
|
|
else if (!strcmp(buf, "linkport.assistlastbyte"))
|
|
calc->linkport.assistlastbyte = value;
|
|
|
|
/* Keypad */
|
|
else if (!strcmp(buf, "keypad.group"))
|
|
calc->keypad.group = value;
|
|
else if (!strcmp(buf, "keypad.onkeyint"))
|
|
calc->keypad.onkeyint = value;
|
|
|
|
/* MD5 assist */
|
|
else if (!strcmp(buf, "md5assist.a"))
|
|
calc->md5assist.regs[0] = value;
|
|
else if (!strcmp(buf, "md5assist.b"))
|
|
calc->md5assist.regs[1] = value;
|
|
else if (!strcmp(buf, "md5assist.c"))
|
|
calc->md5assist.regs[2] = value;
|
|
else if (!strcmp(buf, "md5assist.d"))
|
|
calc->md5assist.regs[3] = value;
|
|
else if (!strcmp(buf, "md5assist.x"))
|
|
calc->md5assist.regs[4] = value;
|
|
else if (!strcmp(buf, "md5assist.t"))
|
|
calc->md5assist.regs[5] = value;
|
|
else if (!strcmp(buf, "md5assist.shift"))
|
|
calc->md5assist.shift = value;
|
|
else if (!strcmp(buf, "md5assist.mode"))
|
|
calc->md5assist.mode = value;
|
|
|
|
/* Programmable timers */
|
|
else if (!strcmp(buf, "usertimer0.frequency"))
|
|
calc->usertimers[0].frequency = value;
|
|
else if (!strcmp(buf, "usertimer0.loopvalue"))
|
|
calc->usertimers[0].loopvalue = value;
|
|
else if (!strcmp(buf, "usertimer0.status"))
|
|
calc->usertimers[0].status = value;
|
|
else if (!strcmp(buf, "usertimer1.frequency"))
|
|
calc->usertimers[1].frequency = value;
|
|
else if (!strcmp(buf, "usertimer1.loopvalue"))
|
|
calc->usertimers[1].loopvalue = value;
|
|
else if (!strcmp(buf, "usertimer1.status"))
|
|
calc->usertimers[1].status = value;
|
|
else if (!strcmp(buf, "usertimer2.frequency"))
|
|
calc->usertimers[2].frequency = value;
|
|
else if (!strcmp(buf, "usertimer2.loopvalue"))
|
|
calc->usertimers[2].loopvalue = value;
|
|
else if (!strcmp(buf, "usertimer2.status"))
|
|
calc->usertimers[2].status = value;
|
|
|
|
/* Main power */
|
|
else if (!strcmp(buf, "poweronhalt"))
|
|
calc->poweronhalt = value;
|
|
|
|
/* Battery */
|
|
else if (!strcmp(buf, "battery"))
|
|
calc->battery = value;
|
|
|
|
/* Memory */
|
|
else if (!strcmp(buf, "mempagemap0"))
|
|
calc->mempagemap[0] = value;
|
|
else if (!strcmp(buf, "mempagemap1"))
|
|
calc->mempagemap[1] = value;
|
|
else if (!strcmp(buf, "mempagemap2"))
|
|
calc->mempagemap[2] = value;
|
|
else if (!strcmp(buf, "mempagemap3"))
|
|
calc->mempagemap[3] = value;
|
|
else if (!strcmp(buf, "flash.unlock"))
|
|
calc->flash.unlock = value;
|
|
else if (!strcmp(buf, "flash.state"))
|
|
calc->flash.state = value;
|
|
else if (!strcmp(buf, "flash.busy"))
|
|
calc->flash.busy = value;
|
|
else if (!strcmp(buf, "flash.progaddr"))
|
|
calc->flash.progaddr = value;
|
|
else if (!strcmp(buf, "flash.progbyte"))
|
|
calc->flash.progbyte = value;
|
|
else if (!strcmp(buf, "flash.toggles"))
|
|
calc->flash.toggles = value;
|
|
else if (!strcmp(buf, "flash.overridegroup"))
|
|
calc->flash.overridegroup = value;
|
|
|
|
else
|
|
set_hw_reg(calc, buf, value);
|
|
}
|
|
|
|
tilem_free(buf);
|
|
|
|
return !ok;
|
|
}
|
|
|
|
int tilem_calc_load_state(TilemCalc* calc, FILE* romfile, FILE* savfile)
|
|
{
|
|
int b;
|
|
int savtype = 0;
|
|
|
|
if (romfile) {
|
|
if (fread(calc->mem, 1, calc->hw.romsize, romfile)
|
|
!= calc->hw.romsize)
|
|
return 1;
|
|
}
|
|
|
|
tilem_calc_reset(calc);
|
|
|
|
if (savfile) {
|
|
/* first byte of old save files is always zero */
|
|
b = fgetc(savfile);
|
|
fseek(savfile, 0L, SEEK_SET);
|
|
|
|
if (b == 0) {
|
|
if (load_old_sav_file(calc, savfile)) {
|
|
tilem_calc_reset(calc);
|
|
return 1;
|
|
}
|
|
else
|
|
savtype = 1;
|
|
}
|
|
else {
|
|
if (load_new_sav_file(calc, savfile)) {
|
|
tilem_calc_reset(calc);
|
|
return 1;
|
|
}
|
|
else
|
|
savtype = 2;
|
|
}
|
|
}
|
|
|
|
if (calc->hw.stateloaded)
|
|
(*calc->hw.stateloaded)(calc, savtype);
|
|
|
|
return 0;
|
|
}
|
|
|
|
char tilem_get_sav_type(FILE* savfile)
|
|
{
|
|
int b;
|
|
char *buf = NULL, *p, *q;
|
|
const TilemHardware **models;
|
|
int nmodels, i;
|
|
char id = 0;
|
|
|
|
tilem_get_supported_hardware(&models, &nmodels);
|
|
|
|
/* first byte of old save files is always zero */
|
|
b = fgetc(savfile);
|
|
fseek(savfile, 0L, SEEK_SET);
|
|
if (b == 0)
|
|
return 0; /* old files give no way to detect model */
|
|
|
|
while (read_sav_line(savfile, &buf)) {
|
|
if (parse_sav_definition(buf, &p)
|
|
&& !strcmp(buf, "MODEL")) {
|
|
q = p;
|
|
while (*q >= ' ')
|
|
q++;
|
|
*q = 0;
|
|
|
|
for (i = 0; i < nmodels; i++)
|
|
if (!strcmp(p, models[i]->name))
|
|
id = models[i]->model_id;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
fseek(savfile, 0L, SEEK_SET);
|
|
tilem_free(buf);
|
|
return id;
|
|
}
|
|
|
|
int tilem_calc_save_state(TilemCalc* calc, FILE* romfile, FILE* savfile)
|
|
{
|
|
dword i;
|
|
dword t;
|
|
int j;
|
|
const char* tname;
|
|
unsigned int rowstride;
|
|
|
|
if (romfile) {
|
|
if (fwrite(calc->mem, 1, calc->hw.romsize, romfile)
|
|
!= calc->hw.romsize)
|
|
return 1;
|
|
}
|
|
|
|
if (savfile) {
|
|
fprintf(savfile, "# Tilem II State File\n# Version: %s\n",
|
|
PACKAGE_VERSION);
|
|
fprintf(savfile, "MODEL = %s\n", calc->hw.name);
|
|
|
|
fprintf(savfile, "\n## CPU ##\n");
|
|
fprintf(savfile, "af = %04X\n", calc->z80.r.af.w.l);
|
|
fprintf(savfile, "bc = %04X\n", calc->z80.r.bc.w.l);
|
|
fprintf(savfile, "de = %04X\n", calc->z80.r.de.w.l);
|
|
fprintf(savfile, "hl = %04X\n", calc->z80.r.hl.w.l);
|
|
fprintf(savfile, "af' = %04X\n", calc->z80.r.af2.w.l);
|
|
fprintf(savfile, "bc' = %04X\n", calc->z80.r.bc2.w.l);
|
|
fprintf(savfile, "de' = %04X\n", calc->z80.r.de2.w.l);
|
|
fprintf(savfile, "hl' = %04X\n", calc->z80.r.hl2.w.l);
|
|
fprintf(savfile, "ix = %04X\n", calc->z80.r.ix.w.l);
|
|
fprintf(savfile, "iy = %04X\n", calc->z80.r.iy.w.l);
|
|
fprintf(savfile, "pc = %04X\n", calc->z80.r.pc.w.l);
|
|
fprintf(savfile, "sp = %04X\n", calc->z80.r.sp.w.l);
|
|
fprintf(savfile, "ir = %04X\n",
|
|
((calc->z80.r.ir.w.l & ~0x80) | calc->z80.r.r7));
|
|
fprintf(savfile, "wz = %04X\n", calc->z80.r.wz.w.l);
|
|
fprintf(savfile, "wz' = %04X\n", calc->z80.r.wz2.w.l);
|
|
fprintf(savfile, "iff1 = %X\n", calc->z80.r.iff1);
|
|
fprintf(savfile, "iff2 = %X\n", calc->z80.r.iff2);
|
|
fprintf(savfile, "im = %X\n", calc->z80.r.im);
|
|
fprintf(savfile, "interrupts = %08X\n", calc->z80.interrupts);
|
|
fprintf(savfile, "clockspeed = %X\n", calc->z80.clockspeed);
|
|
fprintf(savfile, "halted = %X\n", calc->z80.halted);
|
|
|
|
fprintf(savfile, "\n## LCD Driver ##\n");
|
|
fprintf(savfile, "lcd.active = %X\n",
|
|
calc->lcd.active);
|
|
fprintf(savfile, "lcd.contrast = %X\n",
|
|
calc->lcd.contrast);
|
|
fprintf(savfile, "lcd.rowstride = %X\n",
|
|
calc->lcd.rowstride);
|
|
if (calc->hw.flags & TILEM_CALC_HAS_T6A04) {
|
|
fprintf(savfile, "lcd.rowshift = %X\n",
|
|
calc->lcd.rowshift);
|
|
fprintf(savfile, "lcd.inc = %X\n",
|
|
calc->lcd.inc);
|
|
fprintf(savfile, "lcd.mode = %X\n",
|
|
calc->lcd.mode);
|
|
fprintf(savfile, "lcd.x = %02X\n",
|
|
calc->lcd.x);
|
|
fprintf(savfile, "lcd.y = %02X\n",
|
|
calc->lcd.y);
|
|
fprintf(savfile, "lcd.nextbyte = %02X\n",
|
|
calc->lcd.nextbyte);
|
|
fprintf(savfile, "lcd.busy = %X\n",
|
|
calc->lcd.busy);
|
|
}
|
|
fprintf(savfile, "lcd.addr = %X\n", calc->lcd.addr);
|
|
|
|
if (calc->hw.flags & TILEM_CALC_HAS_LINK) {
|
|
fprintf(savfile, "\n## Link Port ##\n");
|
|
fprintf(savfile, "linkport.lines = %X\n",
|
|
calc->linkport.lines);
|
|
fprintf(savfile, "linkport.mode = %08X\n",
|
|
calc->linkport.mode);
|
|
}
|
|
if (calc->hw.flags & TILEM_CALC_HAS_LINK_ASSIST) {
|
|
fprintf(savfile, "linkport.assistflags = %08X\n",
|
|
calc->linkport.assistflags);
|
|
fprintf(savfile, "linkport.assistin = %02X\n",
|
|
calc->linkport.assistin);
|
|
fprintf(savfile, "linkport.assistinbits = %X\n",
|
|
calc->linkport.assistinbits);
|
|
fprintf(savfile, "linkport.assistout = %02X\n",
|
|
calc->linkport.assistout);
|
|
fprintf(savfile, "linkport.assistoutbits = %X\n",
|
|
calc->linkport.assistoutbits);
|
|
fprintf(savfile, "linkport.assistlastbyte = %02X\n",
|
|
calc->linkport.assistlastbyte);
|
|
}
|
|
|
|
fprintf(savfile, "\n## Keypad ##\n");
|
|
fprintf(savfile, "keypad.group = %X\n", calc->keypad.group);
|
|
fprintf(savfile, "keypad.onkeyint = %X\n",
|
|
calc->keypad.onkeyint);
|
|
|
|
fprintf(savfile, "\n## Memory mapping ##\n");
|
|
fprintf(savfile, "mempagemap0 = %X\n", calc->mempagemap[0]);
|
|
fprintf(savfile, "mempagemap1 = %X\n", calc->mempagemap[1]);
|
|
fprintf(savfile, "mempagemap2 = %X\n", calc->mempagemap[2]);
|
|
fprintf(savfile, "mempagemap3 = %X\n", calc->mempagemap[3]);
|
|
|
|
fprintf(savfile, "\n## Power ##\n");
|
|
fprintf(savfile, "poweronhalt = %X\n", calc->poweronhalt);
|
|
fprintf(savfile, "battery = %X\n", calc->battery);
|
|
|
|
if (calc->hw.flags & TILEM_CALC_HAS_FLASH) {
|
|
fprintf(savfile, "\n## Flash ##\n");
|
|
fprintf(savfile, "flash.unlock = %X\n",
|
|
calc->flash.unlock);
|
|
fprintf(savfile, "flash.state = %X\n",
|
|
calc->flash.state);
|
|
fprintf(savfile, "flash.busy = %X\n",
|
|
calc->flash.busy);
|
|
fprintf(savfile, "flash.progaddr = %X\n",
|
|
calc->flash.progaddr);
|
|
fprintf(savfile, "flash.progbyte = %X\n",
|
|
calc->flash.progbyte);
|
|
fprintf(savfile, "flash.toggles = %X\n",
|
|
calc->flash.toggles);
|
|
fprintf(savfile, "flash.overridegroup = %X\n",
|
|
calc->flash.overridegroup);
|
|
}
|
|
|
|
if (calc->hw.flags & TILEM_CALC_HAS_MD5_ASSIST) {
|
|
fprintf(savfile, "\n## MD5 assist ##\n");
|
|
fprintf(savfile, "md5assist.a = %X\n",
|
|
calc->md5assist.regs[0]);
|
|
fprintf(savfile, "md5assist.b = %X\n",
|
|
calc->md5assist.regs[1]);
|
|
fprintf(savfile, "md5assist.c = %X\n",
|
|
calc->md5assist.regs[2]);
|
|
fprintf(savfile, "md5assist.d = %X\n",
|
|
calc->md5assist.regs[3]);
|
|
fprintf(savfile, "md5assist.x = %X\n",
|
|
calc->md5assist.regs[4]);
|
|
fprintf(savfile, "md5assist.t = %X\n",
|
|
calc->md5assist.regs[5]);
|
|
fprintf(savfile, "md5assist.shift = %X\n",
|
|
calc->md5assist.shift);
|
|
fprintf(savfile, "md5assist.mode = %X\n",
|
|
calc->md5assist.mode);
|
|
}
|
|
|
|
for (j = 0; j < calc->hw.nusertimers; j++) {
|
|
fprintf(savfile,
|
|
"\n## Programmable timer %d ##\n", j);
|
|
fprintf(savfile, "usertimer%d.frequency = %X\n",
|
|
j, calc->usertimers[j].frequency);
|
|
fprintf(savfile, "usertimer%d.loopvalue = %X\n",
|
|
j, calc->usertimers[j].loopvalue);
|
|
fprintf(savfile, "usertimer%d.status = %X\n",
|
|
j, calc->usertimers[j].status);
|
|
}
|
|
|
|
fprintf(savfile, "\n## Model-specific ##\n");
|
|
for (j = 0; j < calc->hw.nhwregs; j++) {
|
|
fprintf(savfile, "%s = %X\n", calc->hw.hwregnames[j],
|
|
calc->hwregs[j]);
|
|
}
|
|
|
|
fprintf(savfile, "\n## Timers ##\n");
|
|
for (j = calc->z80.timer_cpu; j;
|
|
j = calc->z80.timers[j].next) {
|
|
tname = get_timer_name(calc, j);
|
|
if (tname) {
|
|
t = tilem_z80_get_timer_clocks(calc, j);
|
|
fprintf(savfile, "timer:%s = %X, %X, 0\n",
|
|
tname, t, calc->z80.timers[j].period);
|
|
}
|
|
}
|
|
for (j = calc->z80.timer_rt; j;
|
|
j = calc->z80.timers[j].next) {
|
|
tname = get_timer_name(calc, j);
|
|
if (tname) {
|
|
t = tilem_z80_get_timer_microseconds(calc, j);
|
|
fprintf(savfile, "timer:%s = %X, %X, 1\n",
|
|
tname, t, calc->z80.timers[j].period);
|
|
}
|
|
}
|
|
|
|
fprintf(savfile, "\n## RAM contents ##\n");
|
|
fprintf(savfile, "RAM = {\n");
|
|
for (i = 0; i < calc->hw.ramsize; i++) {
|
|
if (i % 256 == 0) {
|
|
fprintf(savfile, "# %02X:%04X\n",
|
|
(i >> 14), (i & 0x3fff));
|
|
}
|
|
|
|
fprintf(savfile, "%02X",
|
|
calc->mem[i + calc->hw.romsize]);
|
|
if (i % 32 == 31)
|
|
fprintf(savfile, "\n");
|
|
}
|
|
fprintf(savfile, "}\n## End of RAM contents ##\n");
|
|
|
|
if (calc->hw.lcdmemsize) {
|
|
fprintf(savfile, "\n## LCD contents ##\n");
|
|
fprintf(savfile, "LCD = {\n");
|
|
rowstride = calc->lcd.rowstride;
|
|
if (rowstride == 0)
|
|
rowstride = 32;
|
|
|
|
for (i = 0; i < calc->hw.lcdmemsize; i++) {
|
|
fprintf(savfile, "%02X", calc->lcdmem[i]);
|
|
if (i % rowstride == (rowstride - 1))
|
|
fprintf(savfile, "\n");
|
|
}
|
|
fprintf(savfile, "}\n## End of LCD contents ##\n");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|