Implement 386 instruction table, improve 8086/186/286 instruction table
[multi_emu.git] / emu_pdp11.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 "simh/PDP11/pdp11_defs.h"
9 #include "simh/PDP11/pdp11_cpumod.h"
10 #undef fprintf
11 extern int32 REGFILE[6][2];     /* R0-R5, two sets */
12 extern int32 STACKFILE[4];      /* SP, four modes */
13 extern int32 saved_PC;          /* program counter */
14 extern int32 PSW;               /* PSW */
15 extern int32 last_pa;           /* pa from ReadMW/ReadMB */
16 t_stat cpu_reset (DEVICE *dptr);
17 #else
18 #include "cpu_pdp11.h"
19 #endif
20
21 #define EXCEPTION_TRAP_BASE 0x20
22
23 #define REG_TRACE 1
24 #define MEM_TRACE 1
25
26 #define MEM_SIZE 0x10000
27 #if __BYTE_ORDER == __BIG_ENDIAN
28 #define MEM_SIZE_M1 (MEM_SIZE - 1)
29 #define MEM_SIZE_M2 (MEM_SIZE - 2)
30 #else
31 #define MEM_SIZE_M1 0
32 #define MEM_SIZE_M2 0
33 #endif
34 union {
35   uint8_t b[MEM_SIZE];
36   uint16_t w[MEM_SIZE >> 1];
37 } mem;
38
39 int load_ihx(char *name) {
40   FILE *fp = fopen(name, "r");
41   if (fp == NULL) {
42     perror(name);
43     exit(EXIT_FAILURE);
44   }
45
46   int base = 0, entry_point = 0;
47   bool had_eof = false;
48   char line[0x100];
49   while (fgets(line, 0x100, fp)) {
50     for (char *p = line; *p; ++p)
51       if (*p == '\n') {
52         *p = 0;
53         break;
54       }
55
56     if (had_eof) {
57       fprintf(stderr, "garbage after EOF record: %s\n", line);
58       exit(EXIT_FAILURE);
59     }
60
61     if (line[0] != ':') {
62       fprintf(stderr, "require colon: %s\n", line);
63       exit(EXIT_FAILURE);
64     }
65
66     uint8_t buf[0x7f];
67     int len;
68     for (len = 0; len < 0x7f; ++len) {
69       char *p = line + 1 + len * 2;
70       if (*p == 0)
71         break;
72       if (*p == '\n') {
73         *p = 0;
74         break;
75       }
76       uint8_t c = p[2];
77       p[2] = 0;
78
79       char *q;
80       buf[len] = (uint8_t)strtol(p, &q, 16);
81       p[2] = c;
82       if (q != p + 2) {
83         fprintf(stderr, "not hex byte: %s\n", p);
84         exit(EXIT_FAILURE);
85       }
86     }
87
88     if (len == 0) {
89       fprintf(stderr, "empty line: %s\n", line);
90       exit(EXIT_FAILURE);
91     }
92
93     uint8_t checksum = 0;
94     for (int i = 0; i < len; ++i)
95       checksum += buf[i];
96     if (checksum) {
97       checksum -= buf[len - 1];
98       fprintf(
99         stderr,
100         "checksum %03o, should be %03o\n",
101         checksum,
102         buf[len - 1]
103       );
104       exit(EXIT_FAILURE);
105     }
106
107     len -= 5;
108     if (len != buf[0]) {
109       fprintf(stderr, "incorrect length: %s\n", line);
110       exit(EXIT_FAILURE);
111     }
112
113     int addr = (buf[1] << 8) | buf[2], end_addr;
114     switch (buf[3]) {
115     case 0:
116       addr += base;
117       end_addr = addr + len;
118       if (end_addr < addr || end_addr > MEM_SIZE) {
119         fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr);
120         exit(EXIT_FAILURE);
121       }
122       for (int i = 0; i < len; ++i)
123         mem.b[(addr + i) ^ MEM_SIZE_M1] = buf[4 + i];
124       break;
125     case 1:
126       had_eof = true;
127       break;
128 #if 0
129     case 3:
130       if (len < 4) {
131         fprintf(stderr, "invalid start address record: %s\n", line);
132         exit(EXIT_FAILURE);
133       }
134       entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
135       break;
136 #endif
137     case 4:
138       if (len < 2) {
139         fprintf(stderr, "invalid extended linear address record: %s\n", line);
140         exit(EXIT_FAILURE);
141       }
142       base = (buf[4] << 24) | (buf[5] << 16);
143       break;
144     case 5:
145       if (len < 4) {
146         fprintf(stderr, "invalid start linear address record: %s\n", line);
147         exit(EXIT_FAILURE);
148       }
149       entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
150       break;
151     default:
152       fprintf(stderr, "unknown record type: 0x%x\n", buf[3]);
153       exit(EXIT_FAILURE);
154     }
155   }
156   if (!had_eof) {
157     fprintf(stderr, "no EOF record\n");
158     exit(EXIT_FAILURE);
159   }
160
161   fclose(fp);
162   return entry_point;
163 }
164
165 int read_byte(void *context, int addr) {
166   int data;
167   switch (addr) {
168   case 0177560: // dl11 rcv csr
169     data = 0x80;
170     break;
171   case 0177564: // dl11 xmt csr
172     data = 0x80;
173     break;
174   default:
175     if (addr < 0 || addr >= MEM_SIZE) {
176       fprintf(stderr, "invalid read byte: %06o\n", addr);
177       exit(EXIT_FAILURE);
178     }
179     data = mem.b[addr ^ MEM_SIZE_M1];
180     break;
181   }
182 #if MEM_TRACE
183   fprintf(stderr, "addr=%06o rd=%03o\n", addr, data);
184 #endif
185   return data;
186 }
187
188 int read_word(void *context, int addr) {
189   int data;
190   switch (addr) {
191   case 0177550:
192     data = 0;
193     break;
194   case 0177560: // dl11 rcv csr
195     data = 0x80;
196     break;
197   case 0177562: // dl11 rcv buf
198     data = getchar();
199     switch (data) {
200     case '\n':
201       data = '\r';
202       break;
203     case 0x7f:
204       data = '\b';
205       break;
206 #if 0 // just keep sending 0xff at EOF
207     case EOF:
208       exit(EXIT_SUCCESS);
209       break;
210 #endif
211     }
212     break;
213   default:
214     if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
215       fprintf(stderr, "invalid read word: %06o\n", addr);
216       exit(EXIT_FAILURE);
217     }
218     data = mem.w[(addr ^ MEM_SIZE_M2) >> 1];
219     break;
220   }
221 #if MEM_TRACE
222   fprintf(stderr, "addr=%06o rd=%06o\n", addr, data);
223 #endif
224   return data;
225 }
226
227 void write_byte(void *context, int addr, int data) {
228 #if MEM_TRACE
229   fprintf(stderr, "addr=%06o wr=%03o\n", addr, data);
230 #endif
231   switch (addr) {
232   case 0177566: // dl11 xmt buf
233     data &= 0x7f;
234     switch (data) {
235     case '\r':
236       putchar('\n');
237       break;
238     case '\n':
239       break;
240     default:
241       putchar(data);
242       break;
243     }
244     break;
245   default:
246     if (addr < 0 || addr >= MEM_SIZE) {
247       fprintf(stderr, "invalid write byte: %06o\n", addr);
248       exit(EXIT_FAILURE);
249     }
250     mem.b[addr ^ MEM_SIZE_M1] = data;
251   }
252 }
253
254 void write_word(void *context, int addr, int data) {
255 #if MEM_TRACE
256   fprintf(stderr, "addr=%06o wr=%06o\n", addr, data);
257 #endif
258   switch (addr) {
259   case 0177560:
260     break;
261   default:
262     if (addr < 0 || addr >= MEM_SIZE || (addr & 1)) {
263       fprintf(stderr, "invalid write word: %06o\n", addr);
264       exit(EXIT_FAILURE);
265     }
266     mem.w[(addr ^ MEM_SIZE_M2) >> 1] = data;
267   }
268 }
269
270 #if ALT_BACKEND
271 int32 ReadE(int32 va) {
272   return read_word(NULL, va);
273 }
274
275 int32 ReadW(int32 va) {
276   return read_word(NULL, va);
277 }
278
279 int32 ReadB(int32 va) {
280   return read_byte(NULL, va);
281 }
282
283 int32 ReadCW(int32 va) {
284   return read_word(NULL, va);
285 }
286
287 int32 ReadMW(int32 va) {
288   last_pa = va;
289   return read_word(NULL, va);
290 }
291
292 int32 ReadMB(int32 va) {
293   last_pa = va;
294   return read_byte(NULL, va);
295 }
296
297 int32 PReadW(int32 pa) {
298   return read_word(NULL, pa);
299 }
300
301 int32 PReadB(int32 pa) {
302   return read_byte(NULL, pa);
303 }
304
305 void WriteW(int32 data, int32 va) {
306   write_word(NULL, va, data);
307 }
308
309 void WriteB(int32 data, int32 va) {
310   write_byte(NULL, va, data);
311 }
312
313 void WriteCW(int32 data, int32 va) {
314   write_word(NULL, va, data);
315 }
316
317 void PWriteW(int32 data, int32 pa) {
318   write_word(NULL, pa, data);
319 }
320
321 void PWriteB (int32 data, int32 pa) {
322   write_byte(NULL, pa, data);
323 }
324
325 int m68k_service_trap(unsigned int vector) {
326   //fprintf(stderr, "trap %x\n", vector);
327   if (vector == EXCEPTION_TRAP_BASE + 0xe) // TBI68K bye command
328     exit(EXIT_SUCCESS);
329   return 0;
330 }
331 #endif
332
333 int main(int argc, char **argv) {
334   if (argc < 2) {
335     printf("usage: %s image.ihx\n", argv[0]);
336     exit(EXIT_FAILURE);
337   }
338   int entry_point = load_ihx(argv[1]);
339
340 #if 0
341   // emulate Tutor firmware for TBI68K
342   mem.l[(0 ^ MEM_SIZE_M4) >> 2] = 0x8000; // initial SP
343   mem.l[(4 ^ MEM_SIZE_M4) >> 2] = 0x900; // initial PC
344 #endif
345
346 #if ALT_BACKEND
347   cpu_reset(NULL);
348   saved_PC = entry_point;
349
350   while (true) {
351 #if REG_TRACE
352     fprintf(
353       stderr,
354       "pc=%06o r0=%06o r1=%06o r2=%06o r3=%06o r4=%06o r5=%06o sp=%06o psw=%06o nf=%d zf=%d vf=%d cf=%d\n",
355       saved_PC,
356       REGFILE[0][0],
357       REGFILE[1][0],
358       REGFILE[2][0],
359       REGFILE[3][0],
360       REGFILE[4][0],
361       REGFILE[5][0],
362       STACKFILE[MD_KER],
363       PSW,
364       (PSW >> PSW_V_N) & 1,
365       (PSW >> PSW_V_Z) & 1,
366       (PSW >> PSW_V_V) & 1,
367       (PSW >> PSW_V_C) & 1
368     );
369 #endif
370
371     sim_instr();
372   }
373 #else
374   struct cpu_68000 cpu;
375   cpu_68000_init(&cpu, read_byte, NULL, write_byte, NULL);
376   cpu_68000_reset(&cpu);
377
378   while (true) {
379 #if REG_TRACE
380     fprintf(
381       stderr,
382       "pc=%06o a=%03o b=%03o s=%06o x=%06o p=%03o hf=%d if=%d nf=%d zf=%d vf=%d cf=%d\n",
383       cpu.regs.word.pc,
384       cpu.regs.byte.a,
385       cpu.regs.byte.b,
386       cpu.regs.word.s,
387       cpu.regs.word.x,
388       cpu.regs.byte.p,
389       cpu.regs.bit.hf,
390       cpu.regs.bit._if,
391       cpu.regs.bit.nf,
392       cpu.regs.bit.zf,
393       cpu.regs.bit.vf,
394       cpu.regs.bit.cf
395     );
396 #endif
397
398     cpu_68000_execute(&cpu);
399   }
400 #endif
401
402   return 0;
403 }