dd183593a1271d169cc548bf6e839b6bc1b7405e
[fuzix_sim.git] / sim / sim0.cpp
1 /*
2  * Z80SIM  -  a Z80-CPU simulator
3  *
4  * Copyright (C) 1987-2017 by Udo Munk
5  *
6  * History:
7  * 28-SEP-87 Development on TARGON/35 with AT&T Unix System V.3
8  * 11-JAN-89 Release 1.1
9  * 08-FEB-89 Release 1.2
10  * 13-MAR-89 Release 1.3
11  * 09-FEB-90 Release 1.4  Ported to TARGON/31 M10/30
12  * 20-DEC-90 Release 1.5  Ported to COHERENT 3.0
13  * 10-JUN-92 Release 1.6  long casting problem solved with COHERENT 3.2
14  *                        and some optimisation
15  * 25-JUN-92 Release 1.7  comments in english and ported to COHERENT 4.0
16  * 02-OCT-06 Release 1.8  modified to compile on modern POSIX OS's
17  * 18-NOV-06 Release 1.9  modified to work with CP/M sources
18  * 08-DEC-06 Release 1.10 modified MMU for working with CP/NET
19  * 17-DEC-06 Release 1.11 TCP/IP sockets for CP/NET
20  * 25-DEC-06 Release 1.12 CPU speed option
21  * 19-FEB-07 Release 1.13 various improvements
22  * 06-OCT-07 Release 1.14 bug fixes and improvements
23  * 06-AUG-08 Release 1.15 many improvements and Windows support via Cygwin
24  * 25-AUG-08 Release 1.16 console status I/O loop detection and line discipline
25  * 20-OCT-08 Release 1.17 frontpanel integrated and Altair/IMSAI emulations
26  * 24-JAN-14 Release 1.18 bug fixes and improvements
27  * 02-MAR-14 Release 1.19 source cleanup and improvements
28  * 14-MAR-14 Release 1.20 added Tarbell SD FDC and printer port to Altair
29  * 29-MAR-14 Release 1.21 many improvements
30  * 29-MAY-14 Release 1.22 improved networking and bugfixes
31  * 04-JUN-14 Release 1.23 added 8080 emulation
32  * 06-SEP-14 Release 1.24 bugfixes and improvements
33  * 18-FEB-15 Release 1.25 bugfixes, improvements, added Cromemco Z-1
34  * 18-APR-15 Release 1.26 bugfixes and improvements
35  * 18-JAN-16 Release 1.27 bugfixes and improvements
36  * 05-MAY-16 Release 1.28 improved usability
37  * 20-NOV-16 Release 1.29 bugfixes and improvements
38  * 15-DEC-16 Release 1.30 improved memory management, machine cycle correct CPUs
39  * 28-DEC-16 Release 1.31 improved memory management, reimplemented MMUs
40  * 12-JAN-17 Release 1.32 improved configurations, front panel, added IMSAI VIO
41  * 07-FEB-17 Release 1.33 bugfixes, improvements, better front panels
42  * 16-MAR-17 Release 1.34 improvements, added ProcTec VDM-1
43  * 03-AUG-17 Release 1.35 added UNIX sockets, bugfixes, improvements
44  * 21-DEC-17 Release 1.36 bugfixes and improvements
45  */
46
47 /*
48  *      This module contains the 'main()' function of the simulator,
49  *      where the options are checked and variables are initialised.
50  *      After initialisation of the UNIX interrupts ( int_on() )
51  *      and initialisation of the I/O simulation ( init_io() )
52  *      the user interface ( mon() ) is called.
53  */
54
55 #include <unistd.h>
56 #include <stdlib.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <libgen.h>
60 #include <ctype.h>
61 #include <fcntl.h>
62 #include <memory.h>
63 #include <sys/stat.h>
64 #include <sys/time.h>
65 #include "sim.h"
66 #include "simglb.h"
67 #include "config.h"
68 //#include "../../frontpanel/frontpanel.h"
69 #include "memory.h"
70
71 #define BUFSIZE 256             /* buffer size for file I/O */
72
73 static void init_cpu(void);
74 int load_core(void);
75 static void save_core(void);
76 static int load_mos(int, char *), load_hex(char *), checksum(char *);
77 extern void int_on(void), int_off(void), mon(void);
78 extern void init_io(void), exit_io(void);
79 extern int exatoi(char *);
80
81 BYTE *wrk_ram;                  /* work pointer for the memory */
82
83 int main(int argc, char *argv[])
84 {
85         register char *s, *p;
86         register int i;
87         char *pn = basename(argv[0]);
88         struct timeval tv;
89 #ifdef HAS_CONFIG
90         struct stat sbuf;
91 #endif
92 #ifdef BOOTROM
93         const char *rom = "-r ";
94 #else
95         const char *rom = "";
96 #endif
97 #ifdef CPU_SPEED
98         f_flag = CPU_SPEED;
99         tmax = CPU_SPEED * 10000;
100 #endif
101
102         while (--argc > 0 && (*++argv)[0] == '-')
103                 for (s = argv[0] + 1; *s != '\0'; s++)
104
105                         switch (*s) {
106                         case 's':       /* save core and CPU on exit */
107                                 s_flag = 1;
108                                 break;
109
110                         case 'l':       /* load core and CPU from file */
111                                 l_flag = 1;
112                                 break;
113
114                         case 'u':       /* trap undocumented ops */
115                                 u_flag = 1;
116                                 break;
117
118                         case 'i':       /* trap I/O on unused ports */
119                                 i_flag = 1;
120                                 break;
121
122                         case 'm':       /* initialise Z80 memory */
123                                 if (*(s+1) != '\0') {
124                                         m_flag = exatoi(s+1);
125                                         s += strlen(s+1);
126                                 } else {
127                                         if (argc <= 1)
128                                                 goto usage;
129                                         argc--;
130                                         argv++;
131                                         m_flag = exatoi(argv[0]);
132                                 }
133                                 break;
134
135                         case 'f':       /* set emulation speed */
136                                 if (*(s+1) != '\0') {
137                                         f_flag = atoi(s+1);
138                                         s += strlen(s+1);
139                                 } else {
140                                         if (argc <= 1)
141                                                 goto usage;
142                                         argc--;
143                                         argv++;
144                                         f_flag = atoi(argv[0]);
145                                 }
146                                 tmax = f_flag * 10000;
147                                 break;
148
149                         case 'x':       /* get filename with Z80 executable */
150                                 x_flag = 1;
151                                 s++;
152                                 if (*s == '\0') {
153                                         if (argc <= 1)
154                                                 goto usage;
155                                         argc--;
156                                         argv++;
157                                         s = argv[0];
158                                 }
159                                 p = xfn;
160                                 while (*s)
161                                         *p++ = *s++;
162                                 *p = '\0';
163                                 s--;
164                                 break;
165
166 #ifdef BOOTROM
167                         case 'r':       /* load default boot ROM */
168                                 x_flag = 1;
169                                 strcpy(xfn, BOOTROM);
170                                 break;
171 #endif
172
173 #ifdef HAS_DISKS
174                         case 'd':       /* get path for disk images */
175                                 s++;
176                                 if (*s == '\0') {
177                                         if (argc <= 1)
178                                                 goto usage;
179                                         argc--;
180                                         argv++;
181                                         s = argv[0];
182                                 }
183                                 p = diskdir = diskd;
184                                 while (*s)
185                                         *p++ = *s++;
186                                 *p = '\0';
187                                 s--;
188                                 break;
189 #endif
190
191                         case '8':
192                                 cpu = I8080;
193                                 break;
194
195                         case 'z':
196                                 cpu = Z80;
197                                 break;
198
199                         case '?':
200                         case 'h':
201                                 goto usage;
202
203                         default:
204                                 printf("illegal option %c\n", *s);
205
206 usage:
207
208 #ifdef HAS_DISKS
209                                 printf("usage:\t%s -z -8 -s -l -i -u %s-m val -f freq -x filename -d diskpath\n", pn, rom);
210 #else
211                                 printf("usage:\t%s -z -8 -s -l -i -u %s-m val -f freq -x filename\n", pn, rom);
212 #endif
213                                 puts("\t-z = emulate Zilog Z80");
214                                 puts("\t-8 = emulate Intel 8080");
215                                 puts("\t-s = save core and CPU");
216                                 puts("\t-l = load core and CPU");
217                                 puts("\t-i = trap on I/O to unused ports");
218                                 puts("\t-u = trap on undocumented instructions");
219 #ifdef BOOTROM
220                                 puts("\t-r = load and execute default ROM");
221                                 printf("\t     %s\n", BOOTROM);
222 #endif
223                                 puts("\t-m = init memory with val (00-FF)");
224                                 puts("\t-f = CPU clock frequency freq in MHz");
225                                 puts("\t-x = load and execute filename");
226 #ifdef HAS_DISKS
227                                 puts("\t-d = use disks images at diskpath");
228                                 puts("\t     default path for disk images:");
229                                 puts("\t     ./disks");
230                                 printf("\t     %s\n", DISKSDIR);
231 #endif
232                                 exit(1);
233                         }
234
235         putchar('\n');
236
237         if (cpu == Z80) {
238 puts("#######  #####    ###            #####    ###   #     #");
239 puts("     #  #     #  #   #          #     #    #    ##   ##");
240 puts("    #   #     # #     #         #          #    # # # #");
241 puts("   #     #####  #     #  #####   #####     #    #  #  #");
242 puts("  #     #     # #     #               #    #    #     #");
243 puts(" #      #     #  #   #          #     #    #    #     #");
244 puts("#######  #####    ###            #####    ###   #     #");
245
246         } else {
247
248 puts(" #####    ###     #####    ###            #####    ###   #     #");
249 puts("#     #  #   #   #     #  #   #          #     #    #    ##   ##");
250 puts("#     # #     #  #     # #     #         #          #    # # # #");
251 puts(" #####  #     #   #####  #     #  #####   #####     #    #  #  #");
252 puts("#     # #     #  #     # #     #               #    #    #     #");
253 puts("#     #  #   #   #     #  #   #          #     #    #    #     #");
254 puts(" #####    ###     #####    ###            #####    ###   #     #");
255         }
256
257         printf("\nRelease %s, %s\n", RELEASE, COPYR);
258
259 #ifdef  USR_COM
260         printf("%s Release %s, %s\n\n", USR_COM, USR_REL, USR_CPR);
261 #else
262         putchar('\n');
263 #endif
264         if (f_flag > 0)
265                 printf("CPU speed is %d MHz\n", f_flag);
266         else
267                 printf("CPU speed is unlimited\n");
268
269         fflush(stdout);
270
271         /* if the machine has configuration files try to find them */
272 #ifdef HAS_CONFIG
273         /* first try ./conf */
274         if ((stat("./conf", &sbuf) == 0) && S_ISDIR(sbuf.st_mode)) {
275                 strcpy(&confdir[0], "./conf");
276         /* then CONFDIR as set in Makefile */
277         } else {
278                 strcpy(&confdir[0], CONFDIR);
279         }
280
281         //printf("config = %s\n", &confdir[0]);
282 #endif
283
284         /* seed random generator */
285         gettimeofday(&tv, NULL);
286         srand(tv.tv_sec);
287
288         config();               /* read system configuration */
289         init_memory();          /* initialise memory configuration */
290         init_cpu();             /* initialise CPU */
291         wrk_ram = mem_base();   /* set work pointer for memory */
292
293         /* fill memory content with some initial value */
294         if (m_flag >= 0) {
295                 memset((char *) wrk_ram, m_flag, 65536);
296         } else {
297                 for (i = 0; i < 65536; i++)
298                         *(wrk_ram + i) = (BYTE) (rand() % 256);
299         }
300
301         init_rom();             /* initialise ROM's */
302
303         if (l_flag)             /* load core */
304                 if (load_core())
305                         return(1);
306
307         int_on();               /* initialize UNIX interrupts */
308         init_io();              /* initialize I/O devices */
309
310         mon();                  /* run system */
311
312         if (s_flag)             /* save core */
313                 save_core();
314
315         exit_io();              /* stop I/O devices */
316         int_off();              /* stop UNIX interrupts */
317
318         return(0);
319 }
320
321 /*
322  *      Initialize the CPU
323  */
324 static void init_cpu(void)
325 {
326         /* same for i8080 and Z80 */
327         PC = 0;
328         SP = rand() % 65536;
329         A = rand() % 256;
330         B = rand() % 256;
331         C = rand() % 256;
332         D = rand() % 256;
333         E = rand() % 256;
334         H = rand() % 256;
335         L = rand() % 256;
336         F = rand() % 256;
337
338         if (cpu == I8080) {     /* i8080 specific */
339                 F &= ~(N2_FLAG | N1_FLAG);
340                 F |= N_FLAG;
341         } else {                /* Z80 specific */
342                 I = 0;
343                 A_ = rand() % 256;
344                 B_ = rand() % 256;
345                 C_ = rand() % 256;
346                 D_ = rand() % 256;
347                 E_ = rand() % 256;
348                 H_ = rand() % 256;
349                 L_ = rand() % 256;
350                 F_ = rand() % 256;
351                 IX = rand() % 65536;
352                 IY = rand() % 65536;
353         }
354 }
355
356 /*
357  *      Reset the CPU
358  */
359 void reset_cpu(void)
360 {
361         IFF = int_int = int_nmi = int_protection = int_mode = 0;
362         int_data = -1;
363
364         PC = 0;
365
366         if (cpu == Z80) {
367                 I = 0;
368                 R = 0L;
369         }
370 }
371
372 /*
373  *      This function saves the CPU and the memory into the file core.z80
374  */
375 static void save_core(void)
376 {
377         int fd;
378
379         if ((fd = open("core.z80", O_WRONLY | O_CREAT, 0600)) == -1) {
380                 puts("can't open file core.z80");
381                 return;
382         }
383         write(fd, (char *) &A, sizeof(A));
384         write(fd, (char *) &F, sizeof(F));
385         write(fd, (char *) &B, sizeof(B));
386         write(fd, (char *) &C, sizeof(C));
387         write(fd, (char *) &D, sizeof(D));
388         write(fd, (char *) &E, sizeof(E));
389         write(fd, (char *) &H, sizeof(H));
390         write(fd, (char *) &L, sizeof(L));
391         write(fd, (char *) &A_, sizeof(A_));
392         write(fd, (char *) &F_, sizeof(F_));
393         write(fd, (char *) &B_, sizeof(B_));
394         write(fd, (char *) &C_, sizeof(C_));
395         write(fd, (char *) &D_, sizeof(D_));
396         write(fd, (char *) &E_, sizeof(E_));
397         write(fd, (char *) &H_, sizeof(H_));
398         write(fd, (char *) &L_, sizeof(L_));
399         write(fd, (char *) &I, sizeof(I));
400         write(fd, (char *) &IFF, sizeof(IFF));
401         write(fd, (char *) &R, sizeof(R));
402         write(fd, (char *) &PC, sizeof(PC));
403         write(fd, (char *) &SP, sizeof(SP));
404         write(fd, (char *) &IX, sizeof(IX));
405         write(fd, (char *) &IY, sizeof(IY));
406         write(fd, (char *) mem_base(), 65536);
407         close(fd);
408 }
409
410 /*
411  *      This function loads the CPU and memory from the file core.z80
412  */
413 int load_core(void)
414 {
415         int fd;
416
417         if ((fd = open("core.z80", O_RDONLY)) == -1) {
418                 puts("can't open file core.z80");
419                 return(1);
420         }
421         read(fd, (char *) &A, sizeof(A));
422         read(fd, (char *) &F, sizeof(F));
423         read(fd, (char *) &B, sizeof(B));
424         read(fd, (char *) &C, sizeof(C));
425         read(fd, (char *) &D, sizeof(D));
426         read(fd, (char *) &E, sizeof(E));
427         read(fd, (char *) &H, sizeof(H));
428         read(fd, (char *) &L, sizeof(L));
429         read(fd, (char *) &A_, sizeof(A_));
430         read(fd, (char *) &F_, sizeof(F_));
431         read(fd, (char *) &B_, sizeof(B_));
432         read(fd, (char *) &C_, sizeof(C_));
433         read(fd, (char *) &D_, sizeof(D_));
434         read(fd, (char *) &E_, sizeof(E_));
435         read(fd, (char *) &H_, sizeof(H_));
436         read(fd, (char *) &L_, sizeof(L_));
437         read(fd, (char *) &I, sizeof(I));
438         read(fd, (char *) &IFF, sizeof(IFF));
439         read(fd, (char *) &R, sizeof(R));
440         read(fd, (char *) &PC, sizeof(PC));
441         read(fd, (char *) &SP, sizeof(SP));
442         read(fd, (char *) &IX, sizeof(IX));
443         read(fd, (char *) &IY, sizeof(IY));
444         read(fd, (char *) mem_base(), 65536);
445         close(fd);
446
447         return(0);
448 }
449
450 /*
451  *      Read a file into the memory of the emulated CPU.
452  *      The following file formats are supported:
453  *
454  *              binary images with Mostek header
455  *              Intel hex
456  */
457 int load_file(char *s)
458 {
459         char fn[LENCMD];
460         BYTE fileb[5];
461         register char *pfn = fn;
462         int fd;
463
464         while (isspace((int)*s))
465                 s++;
466         while (*s != ',' && *s != '\n' && *s != '\0')
467                 *pfn++ = *s++;
468         *pfn = '\0';
469         if (strlen(fn) == 0) {
470                 puts("no input file given");
471                 return(1);
472         }
473         if ((fd = open(fn, O_RDONLY)) == -1) {
474                 printf("can't open file %s\n", fn);
475                 return(1);
476         }
477         if (*s == ',')
478                 wrk_ram = mem_base() + exatoi(++s);
479         else
480                 wrk_ram = NULL;
481         read(fd, (char *) fileb, 5); /* read first 5 bytes of file */
482         if (*fileb == (BYTE) 0xff) {    /* Mostek header ? */
483                 lseek(fd, 0l, SEEK_SET);
484                 return (load_mos(fd, fn));
485         }
486         else {
487                 close(fd);
488                 return (load_hex(fn));
489         }
490 }
491
492 /*
493  *      Loader for binary images with Mostek header.
494  *      Format of the first 3 bytes:
495  *
496  *      0xff ll lh
497  *
498  *      ll = load address low
499  *      lh = load address high
500  */
501 static int load_mos(int fd, char *fn)
502 {
503         BYTE fileb[3];
504         unsigned count, readn;
505         int rc = 0;
506
507         read(fd, (char *) fileb, 3);    /* read load address */
508         if (wrk_ram == NULL)            /* and set if not given */
509                 wrk_ram = mem_base() + (fileb[2] << 8) + fileb[1];
510         count = mem_base() + 65535 - wrk_ram;
511         if ((readn = read(fd, (char *) wrk_ram, count)) == count) {
512                 puts("Too much to load, stopped at 0xffff");
513                 rc = 1;
514         }
515         close(fd);
516         printf("Loader statistics for file %s:\n", fn);
517         printf("START : %04x\n", (unsigned int)(wrk_ram - mem_base()));
518         printf("END   : %04x\n", (unsigned int)(wrk_ram - mem_base()
519                                  + readn - 1));
520         printf("LOADED: %04x\n\n", readn);
521         PC = wrk_ram - mem_base();
522         return(rc);
523 }
524
525 /*
526  *      Loader for Intel hex
527  */
528 static int load_hex(char *fn)
529 {
530         register int i;
531         FILE *fd;
532         char buf[BUFSIZE];
533         char *s;
534         int count = 0;
535         int addr = 0;
536         int saddr = 0xffff;
537         int eaddr = 0;
538         int data;
539
540         if ((fd = fopen(fn, "r")) == NULL) {
541                 printf("can't open file %s\n", fn);
542                 return(1);
543         }
544
545         while (fgets(&buf[0], BUFSIZE, fd) != NULL) {
546                 s = &buf[0];
547                 while (isspace((int)*s))
548                         s++;
549                 if (*s != ':')
550                         continue;
551                 if (checksum(s + 1) != 0) {
552                         printf("invalid checksum in hex record: %s\n", s);
553                         return(1);
554                 }
555                 s++;
556                 count = (*s <= '9') ? (*s - '0') << 4 :
557                                       (*s - 'A' + 10) << 4;
558                 s++;
559                 count += (*s <= '9') ? (*s - '0') :
560                                        (*s - 'A' + 10);
561                 s++;
562                 if (count == 0)
563                         break;
564                 addr = (*s <= '9') ? (*s - '0') << 4 :
565                                      (*s - 'A' + 10) << 4;
566                 s++;
567                 addr += (*s <= '9') ? (*s - '0') :
568                                       (*s - 'A' + 10);
569                 s++;
570                 addr *= 256;
571                 addr += (*s <= '9') ? (*s - '0') << 4 :
572                                       (*s - 'A' + 10) << 4;
573                 s++;
574                 addr += (*s <= '9') ? (*s - '0') :
575                                       (*s - 'A' + 10);
576                 s++;
577                 if (addr < saddr)
578                         saddr = addr;
579                 if (addr >= eaddr)
580                         eaddr = addr + count - 1;
581                 s += 2;
582                 for (i = 0; i < count; i++) {
583                         data = (*s <= '9') ? (*s - '0') << 4 :
584                                              (*s - 'A' + 10) << 4;
585                         s++;
586                         data += (*s <= '9') ? (*s - '0') :
587                                               (*s - 'A' + 10);
588                         s++;
589                         *(mem_base() + addr + i) = data;
590                 }
591         }
592
593         fclose(fd);
594         count = eaddr - saddr + 1;
595         printf("Loader statistics for file %s:\n", fn);
596         printf("START : %04xH\n", saddr);
597         printf("END   : %04xH\n", eaddr);
598         printf("LOADED: %04xH (%d)\n\n", count, count);
599         PC = saddr;
600         wrk_ram = mem_base() + saddr;
601
602         return(0);
603 }
604
605 /*
606  *      Verify checksum of Intel hex records
607  */
608 static int checksum(char *s)
609 {
610         int chk = 0;
611
612         while ((*s != '\r') && (*s != '\n')) {
613                 chk += (*s <= '9') ?
614                         (*s - '0') << 4 :
615                         (*s - 'A' + 10) << 4;
616                 s++;
617                 chk += (*s <= '9') ?
618                         (*s - '0') :
619                         (*s - 'A' + 10);
620                 s++;
621         }
622
623         if ((chk & 255) == 0)
624                 return(0);
625         else
626                 return(1);
627 }