1 /*
2 * TilEm II
3 *
4 * Copyright (c) 2011 Benjamin Moody
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program 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 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <tilem.h>
27
28 #include "ti81prg.h"
29
30 #define tSpace 0x56
31 #define t0 0x10
32 #define t9 0x19
33 #define tDecPt 0x1A
34 #define tA 0x59
35 #define tZ 0x72
36 #define tTheta 0x73
37
38 #define ramStart 0xE000
39 #define cxCurApp 0xE347
40 #define cxOldApp (cxCurApp + 1)
41 #define DimX_old 0xEEF9
42 #define DimX_new 0xF12D
43 #define prgm0Name 0xF1D3
44 #define prgm0Start 0xF2FB
45 #define prgm0End (prgm0Start + 2)
46 #define prgmThetaEnd (prgm0End + 36*2)
47 #define progMem 0xF347
48 #define progMemEnd 0xFCA6
49
50 #define cxPrgmEdit 2
51 #define cxPrgmExec 10
52 #define cxMenu 11
53
ti81_program_new(int size)54 TI81Program * ti81_program_new(int size)
55 {
56 TI81Program *prgm = tilem_new0(TI81Program, 1);
57
58 prgm->info.slot = TI81_SLOT_AUTO;
59 memset(prgm->info.name, tSpace, 8);
60
61 if (size > 0) {
62 prgm->info.size = size;
63 prgm->data = tilem_new_atomic(byte, size);
64 }
65
66 return prgm;
67 }
68
ti81_program_free(TI81Program * prgm)69 void ti81_program_free(TI81Program *prgm)
70 {
71 if (!prgm)
72 return;
73
74 if (prgm->data)
75 tilem_free(prgm->data);
76 tilem_free(prgm);
77 }
78
get_byte_ptr(const TilemCalc * calc,dword addr)79 static byte *get_byte_ptr(const TilemCalc *calc, dword addr)
80 {
81 if (addr < ramStart || addr > 0xffff)
82 return NULL;
83
84 return &calc->ram[addr - ramStart];
85 }
86
read_byte(const TilemCalc * calc,dword addr)87 static int read_byte(const TilemCalc *calc, dword addr)
88 {
89 const byte *p = get_byte_ptr(calc, addr);
90
91 if (!p)
92 return -1;
93 else
94 return *p;
95 }
96
read_word(const TilemCalc * calc,dword addr)97 static dword read_word(const TilemCalc *calc, dword addr)
98 {
99 const byte *p = get_byte_ptr(calc, addr);
100
101 if (!p)
102 return 0;
103 else
104 return (p[0] | p[1] << 8);
105 }
106
write_word(TilemCalc * calc,dword addr,dword value)107 static void write_word(TilemCalc *calc, dword addr, dword value)
108 {
109 byte *p = get_byte_ptr(calc, addr);
110
111 if (p) {
112 p[0] = value & 0xff;
113 p[1] = (value >> 8) & 0xff;
114 }
115 }
116
check_busy(const TilemCalc * calc)117 static int check_busy(const TilemCalc *calc)
118 {
119 int cur, old;
120
121 cur = read_byte(calc, cxCurApp);
122 old = read_byte(calc, cxCurApp);
123
124 if (cur == cxPrgmEdit || cur == cxPrgmExec)
125 return 1;
126 else if (cur == cxMenu && (old == cxPrgmEdit || old == cxPrgmExec))
127 return 1;
128 else
129 return 0;
130 }
131
get_free_mem_end(const TilemCalc * calc)132 static dword get_free_mem_end(const TilemCalc *calc)
133 {
134 const byte *p;
135 int n, i;
136
137 p = get_byte_ptr(calc, DimX_new);
138
139 /* DimX is always a small positive integer, so the first byte
140 must be between 80h and 82h, and the last five bytes must
141 always be zero. On 1.1K, DimX_new is part of textShadow,
142 so none of these byte values make any sense. */
143
144 if (p[0] < 0x80 || p[0] > 0x82 || p[7] != 0)
145 p = get_byte_ptr(calc, DimX_old);
146
147 if (p[0] < 0x80 || p[0] > 0x82)
148 return 0;
149
150 for (i = 3; i < 7; i++)
151 if (p[i])
152 return 0;
153
154 n = ((p[2] & 0xf)
155 + ((p[2] >> 4) * 10)
156 + ((p[1] & 0xf) * 100)
157 + ((p[1] >> 4) * 1000));
158
159 for (i = p[0]; i < 0x83; i++) {
160 if (n % 10)
161 return 0;
162 n /= 10;
163 }
164
165 return (progMemEnd + 1 - 16 * n);
166 }
167
ti81_get_program_info(const TilemCalc * calc,int slot,TI81ProgInfo * info)168 int ti81_get_program_info(const TilemCalc *calc, int slot, TI81ProgInfo *info)
169 {
170 const byte *p;
171 dword progstart, progend;
172
173 if (slot < 0 || slot > TI81_SLOT_MAX)
174 return TI81_ERR_INTERNAL;
175
176 if (check_busy(calc))
177 return TI81_ERR_BUSY;
178
179 progstart = read_word(calc, prgm0Start + 2 * slot);
180 progend = read_word(calc, prgm0Start + 2 * slot + 2);
181
182 if (progstart < ramStart || progend < ramStart || progend < progstart)
183 return TI81_ERR_BUSY;
184
185 info->slot = slot;
186 info->size = progend - progstart;
187 info->addr = progstart;
188
189 p = get_byte_ptr(calc, prgm0Name + 8 * slot);
190 if (!p) return TI81_ERR_INTERNAL;
191 memcpy(info->name, p, 8);
192
193 return 0;
194 }
195
ti81_get_program(const TilemCalc * calc,int slot,TI81Program ** prgm)196 int ti81_get_program(const TilemCalc *calc, int slot, TI81Program **prgm)
197 {
198 TI81ProgInfo info;
199 const byte *p;
200 int s;
201
202 if ((s = ti81_get_program_info(calc, slot, &info))) {
203 *prgm = NULL;
204 return s;
205 }
206
207 *prgm = ti81_program_new(info.size);
208 (*prgm)->info = info;
209 if (info.size > 0 && (p = get_byte_ptr(calc, info.addr)))
210 memcpy((*prgm)->data, p, info.size);
211
212 return 0;
213 }
214
ti81_load_program(TilemCalc * calc,const TI81Program * prgm)215 int ti81_load_program(TilemCalc *calc, const TI81Program *prgm)
216 {
217 TI81ProgInfo info;
218 int slot = prgm->info.slot;
219 int s, i;
220 dword progs_start, progs_end, mem_end, x;
221 byte *p;
222
223 if (slot == TI81_SLOT_AUTO) {
224 for (slot = 0; slot <= TI81_SLOT_MAX; slot++) {
225 if ((s = ti81_get_program_info(calc, slot, &info)))
226 return s;
227 if (info.size == 0 && info.name[0] == tSpace)
228 break;
229 }
230
231 if (slot > TI81_SLOT_MAX)
232 return TI81_ERR_SLOTS_FULL;
233 }
234
235 if ((s = ti81_get_program_info(calc, slot, &info)))
236 return s;
237
238 /* move later programs forward/backward in memory */
239
240 progs_start = info.addr + info.size;
241 progs_end = read_word(calc, prgmThetaEnd);
242 if (progs_end < progs_start)
243 return TI81_ERR_BUSY;
244
245 mem_end = get_free_mem_end(calc);
246 if (progs_end + prgm->info.size - info.size > mem_end)
247 return TI81_ERR_MEMORY;
248
249 if (prgm->info.size != info.size && progs_start != progs_end) {
250 p = get_byte_ptr(calc, progs_start);
251 if (!p) return TI81_ERR_INTERNAL;
252 memmove(p + prgm->info.size - info.size, p,
253 progs_end - progs_start);
254 }
255
256 /* update program pointers */
257
258 for (i = slot; i <= TI81_SLOT_MAX; i++) {
259 x = read_word(calc, prgm0End + 2 * i);
260 write_word(calc, prgm0End + 2 * i,
261 x + prgm->info.size - info.size);
262 }
263
264 /* copy program data */
265
266 if (prgm->info.size != 0) {
267 p = get_byte_ptr(calc, info.addr);
268 if (!p) return TI81_ERR_INTERNAL;
269 memcpy(p, prgm->data, prgm->info.size);
270 }
271
272 /* copy program name */
273
274 p = get_byte_ptr(calc, prgm0Name + 8 * slot);
275 if (!p) return TI81_ERR_INTERNAL;
276 memcpy(p, prgm->info.name, 8);
277
278 return 0;
279 }
280
ti81_read_prg_file(FILE * f,TI81Program ** prgm)281 int ti81_read_prg_file(FILE *f, TI81Program **prgm)
282 {
283 byte buf[20];
284 unsigned int size, i;
285 unsigned int sum = 0;
286 TI81Program *p;
287
288 *prgm = NULL;
289
290 if (fread(buf, 1, 20, f) != 20)
291 return TI81_ERR_INVALID_FILE;
292
293 if (strcmp((char *) buf, "**TI81**") || buf[9] != 0x6e)
294 return TI81_ERR_INVALID_FILE;
295
296 size = buf[10] | buf[11] << 8;
297
298 p = ti81_program_new(size);
299
300 memcpy(p->info.name, buf + 12, 8);
301
302 for (i = 0; i < 8; i++)
303 sum += buf[12 + i];
304
305 if (fread(p->data, 1, size, f) != size) {
306 ti81_program_free(p);
307 return TI81_ERR_INVALID_FILE;
308 }
309
310 for (i = 0; i < size; i++)
311 sum += p->data[i];
312
313 if (fread(buf, 1, 2, f) != 2) {
314 ti81_program_free(p);
315 return TI81_ERR_INVALID_FILE;
316 }
317
318 sum -= (buf[0] | buf[1] << 8);
319 if (sum & 0xffff)
320 fprintf(stderr, "warning: checksum incorrect\n");
321
322 *prgm = p;
323 return 0;
324 }
325
ti81_write_prg_file(FILE * f,const TI81Program * prgm)326 int ti81_write_prg_file(FILE *f, const TI81Program *prgm)
327 {
328 byte buf[20];
329 unsigned int size, i;
330 unsigned int sum = 0;
331
332 memcpy(buf, "**TI81**\0n", 10);
333 size = prgm->info.size;
334 buf[10] = size & 0xff;
335 buf[11] = (size >> 8) & 0xff;
336
337 memcpy(buf + 12, prgm->info.name, 8);
338
339 for (i = 0; i < 8; i++)
340 sum += buf[12 + i];
341
342 if (fwrite(buf, 1, 20, f) != 20)
343 return TI81_ERR_FILE_IO;
344
345 if (fwrite(prgm->data, 1, size, f) != size)
346 return TI81_ERR_FILE_IO;
347
348 for (i = 0; i < size; i++)
349 sum += prgm->data[i];
350
351 buf[0] = sum & 0xff;
352 buf[1] = (sum >> 8) & 0xff;
353 if (fwrite(buf, 1, 2, f) != 2)
354 return TI81_ERR_FILE_IO;
355
356 return 0;
357 }
358
ti81_program_slot_to_string(int slot)359 char * ti81_program_slot_to_string(int slot)
360 {
361 char buf[50];
362 char *s;
363
364 if (slot == TI81_SLOT_AUTO)
365 strcpy(buf, "Automatic");
366 else if (slot < 0 || slot > 36)
367 strcpy(buf, "?");
368 else if (slot < 10)
369 sprintf(buf, "Prgm%c", slot + '0');
370 else if (slot < 36)
371 sprintf(buf, "Prgm%c", slot + 'A' - 10);
372 else
373 strcpy(buf, "Prgm\316\270");
374
375 s = tilem_new_atomic(char, strlen(buf) + 1);
376 strcpy(s, buf);
377 return s;
378 }
379
ti81_program_name_to_string(const byte * prgname)380 char * ti81_program_name_to_string(const byte *prgname)
381 {
382 char buf[50];
383 char *s;
384 int i, j;
385
386 for (i = j = 0; i < 8; i++) {
387 if (prgname[i] == tSpace)
388 buf[j++] = '_';
389 else if (prgname[i] == tDecPt)
390 buf[j++] = '.';
391 else if (prgname[i] == tTheta) {
392 buf[j++] = '\316';
393 buf[j++] = '\270';
394 }
395 else if (prgname[i] >= t0 && prgname[i] <= t9)
396 buf[j++] = '0' + prgname[i] - t0;
397 else if (prgname[i] >= tA && prgname[i] <= tZ)
398 buf[j++] = 'A' + prgname[i] - tA;
399 else
400 buf[j++] = '?';
401 }
402
403 while (j > 0 && buf[j - 1] == '_')
404 j--;
405 buf[j] = 0;
406
407 s = tilem_new_atomic(char, strlen(buf) + 1);
408 strcpy(s, buf);
409 return s;
410 }
411