/*
* TilEm II
*
* Copyright (c) 2011 Benjamin Moody
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include "ti81prg.h"
#define tSpace 0x56
#define t0 0x10
#define t9 0x19
#define tDecPt 0x1A
#define tA 0x59
#define tZ 0x72
#define tTheta 0x73
#define ramStart 0xE000
#define cxCurApp 0xE347
#define cxOldApp (cxCurApp + 1)
#define DimX_old 0xEEF9
#define DimX_new 0xF12D
#define prgm0Name 0xF1D3
#define prgm0Start 0xF2FB
#define prgm0End (prgm0Start + 2)
#define prgmThetaEnd (prgm0End + 36*2)
#define progMem 0xF347
#define progMemEnd 0xFCA6
#define cxPrgmEdit 2
#define cxPrgmExec 10
#define cxMenu 11
TI81Program * ti81_program_new(int size)
{
TI81Program *prgm = tilem_new0(TI81Program, 1);
prgm->info.slot = TI81_SLOT_AUTO;
memset(prgm->info.name, tSpace, 8);
if (size > 0) {
prgm->info.size = size;
prgm->data = tilem_new_atomic(byte, size);
}
return prgm;
}
void ti81_program_free(TI81Program *prgm)
{
if (!prgm)
return;
if (prgm->data)
tilem_free(prgm->data);
tilem_free(prgm);
}
static byte *get_byte_ptr(const TilemCalc *calc, dword addr)
{
if (addr < ramStart || addr > 0xffff)
return NULL;
return &calc->ram[addr - ramStart];
}
static int read_byte(const TilemCalc *calc, dword addr)
{
const byte *p = get_byte_ptr(calc, addr);
if (!p)
return -1;
else
return *p;
}
static dword read_word(const TilemCalc *calc, dword addr)
{
const byte *p = get_byte_ptr(calc, addr);
if (!p)
return 0;
else
return (p[0] | p[1] << 8);
}
static void write_word(TilemCalc *calc, dword addr, dword value)
{
byte *p = get_byte_ptr(calc, addr);
if (p) {
p[0] = value & 0xff;
p[1] = (value >> 8) & 0xff;
}
}
static int check_busy(const TilemCalc *calc)
{
int cur, old;
cur = read_byte(calc, cxCurApp);
old = read_byte(calc, cxCurApp);
if (cur == cxPrgmEdit || cur == cxPrgmExec)
return 1;
else if (cur == cxMenu && (old == cxPrgmEdit || old == cxPrgmExec))
return 1;
else
return 0;
}
static dword get_free_mem_end(const TilemCalc *calc)
{
const byte *p;
int n, i;
p = get_byte_ptr(calc, DimX_new);
/* DimX is always a small positive integer, so the first byte
must be between 80h and 82h, and the last five bytes must
always be zero. On 1.1K, DimX_new is part of textShadow,
so none of these byte values make any sense. */
if (p[0] < 0x80 || p[0] > 0x82 || p[7] != 0)
p = get_byte_ptr(calc, DimX_old);
if (p[0] < 0x80 || p[0] > 0x82)
return 0;
for (i = 3; i < 7; i++)
if (p[i])
return 0;
n = ((p[2] & 0xf)
+ ((p[2] >> 4) * 10)
+ ((p[1] & 0xf) * 100)
+ ((p[1] >> 4) * 1000));
for (i = p[0]; i < 0x83; i++) {
if (n % 10)
return 0;
n /= 10;
}
return (progMemEnd + 1 - 16 * n);
}
int ti81_get_program_info(const TilemCalc *calc, int slot, TI81ProgInfo *info)
{
const byte *p;
dword progstart, progend;
if (slot < 0 || slot > TI81_SLOT_MAX)
return TI81_ERR_INTERNAL;
if (check_busy(calc))
return TI81_ERR_BUSY;
progstart = read_word(calc, prgm0Start + 2 * slot);
progend = read_word(calc, prgm0Start + 2 * slot + 2);
if (progstart < ramStart || progend < ramStart || progend < progstart)
return TI81_ERR_BUSY;
info->slot = slot;
info->size = progend - progstart;
info->addr = progstart;
p = get_byte_ptr(calc, prgm0Name + 8 * slot);
if (!p) return TI81_ERR_INTERNAL;
memcpy(info->name, p, 8);
return 0;
}
int ti81_get_program(const TilemCalc *calc, int slot, TI81Program **prgm)
{
TI81ProgInfo info;
const byte *p;
int s;
if ((s = ti81_get_program_info(calc, slot, &info))) {
*prgm = NULL;
return s;
}
*prgm = ti81_program_new(info.size);
(*prgm)->info = info;
if (info.size > 0 && (p = get_byte_ptr(calc, info.addr)))
memcpy((*prgm)->data, p, info.size);
return 0;
}
int ti81_load_program(TilemCalc *calc, const TI81Program *prgm)
{
TI81ProgInfo info;
int slot = prgm->info.slot;
int s, i;
dword progs_start, progs_end, mem_end, x;
byte *p;
if (slot == TI81_SLOT_AUTO) {
for (slot = 0; slot <= TI81_SLOT_MAX; slot++) {
if ((s = ti81_get_program_info(calc, slot, &info)))
return s;
if (info.size == 0 && info.name[0] == tSpace)
break;
}
if (slot > TI81_SLOT_MAX)
return TI81_ERR_SLOTS_FULL;
}
if ((s = ti81_get_program_info(calc, slot, &info)))
return s;
/* move later programs forward/backward in memory */
progs_start = info.addr + info.size;
progs_end = read_word(calc, prgmThetaEnd);
if (progs_end < progs_start)
return TI81_ERR_BUSY;
mem_end = get_free_mem_end(calc);
if (progs_end + prgm->info.size - info.size > mem_end)
return TI81_ERR_MEMORY;
if (prgm->info.size != info.size && progs_start != progs_end) {
p = get_byte_ptr(calc, progs_start);
if (!p) return TI81_ERR_INTERNAL;
memmove(p + prgm->info.size - info.size, p,
progs_end - progs_start);
}
/* update program pointers */
for (i = slot; i <= TI81_SLOT_MAX; i++) {
x = read_word(calc, prgm0End + 2 * i);
write_word(calc, prgm0End + 2 * i,
x + prgm->info.size - info.size);
}
/* copy program data */
if (prgm->info.size != 0) {
p = get_byte_ptr(calc, info.addr);
if (!p) return TI81_ERR_INTERNAL;
memcpy(p, prgm->data, prgm->info.size);
}
/* copy program name */
p = get_byte_ptr(calc, prgm0Name + 8 * slot);
if (!p) return TI81_ERR_INTERNAL;
memcpy(p, prgm->info.name, 8);
return 0;
}
int ti81_read_prg_file(FILE *f, TI81Program **prgm)
{
byte buf[20];
unsigned int size, i;
unsigned int sum = 0;
TI81Program *p;
*prgm = NULL;
if (fread(buf, 1, 20, f) != 20)
return TI81_ERR_INVALID_FILE;
if (strcmp((char *) buf, "**TI81**") || buf[9] != 0x6e)
return TI81_ERR_INVALID_FILE;
size = buf[10] | buf[11] << 8;
p = ti81_program_new(size);
memcpy(p->info.name, buf + 12, 8);
for (i = 0; i < 8; i++)
sum += buf[12 + i];
if (fread(p->data, 1, size, f) != size) {
ti81_program_free(p);
return TI81_ERR_INVALID_FILE;
}
for (i = 0; i < size; i++)
sum += p->data[i];
if (fread(buf, 1, 2, f) != 2) {
ti81_program_free(p);
return TI81_ERR_INVALID_FILE;
}
sum -= (buf[0] | buf[1] << 8);
if (sum & 0xffff)
fprintf(stderr, "warning: checksum incorrect\n");
*prgm = p;
return 0;
}
int ti81_write_prg_file(FILE *f, const TI81Program *prgm)
{
byte buf[20];
unsigned int size, i;
unsigned int sum = 0;
memcpy(buf, "**TI81**\0n", 10);
size = prgm->info.size;
buf[10] = size & 0xff;
buf[11] = (size >> 8) & 0xff;
memcpy(buf + 12, prgm->info.name, 8);
for (i = 0; i < 8; i++)
sum += buf[12 + i];
if (fwrite(buf, 1, 20, f) != 20)
return TI81_ERR_FILE_IO;
if (fwrite(prgm->data, 1, size, f) != size)
return TI81_ERR_FILE_IO;
for (i = 0; i < size; i++)
sum += prgm->data[i];
buf[0] = sum & 0xff;
buf[1] = (sum >> 8) & 0xff;
if (fwrite(buf, 1, 2, f) != 2)
return TI81_ERR_FILE_IO;
return 0;
}
char * ti81_program_slot_to_string(int slot)
{
char buf[50];
char *s;
if (slot == TI81_SLOT_AUTO)
strcpy(buf, "Automatic");
else if (slot < 0 || slot > 36)
strcpy(buf, "?");
else if (slot < 10)
sprintf(buf, "Prgm%c", slot + '0');
else if (slot < 36)
sprintf(buf, "Prgm%c", slot + 'A' - 10);
else
strcpy(buf, "Prgm\316\270");
s = tilem_new_atomic(char, strlen(buf) + 1);
strcpy(s, buf);
return s;
}
char * ti81_program_name_to_string(const byte *prgname)
{
char buf[50];
char *s;
int i, j;
for (i = j = 0; i < 8; i++) {
if (prgname[i] == tSpace)
buf[j++] = '_';
else if (prgname[i] == tDecPt)
buf[j++] = '.';
else if (prgname[i] == tTheta) {
buf[j++] = '\316';
buf[j++] = '\270';
}
else if (prgname[i] >= t0 && prgname[i] <= t9)
buf[j++] = '0' + prgname[i] - t0;
else if (prgname[i] >= tA && prgname[i] <= tZ)
buf[j++] = 'A' + prgname[i] - tA;
else
buf[j++] = '?';
}
while (j > 0 && buf[j - 1] == '_')
j--;
buf[j] = 0;
s = tilem_new_atomic(char, strlen(buf) + 1);
strcpy(s, buf);
return s;
}