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