Implement 386 instruction table, improve 8086/186/286 instruction table
[multi_emu.git] / emu_65c02.c
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #if ALT_BACKEND
7 #include "vrEmu6502/src/vrEmu6502.h"
8 #else
9 #include "cpu_65c02.h"
10 #endif
11
12 #define REG_TRACE 0
13 #define MEM_TRACE 0
14
15 #define MEM_SIZE 0x10000
16 uint8_t mem[MEM_SIZE];
17
18 int load_ihx(char *name) {
19   FILE *fp = fopen(name, "r");
20   if (fp == NULL) {
21     perror(name);
22     exit(EXIT_FAILURE);
23   }
24
25   int base = 0, entry_point = 0;
26   bool had_eof = false;
27   char line[0x100];
28   while (fgets(line, 0x100, fp)) {
29     for (char *p = line; *p; ++p)
30       if (*p == '\n') {
31         *p = 0;
32         break;
33       }
34
35     if (had_eof) {
36       fprintf(stderr, "garbage after EOF record: %s\n", line);
37       exit(EXIT_FAILURE);
38     }
39
40     if (line[0] != ':') {
41       fprintf(stderr, "require colon: %s\n", line);
42       exit(EXIT_FAILURE);
43     }
44
45     uint8_t buf[0x7f];
46     int len;
47     for (len = 0; len < 0x7f; ++len) {
48       char *p = line + 1 + len * 2;
49       if (*p == 0)
50         break;
51       if (*p == '\n') {
52         *p = 0;
53         break;
54       }
55       uint8_t c = p[2];
56       p[2] = 0;
57
58       char *q;
59       buf[len] = (uint8_t)strtol(p, &q, 16);
60       p[2] = c;
61       if (q != p + 2) {
62         fprintf(stderr, "not hex byte: %s\n", p);
63         exit(EXIT_FAILURE);
64       }
65     }
66
67     if (len == 0) {
68       fprintf(stderr, "empty line: %s\n", line);
69       exit(EXIT_FAILURE);
70     }
71
72     uint8_t checksum = 0;
73     for (int i = 0; i < len; ++i)
74       checksum += buf[i];
75     if (checksum) {
76       checksum -= buf[len - 1];
77       fprintf(
78         stderr,
79         "checksum %02x, should be %02x\n",
80         checksum,
81         buf[len - 1]
82       );
83       exit(EXIT_FAILURE);
84     }
85
86     len -= 5;
87     if (len != buf[0]) {
88       fprintf(stderr, "incorrect length: %s\n", line);
89       exit(EXIT_FAILURE);
90     }
91
92     int addr = (buf[1] << 8) | buf[2], end_addr;
93     switch (buf[3]) {
94     case 0:
95       addr += base;
96       end_addr = addr + len;
97       if (end_addr < addr || end_addr > MEM_SIZE) {
98         fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr);
99         exit(EXIT_FAILURE);
100       }
101       memcpy(mem + addr, buf + 4, len);
102       break;
103     case 1:
104       had_eof = true;
105       break;
106     case 4:
107       if (len < 2) {
108         fprintf(stderr, "invalid extended linear address record: %s\n", line);
109         exit(EXIT_FAILURE);
110       }
111       base = (buf[4] << 24) | (buf[5] << 16);
112       break;
113     case 5:
114       if (len < 4) {
115         fprintf(stderr, "invalid start linear address record: %s\n", line);
116         exit(EXIT_FAILURE);
117       }
118       entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
119       break;
120     default:
121       fprintf(stderr, "unknown record type: 0x%x\n", buf[3]);
122       exit(EXIT_FAILURE);
123     }
124   }
125   if (!had_eof) {
126     fprintf(stderr, "no EOF record\n");
127     exit(EXIT_FAILURE);
128   }
129
130   fclose(fp);
131   return entry_point;
132 }
133
134 int read_byte(void *context, int addr) {
135 #if MEM_TRACE
136   int data = mem[addr];
137   fprintf(stderr, "addr=%04x rd=%02x\n", addr, data);
138   return data;
139 #else
140   return mem[addr];
141 #endif
142 }
143
144 void write_byte(void *context, int addr, int data) {
145 #if MEM_TRACE
146   fprintf(stderr, "addr=%04x wr=%02x\n", addr, data);
147 #endif
148   mem[addr] = data;
149 }
150
151 #if ALT_BACKEND
152 uint8_t mem_read(uint16_t addr, bool isDbg) {
153   return read_byte(NULL, addr);
154 }
155
156 void mem_write(uint16_t addr, uint8_t val) {
157   write_byte(NULL, addr, val);
158 }
159 #endif
160
161 int main(int argc, char **argv) {
162   if (argc < 2) {
163     printf("usage: %s image.ihx\n", argv[0]);
164     exit(EXIT_FAILURE);
165   }
166   int entry_point = load_ihx(argv[1]);
167
168 #if ALT_BACKEND
169   mem[0xfffc] = (uint8_t)(entry_point & 0xff);
170   mem[0xfffd] = (uint8_t)(entry_point >> 8);
171
172   VrEmu6502 *cpu = vrEmu6502New(CPU_W65C02, mem_read, mem_write);
173   if (cpu == NULL) {
174     perror("malloc()");
175     exit(EXIT_FAILURE);
176   }
177
178   while (true) {
179 #if REG_TRACE
180     fprintf(
181       stderr,
182       "pc=%04x a=%02x x=%02x y=%02x s=%02x p=%02x cf=%d zf=%d if=%d df=%d vf=%d nf=%d\n",
183       vrEmu6502GetPC(cpu),
184       vrEmu6502GetAcc(cpu),
185       vrEmu6502GetX(cpu),
186       vrEmu6502GetY(cpu),
187       vrEmu6502GetStackPointer(cpu),
188       vrEmu6502GetStatus(cpu) | 0x30,
189       vrEmu6502GetStatus(cpu) & 1,
190       (vrEmu6502GetStatus(cpu) >> 1) & 1,
191       (vrEmu6502GetStatus(cpu) >> 2) & 1,
192       (vrEmu6502GetStatus(cpu) >> 3) & 1,
193       (vrEmu6502GetStatus(cpu) >> 6) & 1,
194       (vrEmu6502GetStatus(cpu) >> 7) & 1
195     );
196 #endif
197
198     int pc = vrEmu6502GetPC(cpu);
199     int i;
200     vrEmu6502RunInstrs(cpu, 1, &i);
201     if (pc == vrEmu6502GetPC(cpu)) {
202       printf("hung at %04x\n", pc);
203       break;
204     }
205   }
206 #else
207   struct cpu_65c02 cpu;
208   cpu_65c02_init(&cpu, read_byte, NULL, write_byte, NULL);
209   cpu_65c02_reset(&cpu);
210   cpu.regs.word.pc = entry_point;
211   cpu.regs.byte.s = 0xff;
212
213   while (true) {
214 #if REG_TRACE
215     fprintf(
216       stderr,
217       "pc=%04x a=%02x x=%02x y=%02x s=%02x p=%02x cf=%d zf=%d if=%d df=%d vf=%d nf=%d\n",
218       cpu.regs.word.pc,
219       cpu.regs.byte.a,
220       cpu.regs.byte.x,
221       cpu.regs.byte.y,
222       cpu.regs.byte.s,
223       cpu.regs.byte.p,
224       cpu.regs.bit.cf,
225       cpu.regs.bit.zf,
226       cpu.regs.bit._if,
227       cpu.regs.bit.df,
228       cpu.regs.bit.vf,
229       cpu.regs.bit.nf
230     );
231 #endif
232
233     int pc = cpu.regs.word.pc;
234     cpu_65c02_execute(&cpu);
235     if (pc == cpu.regs.word.pc) {
236       printf("hung at %04x\n", pc);
237       break;
238     }
239   }
240 #endif
241
242   return 0;
243 }