Implement 386 instruction table, improve 8086/186/286 instruction table
[multi_emu.git] / emu_z80.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 "z80/z80.h"
8 #else
9 #include "cpu_z80.h"
10 #endif
11
12 #define REG_TRACE 0
13 #define MEM_TRACE 0
14 #define IO_TRACE 0
15
16 #define MEM_SIZE 0x10000
17 uint8_t mem[MEM_SIZE];
18
19 // read and write are separated for I/O
20 #define IO_SIZE 0x100
21 uint8_t io_read[IO_SIZE];
22 uint8_t io_write[IO_SIZE];
23
24 int load_ihx(char *name) {
25   FILE *fp = fopen(name, "r");
26   if (fp == NULL) {
27     perror(name);
28     exit(EXIT_FAILURE);
29   }
30
31   int base = 0, entry_point = 0;
32   bool had_eof = false;
33   char line[0x100];
34   while (fgets(line, 0x100, fp)) {
35     for (char *p = line; *p; ++p)
36       if (*p == '\n') {
37         *p = 0;
38         break;
39       }
40
41     if (had_eof) {
42       fprintf(stderr, "garbage after EOF record: %s\n", line);
43       exit(EXIT_FAILURE);
44     }
45
46     if (line[0] != ':') {
47       fprintf(stderr, "require colon: %s\n", line);
48       exit(EXIT_FAILURE);
49     }
50
51     uint8_t buf[0x7f];
52     int len;
53     for (len = 0; len < 0x7f; ++len) {
54       char *p = line + 1 + len * 2;
55       if (*p == 0)
56         break;
57       if (*p == '\n') {
58         *p = 0;
59         break;
60       }
61       uint8_t c = p[2];
62       p[2] = 0;
63
64       char *q;
65       buf[len] = (uint8_t)strtol(p, &q, 16);
66       p[2] = c;
67       if (q != p + 2) {
68         fprintf(stderr, "not hex byte: %s\n", p);
69         exit(EXIT_FAILURE);
70       }
71     }
72
73     if (len == 0) {
74       fprintf(stderr, "empty line: %s\n", line);
75       exit(EXIT_FAILURE);
76     }
77
78     uint8_t checksum = 0;
79     for (int i = 0; i < len; ++i)
80       checksum += buf[i];
81     if (checksum) {
82       checksum -= buf[len - 1];
83       fprintf(
84         stderr,
85         "checksum %02x, should be %02x\n",
86         checksum,
87         buf[len - 1]
88       );
89       exit(EXIT_FAILURE);
90     }
91
92     len -= 5;
93     if (len != buf[0]) {
94       fprintf(stderr, "incorrect length: %s\n", line);
95       exit(EXIT_FAILURE);
96     }
97
98     int addr = (buf[1] << 8) | buf[2], end_addr;
99     switch (buf[3]) {
100     case 0:
101       addr += base;
102       end_addr = addr + len;
103       if (end_addr < addr || end_addr > MEM_SIZE) {
104         fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr);
105         exit(EXIT_FAILURE);
106       }
107       memcpy(mem + addr, buf + 4, len);
108       break;
109     case 1:
110       had_eof = true;
111       break;
112     case 4:
113       if (len < 2) {
114         fprintf(stderr, "invalid extended linear address record: %s\n", line);
115         exit(EXIT_FAILURE);
116       }
117       base = (buf[4] << 24) | (buf[5] << 16);
118       break;
119     case 5:
120       if (len < 4) {
121         fprintf(stderr, "invalid start linear address record: %s\n", line);
122         exit(EXIT_FAILURE);
123       }
124       entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
125       break;
126     default:
127       fprintf(stderr, "unknown record type: 0x%x\n", buf[3]);
128       exit(EXIT_FAILURE);
129     }
130   }
131   if (!had_eof) {
132     fprintf(stderr, "no EOF record\n");
133     exit(EXIT_FAILURE);
134   }
135
136   fclose(fp);
137   return entry_point;
138 }
139
140 int bdos(int c, int de) {
141   switch (c) {
142   case 2:
143     de &= 0xff;
144     if (de != '\r')
145       putchar(de);
146     break;
147   case 9:
148     while (mem[de] != '$') {
149       if (mem[de] != '\r')
150         putchar(mem[de]);
151       de = (de + 1) & 0xffff;
152     }
153     break;
154   default:
155     fprintf(stderr, "unimplemented BDOS call 0x%02x\n", c);
156     exit(EXIT_FAILURE);
157   }
158   return de;
159 }
160
161 int read_byte(void *context, int addr) {
162 #if MEM_TRACE
163   int data = mem[addr];
164   fprintf(stderr, "addr=%04x rd=%02x\n", addr, data);
165   return data;
166 #else
167   return mem[addr];
168 #endif
169 }
170
171 void write_byte(void *context, int addr, int data) {
172 #if MEM_TRACE
173   fprintf(stderr, "addr=%04x wr=%02x\n", addr, data);
174 #endif
175   mem[addr] = data;
176 }
177
178 int in_byte(void *context, int addr) {
179   addr &= 0xff;
180 #if IO_TRACE
181   int data = io_read[addr];
182   printf("io=%02x rd=%02x\n", addr, data);
183   return data;
184 #else
185   return io_read[addr];
186 #endif
187 }
188
189 void out_byte(void *context, int addr, int data) {
190   addr &= 0xff;
191 #if IO_TRACE
192   printf("io=%02x wr=%02x\n", addr, data);
193 #endif
194   io_write[addr] = data;
195 }
196
197 #if ALT_BACKEND
198 uint8_t rb(void *userdata, uint16_t addr) {
199   return read_byte(NULL, addr);
200 }
201
202 void wb(void *userdata, uint16_t addr, uint8_t val) {
203   write_byte(NULL, addr, val);
204 }
205
206 uint8_t in(z80 *const z, uint8_t port) {
207   return in_byte(NULL, port);
208 }
209
210 void out(z80 *const z, uint8_t port, uint8_t val) {
211   out_byte(NULL, port, val);
212 }
213 #endif
214
215 int main(int argc, char **argv) {
216   if (argc < 2) {
217     printf("usage: %s image.ihx\n", argv[0]);
218     exit(EXIT_FAILURE);
219   }
220   int entry_point = load_ihx(argv[1]);
221
222   mem[5] = 0xc9; // ret, for bdos emulation
223   mem[6] = 0xff; // memory top, lo byte
224   mem[7] = 0xff; // memory top, hi byte
225
226 #if ALT_BACKEND
227   z80 cpu;
228   z80_init(&cpu);
229   cpu.read_byte = rb;
230   cpu.write_byte = wb;
231   cpu.port_in = in;
232   cpu.port_out = out;
233   cpu.pc = entry_point;
234
235   while (true) {
236 #if REG_TRACE
237     fprintf(
238       stderr,
239       "pc=%04x af=%04x bc=%04x de=%04x hl=%04x sp=%04x ix=%04x iy=%04x cf=%d nf=%d pvf=%d hf=%d zf=%d sf=%d\n",
240       cpu.pc,
241       cpu.cf |
242       (cpu.nf << 1) |
243       (cpu.pf << 2) |
244       (cpu.xf << 3) |
245       (cpu.hf << 4) |
246       (cpu.yf << 5) |
247       (cpu.zf << 6) |
248       (cpu.sf << 7) |
249       (cpu.a << 8),
250       cpu.c | (cpu.b << 8),
251       cpu.e | (cpu.d << 8),
252       cpu.l | (cpu.h << 8),
253       cpu.sp,
254       cpu.ix,
255       cpu.iy,
256       cpu.cf,
257       cpu.nf,
258       cpu.pf,
259       cpu.hf,
260       cpu.zf,
261       cpu.sf
262     );
263 #endif
264
265     if (cpu.pc == 0) {
266       putchar('\n');
267       break;
268     }
269     if (cpu.pc == 5) {
270       int de = cpu.e | (cpu.d << 8);
271       de = bdos(cpu.c, de);
272       cpu.e = de & 0xff;
273       cpu.d = de >> 8;
274     }
275     z80_step(&cpu, 1);
276   }
277 #else
278   struct cpu_z80 cpu;
279   cpu_z80_init(
280     &cpu,
281     read_byte,
282     NULL,
283     write_byte,
284     NULL,
285     in_byte,
286     NULL,
287     out_byte,
288     NULL
289   );
290   cpu_z80_reset(&cpu);
291   cpu.regs.word.pc = entry_point;
292
293   while (true) {
294 #if REG_TRACE
295     fprintf(
296       stderr,
297       "pc=%04x af=%04x bc=%04x de=%04x hl=%04x sp=%04x ix=%04x iy=%04x cf=%d nf=%d pvf=%d hf=%d zf=%d sf=%d\n",
298       cpu.regs.word.pc,
299       cpu.regs.word.af,
300       cpu.regs.word.bc,
301       cpu.regs.word.de,
302       cpu.regs.word.hl,
303       cpu.regs.word.sp,
304       cpu.regs.word.ix,
305       cpu.regs.word.iy,
306       cpu.regs.bit.cf,
307       cpu.regs.bit.nf,
308       cpu.regs.bit.pvf,
309       cpu.regs.bit.hf,
310       cpu.regs.bit.zf,
311       cpu.regs.bit.sf
312     );
313 #endif
314
315     if (cpu.regs.word.pc == 0) {
316       putchar('\n');
317       break;
318     }
319     if (cpu.regs.word.pc == 5) {
320       cpu.regs.word.de = bdos(
321         cpu.regs.byte.c,
322         cpu.regs.word.de
323       );
324     }
325     cpu_z80_execute(&cpu);
326   }
327 #endif
328
329   return 0;
330 }