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