Implement 386 instruction table, improve 8086/186/286 instruction table
[multi_emu.git] / emu_mips.c
1 #include <endian.h>
2 #include <stdbool.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #if ALT_BACKEND
8 #include "yams/src/cpu.h"
9 #include "yams/src/cpu_defs.h"
10 #include "yams/src/cpuregs.h"
11 #include "yams/src/memory.h"
12 #include "yams/src/simulator.h"
13 #else
14 #include "cpu_mips.h"
15 #endif
16
17 #define REG_TRACE 0
18 #define MEM_TRACE 0
19
20 #define MEM_SIZE 0x800000
21 #if __BYTE_ORDER == __LITTLE_ENDIAN
22 #define MEM_SIZE_M1 (MEM_SIZE - 1)
23 #define MEM_SIZE_M2 (MEM_SIZE - 2)
24 #define MEM_SIZE_M4 (MEM_SIZE - 4)
25 #else
26 #define MEM_SIZE_M1 0
27 #define MEM_SIZE_M2 0
28 #define MEM_SIZE_M4 0
29 #endif
30 union {
31   uint8_t b[MEM_SIZE];
32   uint16_t hw[MEM_SIZE >> 1];
33   uint32_t w[MEM_SIZE >> 2];
34 } mem;
35
36 int load_ihx(char *name) {
37   FILE *fp = fopen(name, "r");
38   if (fp == NULL) {
39     perror(name);
40     exit(EXIT_FAILURE);
41   }
42
43   int base = 0, entry_point = 0;
44   bool had_eof = false;
45   char line[0x100];
46   while (fgets(line, 0x100, fp)) {
47     for (char *p = line; *p; ++p)
48       if (*p == '\n') {
49         *p = 0;
50         break;
51       }
52
53     if (had_eof) {
54       fprintf(stderr, "garbage after EOF record: %s\n", line);
55       exit(EXIT_FAILURE);
56     }
57
58     if (line[0] != ':') {
59       fprintf(stderr, "require colon: %s\n", line);
60       exit(EXIT_FAILURE);
61     }
62
63     uint8_t buf[0x7f];
64     int len;
65     for (len = 0; len < 0x7f; ++len) {
66       char *p = line + 1 + len * 2;
67       if (*p == 0)
68         break;
69       if (*p == '\n') {
70         *p = 0;
71         break;
72       }
73       uint8_t c = p[2];
74       p[2] = 0;
75
76       char *q;
77       buf[len] = (uint8_t)strtol(p, &q, 16);
78       p[2] = c;
79       if (q != p + 2) {
80         fprintf(stderr, "not hex byte: %s\n", p);
81         exit(EXIT_FAILURE);
82       }
83     }
84
85     if (len == 0) {
86       fprintf(stderr, "empty line: %s\n", line);
87       exit(EXIT_FAILURE);
88     }
89
90     uint8_t checksum = 0;
91     for (int i = 0; i < len; ++i)
92       checksum += buf[i];
93     if (checksum) {
94       checksum -= buf[len - 1];
95       fprintf(
96         stderr,
97         "checksum %02x, should be %02x\n",
98         checksum,
99         buf[len - 1]
100       );
101       exit(EXIT_FAILURE);
102     }
103
104     len -= 5;
105     if (len != buf[0]) {
106       fprintf(stderr, "incorrect length: %s\n", line);
107       exit(EXIT_FAILURE);
108     }
109
110     int addr = (buf[1] << 8) | buf[2], end_addr;
111     switch (buf[3]) {
112     case 0:
113       addr += base;
114       end_addr = addr + len;
115       if (end_addr < addr || end_addr > MEM_SIZE) {
116         fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr);
117         exit(EXIT_FAILURE);
118       }
119       for (int i = 0; i < len; ++i)
120         mem.b[(addr + i) ^ MEM_SIZE_M1] = buf[4 + i];
121       break;
122     case 1:
123       had_eof = true;
124       break;
125 #if 0
126     case 3:
127       if (len < 4) {
128         fprintf(stderr, "invalid start address record: %s\n", line);
129         exit(EXIT_FAILURE);
130       }
131       entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
132       break;
133 #endif
134     case 4:
135       if (len < 2) {
136         fprintf(stderr, "invalid extended linear address record: %s\n", line);
137         exit(EXIT_FAILURE);
138       }
139       base = (buf[4] << 24) | (buf[5] << 16);
140       break;
141     case 5:
142       if (len < 4) {
143         fprintf(stderr, "invalid start linear address record: %s\n", line);
144         exit(EXIT_FAILURE);
145       }
146       entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
147       break;
148     default:
149       fprintf(stderr, "unknown record type: 0x%x\n", buf[3]);
150       exit(EXIT_FAILURE);
151     }
152   }
153   if (!had_eof) {
154     fprintf(stderr, "no EOF record\n");
155     exit(EXIT_FAILURE);
156   }
157
158   fclose(fp);
159   return entry_point;
160 }
161
162 int read_byte(void *context, int addr) {
163   if (addr < 0 || addr >= MEM_SIZE) {
164     fprintf(stderr, "invalid read byte: %08x\n", addr);
165     exit(EXIT_FAILURE);
166   }
167   int data = mem.b[addr ^ MEM_SIZE_M1];
168 #if MEM_TRACE
169   fprintf(stderr, "addr=%08x rd=%02x\n", addr, data);
170 #endif
171   return data;
172 }
173
174 int read_halfword(void *context, int addr) {
175   if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
176     fprintf(stderr, "invalid read halfword: %08x\n", addr);
177     exit(EXIT_FAILURE);
178   }
179   int data = mem.hw[(addr ^ MEM_SIZE_M2) >> 1];
180 #if MEM_TRACE
181   fprintf(stderr, "addr=%08x rd=%04x\n", addr, data);
182 #endif
183   return data;
184 }
185
186 int read_word(void *context, int addr) {
187   if (addr < 0 || addr >= MEM_SIZE || (addr & 3)) {
188     fprintf(stderr, "invalid read word: %08x\n", addr);
189     exit(EXIT_FAILURE);
190   }
191   int data = mem.w[(addr ^ MEM_SIZE_M4) >> 2];
192 #if MEM_TRACE
193   fprintf(stderr, "addr=%08x rd=%08x\n", addr, data);
194 #endif
195   return data;
196 }
197
198 void write_byte(void *context, int addr, int data) {
199 #if MEM_TRACE
200   fprintf(stderr, "addr=%08x wr=%02x\n", addr, data);
201 #endif
202   if (addr < 0 || addr >= MEM_SIZE) {
203     fprintf(stderr, "invalid write byte: %08x\n", addr);
204     exit(EXIT_FAILURE);
205   }
206   mem.b[addr ^ MEM_SIZE_M1] = data;
207 }
208
209 void write_halfword(void *context, int addr, int data) {
210 #if MEM_TRACE
211   fprintf(stderr, "addr=%08x wr=%04x\n", addr, data);
212 #endif
213   if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
214     fprintf(stderr, "invalid write halfword: %08x\n", addr);
215     exit(EXIT_FAILURE);
216   }
217   mem.hw[(addr ^ MEM_SIZE_M2) >> 1] = data;
218 }
219
220 void write_word(void *context, int addr, int data) {
221 #if MEM_TRACE
222   fprintf(stderr, "addr=%08x wr=%08x\n", addr, data);
223 #endif
224   if (addr < 0 || addr >= MEM_SIZE || (addr & 3)) {
225     fprintf(stderr, "invalid write word: %08x\n", addr);
226     exit(EXIT_FAILURE);
227   }
228   mem.w[(addr ^ MEM_SIZE_M4) >> 2] = data;
229 }
230
231 void _syscall(uint32_t *v0, uint32_t *a0, uint32_t *a1) {
232   switch (*v0) {
233   case 1: /* print int */
234     printf("%d", *a0);
235     break;
236   case 4: /* print string */
237     for (int p = *a0, c; (c = mem.b[p ^ MEM_SIZE_M1]) != 0; ++p)
238       putchar(c);
239     break;
240   case 5: /* read int */
241     {
242       fflush(stdout);
243
244       char buf[0x100];
245       if (fgets(buf, 0x100, stdin) == NULL) {
246         perror("fgets()");
247         exit(EXIT_FAILURE);
248       }
249       *v0 = atoi(buf);
250     }
251     break;
252   case 8: /* read string */
253     {
254       fflush(stdout);
255
256       char *buf = malloc(*a1);
257       if (buf == NULL) {
258         perror("malloc()");
259         exit(EXIT_FAILURE);
260       }
261       if (fgets(buf, *a1, stdin) == NULL) {
262         perror("fgets()");
263         exit(EXIT_FAILURE);
264       }
265       char *p = buf;
266       int q = *a0;
267       while ((mem.b[q++ ^ MEM_SIZE_M1] = *p++) != 0)
268         ;
269       free(buf);
270     }
271     break;
272   case 10: /* end program */
273     exit(EXIT_SUCCESS);
274   case 11: /* print char */
275     putchar(*a0);
276     break;
277   case 12: /* read char */
278     fflush(stdout);
279     *a0 = getchar();
280     break;
281   default:
282     fprintf(stderr, "unimplemented syscall %d\n", *v0);
283     exit(EXIT_FAILURE);
284   }
285 }
286
287 #if ALT_BACKEND
288 int simulator_bigendian = 1;
289
290 /* Translates a virtual address to a physical address
291  * does not check TLB exceptions so make sure they do not 
292  * occur when calling this ;) (e.g. by doing memory load/store _before_)
293  */
294 uint32_t phys_addr(uint32_t vaddr, cpu_t *cpu) {
295   return vaddr;
296 }
297
298 /* Read a byte, halfword or word from memory. Return exception number. */
299 exception_t mem_read(memory_t *mem, uint32_t addr, void *buf, 
300                      int size, cpu_t *cpu) {
301   //addr = phys_addr(addr, NULL);
302   switch (size) {
303   case 1:
304     *(uint8_t *)buf = read_byte(NULL, addr);
305     break;
306   case 2:
307     *(uint16_t *)buf = read_halfword(NULL, addr);
308     break;
309   case 4:
310     *(uint32_t *)buf = read_word(NULL, addr);
311     break;
312   default:
313     abort();
314   }
315   return NoException;
316 }
317
318 /* Write a byte, halfword or word to memory. Return exception number. */
319 exception_t mem_write(memory_t *mem, uint32_t addr, void *buf, 
320                       int size, cpu_t *cpu) {
321   //addr = phys_addr(addr, NULL);
322   switch (size) {
323   case 1:
324     write_byte(NULL, addr, *(uint8_t *)buf);
325     break;
326   case 2:
327     write_halfword(NULL, addr, *(uint16_t *)buf);
328     break;
329   case 4:
330     write_word(NULL, addr, *(uint32_t *)buf);
331     break;
332   default:
333     abort();
334   }
335   return NoException;
336 }
337 #endif
338
339 int main(int argc, char **argv) {
340   if (argc < 2) {
341     printf("usage: %s image.ihx\n", argv[0]);
342     exit(EXIT_FAILURE);
343   }
344   int entry_point = load_ihx(argv[1]);
345
346   // place exit trampoline above the stack so program can return into it
347   mem.w[((MEM_SIZE - 8) ^ MEM_SIZE_M4) >> 2] = 0x2402000a; // li $v0, 10
348   mem.w[((MEM_SIZE - 4) ^ MEM_SIZE_M4) >> 2] = 0x0000000c; // syscall
349
350 #if ALT_BACKEND
351   cpu_t *cpu = cpu_init(0);
352
353   cpu->registers[PC] = entry_point;
354   cpu->next_pc = entry_point + 4;
355   cpu->registers[R29] = MEM_SIZE - 0xc; // sp
356   cpu->registers[R31] = MEM_SIZE - 8; // ra
357
358   while (true) {
359 #if REG_TRACE
360     fprintf(
361       stderr,
362       "pc=%08x zero=%08x at=%08x v0=%08x v1=%08x a0=%08x a1=%08x a2=%08x a3=%08x t0=%08x t1=%08x t2=%08x t3=%08x t4=%08x t5=%08x t6=%08x t7=%08x s0=%08x s1=%08x s2=%08x s3=%08x s4=%08x s5=%08x s6=%08x s7=%08x t8=%08x t9=%08x k0=%08x k1=%08x gp=%08x sp=%08x fp=%08x ra=%08x hi=%08x lo=%08x\n",
363       cpu->registers[PC],
364       cpu->registers[R0],
365       cpu->registers[R1],
366       cpu->registers[R2],
367       cpu->registers[R3],
368       cpu->registers[R4],
369       cpu->registers[R5],
370       cpu->registers[R6],
371       cpu->registers[R7],
372       cpu->registers[R8],
373       cpu->registers[R9],
374       cpu->registers[R10],
375       cpu->registers[R11],
376       cpu->registers[R12],
377       cpu->registers[R13],
378       cpu->registers[R14],
379       cpu->registers[R15],
380       cpu->registers[R16],
381       cpu->registers[R17],
382       cpu->registers[R18],
383       cpu->registers[R19],
384       cpu->registers[R20],
385       cpu->registers[R21],
386       cpu->registers[R22],
387       cpu->registers[R23],
388       cpu->registers[R24],
389       cpu->registers[R25],
390       cpu->registers[R26],
391       cpu->registers[R27],
392       cpu->registers[R28],
393       cpu->registers[R29],
394       cpu->registers[R30],
395       cpu->registers[R31],
396       cpu->registers[HI],
397       cpu->registers[LO]
398     );
399 #endif
400
401     cpu_update(cpu);
402     if (cpu->exception_pending == Syscall) {
403       _syscall(
404         cpu->registers + R2, // v0
405         cpu->registers + R4, // a0
406         cpu->registers + R5 // a1
407       );
408       cpu->exception_pending = NoException;
409       cpu->registers[PC] = cpu->next_pc;
410       cpu->next_pc += 4;
411     }
412   }
413 #else
414   struct cpu_mips cpu;
415   cpu_mips_init(&cpu, read_byte, NULL, write_byte, NULL);
416   cpu_mips_reset(&cpu);
417
418   while (true) {
419 #if REG_TRACE
420     fprintf(
421       stderr,
422       "pc=%04x a=%02x b=%02x s=%04x x=%04x p=%02x hf=%d if=%d nf=%d zf=%d vf=%d cf=%d\n",
423       cpu.regs.word.pc,
424       cpu.regs.byte.a,
425       cpu.regs.byte.b,
426       cpu.regs.word.s,
427       cpu.regs.word.x,
428       cpu.regs.byte.p,
429       cpu.regs.bit.hf,
430       cpu.regs.bit._if,
431       cpu.regs.bit.nf,
432       cpu.regs.bit.zf,
433       cpu.regs.bit.vf,
434       cpu.regs.bit.cf
435     );
436 #endif
437
438     cpu_mips_execute(&cpu);
439   }
440 #endif
441
442   return 0;
443 }