1 #include "link.h"
2 #include "asic.h"
3 #include "cpu.h"
4 #include "mem.h"
5 #include "control.h"
6 #include "interrupt.h"
7 #include "emu.h"
8 #include "os/os.h"
9
10 #include <stdio.h>
11 #include <string.h>
12
13 #define ADDR_SAFE_RAM 0xD052C6
14 #define ADDR_ERRNO 0xD008DF
15 #define ADDR_PRGM_SIZE 0xD0118C
16
17 #define FILE_DATA 0x35
18 #define FILE_DATA_START 0x37
19
20 static const uint8_t jforcegraph[9] = {
21 0xF3, /* di */
22 0xFD, 0xCB, 0x03, 0x86, /* res graphdraw,(iy+graphflags) */
23 0xC3, 0x7C, 0x14, 0x02 /* jp _jforcegraphnokey */
24 };
25
26 static const uint8_t jforcehome[7] = {
27 0xF3, /* di */
28 0x3E, 0x09, /* ld a,kclear */
29 0xC3, 0x64, 0x01, 0x02 /* jp _jforcecmd */
30 };
31
32 static const uint8_t archivevar[15] = {
33 0xF3, /* di */
34 0xCD, 0xC8, 0x02, 0x02, /* call _op4toop1 */
35 0xCD, 0x0C, 0x05, 0x02, /* call _chkfindsym */
36 0xCD, 0x4C, 0x14, 0x02, /* call _archivevar */
37 0x18, 0xFE /* _sink: jr _sink */
38 };
39
40 static const uint8_t header_data[10] = {
41 0x2A, 0x2A, 0x54, 0x49, 0x38, 0x33, 0x46, 0x2A, 0x1A, 0x0A
42 };
43
44 static const uint8_t pgrm_loader[34] = {
45 0xF3, /* di */
46 0xE5, /* push hl */
47 0xCD, 0x0C, 0x05, 0x02, /* call _chkfindsym */
48 0xD4, 0x34, 0x14, 0x02, /* call nc,_delvararc */
49 0xE1, /* pop hl */
50 0x3A, 0xF8, 0x05, 0xD0, /* ld a,(OP1) */
51 0xCD, 0x38, 0x13, 0x02, /* call _createvar */
52 0xED, 0x53,
53 (ADDR_SAFE_RAM>>0)&255,
54 (ADDR_SAFE_RAM>>8)&255,
55 (ADDR_SAFE_RAM>>16)&255, /* ld (SAFE_RAM),de */
56 0x18, 0xFE /* _sink: jr _sink */
57 };
58
59 /*
60 bool emu_list_variables(void) {
61 calc_var_t var;
62 vat_search_init(&var);
63 puts("VAT:");
64 while (vat_search_next(&var)) {
65 printf("type: %s, name: %s, size: %hu\n",
66 calc_var_type_names[var.type],
67 calc_var_name_to_utf8(var.name),
68 var.size);
69 }
70 return true;
71 }
72 */
73
run_asm(const uint8_t * data,const size_t data_size,const uint32_t cycles)74 static void run_asm(const uint8_t *data, const size_t data_size, const uint32_t cycles) {
75 cpu.halted = cpu.IEF_wait = cpu.IEF1 = cpu.IEF2 = cpu.NMI = 0;
76 memcpy(phys_mem_ptr(ADDR_SAFE_RAM, 8400), data, data_size);
77 cpu.cycles = 0;
78 cpu.next = sched.event.cycle = cycles;
79 cpu_flush(ADDR_SAFE_RAM, 1);
80 cpu_execute();
81 }
82
83 /*
84 * Really hackish way to send a variable -- Like, on a scale of 1 to hackish, it's like really hackish
85 * Proper USB emulation should really be a thing
86 * See GitHub issue #25
87 */
emu_send_variable(const char * file,int location)88 int EMSCRIPTEN_KEEPALIVE emu_send_variable(const char *file, int location) {
89 const size_t h_size = sizeof header_data;
90 const uint8_t tVarLst = 0x5D, tAns = 0x72, cxError = 0x52;
91 unsigned int i;
92 int ret = LINK_GOOD;
93
94 FILE *fd;
95 uint8_t tmp_buf[0x80];
96
97 uint32_t save_cycles;
98
99 uint8_t var_ver,
100 var_arc;
101
102 uint8_t *cxCurApp = phys_mem_ptr(0xD007E0, 1),
103 *op1 = phys_mem_ptr(0xD005F8, 9),
104 *var_ptr;
105
106 uint16_t var_size,
107 var_size2,
108 data_size,
109 checksum,
110 cchecksum,
111 header_size;
112
113 size_t temp_size;
114 long lSize;
115
116 /* Return if we are at an error menu */
117 if (*cxCurApp == cxError || !(fd = fopen_utf8(file, "rb"))) {
118 gui_console_printf("[CEmu] Transfer Error: OS in error screen.\n");
119 return LINK_ERR;
120 }
121
122 save_cycles = cpu.cycles;
123
124 if (fread(tmp_buf, 1, h_size, fd) != h_size) goto r_err;
125 if (memcmp(tmp_buf, header_data, h_size)) goto r_err;
126
127 if (fseek(fd, FILE_DATA, SEEK_SET)) goto r_err;
128 if (fread(&data_size, 2, 1, fd) != 1) goto r_err;
129
130
131 if (fseek(fd, 0L, SEEK_END)) goto r_err;
132 if ((lSize = ftell(fd)) <= 0) goto r_err;
133
134 temp_size = (size_t)data_size + FILE_DATA + 4;
135
136 if ((size_t)lSize != temp_size) {
137 gui_console_printf("[CEmu] Transfer Warning: File data section size incorrect.\n");
138 ret = LINK_WARN;
139 }
140
141 if (fseek(fd, FILE_DATA_START, SEEK_SET)) goto r_err;
142
143 /* make sure the checksum is correct */
144 checksum = 0;
145 for (i = FILE_DATA_START; i<(unsigned int)lSize-2; i++) {
146 checksum = (checksum + fgetc(fd)) & 0xffff;
147 }
148
149 if (fread(&cchecksum, 2, 1, fd) != 1) goto r_err;
150
151 if (cchecksum != checksum) {
152 gui_console_printf("[CEmu] Transfer Warning: File checksum invalid.\n");
153 ret = LINK_WARN;
154 }
155
156 if (fseek(fd, FILE_DATA_START, SEEK_SET)) goto r_err;
157
158 if (control.off) {
159 intrpt_set(INT_ON, true);
160 control.readBatteryStatus = (uint8_t)~1u;
161 intrpt_pulse(INT_WAKE);
162 cpu.cycles = cpu.IEF_wait = 0;
163 cpu.next = 100000000;
164 cpu_execute();
165 intrpt_set(INT_ON, false);
166 goto r_err;
167 }
168
169 /* parse each variable individually until the entire file is complete. */
170
171 run_asm(jforcegraph, sizeof jforcegraph, 2500000);
172
173 while (ftell(fd) < lSize-2) {
174
175 if (fread(&header_size, 2, 1, fd) != 1) goto r_err;
176 if (fread(&var_size, 2, 1, fd) != 1) goto r_err;
177 if (fread(op1, 1, 9, fd) != 9) goto r_err;
178 if (header_size == 11) {
179 var_ver = var_arc = 0;
180 } else if (header_size == 13) {
181 if (fread(&var_ver, 1, 1, fd) != 1) goto r_err;
182 if (fread(&var_arc, 1, 1, fd) != 1) goto r_err;
183 } else goto r_err;
184 if (fread(&var_size2, 2, 1, fd) != 1) goto r_err;
185 if (var_size != var_size2) goto r_err;
186
187 /* Hack for TI Connect CE bug - see github issue #80 */
188 if ((*op1 == CALC_VAR_TYPE_REAL_LIST || *op1 == CALC_VAR_TYPE_CPLX_LIST) &&
189 !(op1[1] == tVarLst || op1[1] == tAns)) {
190 memmove(op1 + 2, op1 + 1, 7);
191 op1[1] = tVarLst;
192 }
193
194 mem_poke_byte(ADDR_ERRNO, 0);
195 cpu.registers.HL = var_size - 2;
196
197 /* copy the program into the emulator */
198
199 run_asm(pgrm_loader, sizeof pgrm_loader, 23000000);
200
201 if (mem_peek_byte(ADDR_ERRNO)) {
202 gui_console_printf("[CEmu] Transfer Error: OS Error encountered\n");
203 ret = LINK_ERR;
204 goto r_err;
205 }
206
207 if (mem_peek_word(ADDR_PRGM_SIZE, true)) {
208 gui_console_printf("[CEmu] Transfer Warning: Running assembly program; RAM leak possible\n");
209 ret = LINK_WARN;
210 }
211
212 var_ptr = phys_mem_ptr(mem_peek_long(ADDR_SAFE_RAM), var_size);
213
214 if (fread(var_ptr, 1, var_size, fd) != var_size) goto r_err;
215
216 switch (location) {
217 case LINK_FILE:
218 if (var_arc != 0x80) break;
219 /* fallthrough */
220 case LINK_ARCH:
221 run_asm(archivevar, sizeof archivevar, 23000000);
222 break;
223 case LINK_RAM:
224 break;
225 }
226 }
227
228 run_asm(jforcehome, sizeof jforcehome, 23000000);
229 r_err:
230 cpu.cycles = save_cycles;
231 sched.event.cycle = 0;
232 cpu_restore_next();
233 fclose(fd);
234 return ret;
235 }
236
237 static const char header[] = "**TI83F*\x1A\x0A\0Exported via CEmu ";
emu_receive_variable(const char * file,const calc_var_t * vars,int count)238 int emu_receive_variable(const char *file, const calc_var_t *vars, int count) {
239 FILE *fd;
240 calc_var_t var;
241 uint16_t header_size = 13, size = 0, checksum = 0;
242 int byte;
243
244 fd = fopen_utf8(file, "w+b");
245 if (!fd) {
246 goto w_err;
247 }
248 setbuf(fd, NULL);
249 if (fwrite(header, sizeof header - 1, 1, fd) != 1) goto w_err;
250 if (fseek(fd, FILE_DATA_START, SEEK_SET)) goto w_err;
251 while (count--) {
252 if (!vat_search_find(vars++, &var)) goto w_err;
253 if (fwrite(&header_size, 2, 1, fd) != 1) goto w_err;
254 if (fwrite(&var.size, 2, 1, fd) != 1) goto w_err;
255 if (fwrite(&var.type, 1, 1, fd) != 1) goto w_err;
256 if (fwrite(&var.name, 8, 1, fd) != 1) goto w_err;
257 if (fwrite(&var.version, 1, 1, fd) != 1) goto w_err;
258 if (fputc(var.archived << 7, fd) == EOF) goto w_err;
259 if (fwrite(&var.size, 2, 1, fd) != 1) goto w_err;
260 if (fwrite(var.data, var.size, 1, fd) != 1) goto w_err;
261 size += 17 + var.size;
262 }
263 if (fseek(fd, FILE_DATA, SEEK_SET)) goto w_err;
264 if (fwrite(&size, 2, 1, fd) != 1) goto w_err;
265 if (fflush(fd)) goto w_err;
266 while (size--) {
267 if ((byte = fgetc(fd)) == EOF) goto w_err;
268 checksum += byte;
269 }
270 if (fwrite(&checksum, 2, 1, fd) != 1) goto w_err;
271 (void)fclose(fd);
272
273 return LINK_GOOD;
274
275 w_err:
276 (void)fclose(fd);
277 if(remove(file)) {
278 gui_console_printf("[CEmu] Transfer Error: Please contact the developers\n");
279 }
280 return LINK_ERR;
281 }
282