1 /*-----------------------------------------------------------------------*\
2  |  main.c  --  main driver program for the z80 emulator  --  all I/O    |
3  |  to the Unix world is done from this file  --  "z80.c" calls various  |
4  |  functions within this file                                           |
5  |                                                                       |
6  |  Copyright 1986-1988 by Parag Patel.  All Rights Reserved.            |
7  |  Copyright 1994-1995 by CodeGen, Inc.  All Rights Reserved.           |
8 \*-----------------------------------------------------------------------*/
9 
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <ctype.h>
18 
19 #include "defs.h"
20 #include "vt.h"
21 
22 #if defined macintosh
23 #	include <Types.h>
24 #	include <Events.h>
25 #	ifdef THINK_C
26 #		include <console.h>
27 #	endif
28 #elif defined DJGPP
29 #	include <pc.h>
30 #elif defined _WIN32
31 #
32 #else	/* UNIX */
33 #	include <unistd.h>
34 #	include <sys/ioctl.h>
35 #	if defined POSIX_TTY
36 #		include <sys/termios.h>
37 #	elif defined BeBox
38 #		include <termios.h>
39 #	else
40 #		include <termio.h>
41 #	endif
42 #endif
43 
44 #define INTR_CHAR	31	/* control-underscore */
45 
46 extern int errno;
47 
48 
49 /* globally visible vars */
50 static FILE *logfile = NULL;
51 
52 
53 #ifndef _WIN32
54 #  if defined UNIX || defined BeBox
55 #    ifdef POSIX_TTY
56 #	   define termio termios
57 #    endif
58 static struct termio rawterm, oldterm;	/* for raw terminal I/O */
59 #  endif
60 #endif
61 
62 #ifdef _WIN32
63 static int have_term = 0;   /* no terminal in Win32 */
64 #else
65 static int have_term = 1;   /* FALSE if terminal initialization failed */
66 #endif
67 
68 static void dumptrace(z80info *z80);
69 
70 
jgets(char * s,int len,FILE * f)71 char *jgets(char *s, int len, FILE *f)
72 {
73 	char *rtn = fgets(s, len, f);
74 	if (rtn)
75 	{
76 		int x;
77 		for (x = 0; s[x] && s[x] != '\r' && s[x] != '\n'; ++x);
78 		s[x] = 0;
79 	}
80 	return rtn;
81 }
82 
83 /*-----------------------------------------------------------------------*\
84  |  resetterm  --  reset terminal characteristics to original settings
85 \*-----------------------------------------------------------------------*/
86 
87 void
resetterm(void)88 resetterm(void)
89 {
90 #ifndef _WIN32
91     if (have_term)
92 		tcsetattr(fileno(stdin), TCSADRAIN, &oldterm);
93 #endif
94 }
95 
96 
97 
98 /*-----------------------------------------------------------------------*\
99  |  setterm  --  set terminal characteristics to raw mode
100 \*-----------------------------------------------------------------------*/
101 
102 void
setterm(void)103 setterm(void)
104 {
105 #ifndef _WIN32
106     if (have_term)
107 		tcsetattr(fileno(stdin), TCSADRAIN, &rawterm);
108 #endif
109 }
110 
111 
112 
113 /*-----------------------------------------------------------------------*\
114  |  initterm  --  initialize terminal stuff  --  called once on startup
115  |  and then after returning from a sub-shell
116 \*-----------------------------------------------------------------------*/
117 
118 static void
initterm(void)119 initterm(void)
120 {
121 #ifdef _WIN32
122 		fprintf(stderr, "Sorry, terminal not found, using cooked mode.\n");
123 		have_term = 0;
124 #else
125 	if (tcgetattr(fileno(stdin), &oldterm))
126 	{
127 		fprintf(stderr, "Sorry, terminal not found, using cooked mode.\n");
128 		have_term = 0;
129 	}
130         else {
131 	rawterm = oldterm;
132 	rawterm.c_iflag &= ~(ICRNL | IXON | IXOFF | INLCR | ICRNL);
133 	rawterm.c_lflag &= ~(ICANON | ECHO);
134 	rawterm.c_cc[VSUSP] = -1;
135 	rawterm.c_cc[VQUIT] = -1;
136 	rawterm.c_cc[VERASE] = -1;
137 	rawterm.c_cc[VKILL] = -1;
138 	}
139 	// tcsetattr(fileno(stdin), TCSADRAIN, &rawterm);
140 
141 #if 0
142 	/* rawterm.c_lflag &= ~(ISIG | ICANON | ECHO); */
143 	rawterm.c_lflag &= ~(ICANON | ECHO);
144 #ifdef IENQAK
145 	rawterm.c_iflag &= ~(IENQAK | IXON | IXOFF | INLCR | ICRNL);
146 #else
147 	rawterm.c_iflag &= ~(IXON | IXOFF | INLCR | ICRNL);
148 #endif
149 	rawterm.c_oflag &= ~OPOST;
150 	rawterm.c_cc[VINTR] = INTR_CHAR;
151 	rawterm.c_cc[VSUSP] = -1;
152 	rawterm.c_cc[VQUIT] = -1;
153 	rawterm.c_cc[VERASE] = -1;
154 	rawterm.c_cc[VKILL] = -1;
155 	rawterm.c_cc[VMIN] = 1;		/* MIN number of chars */
156 	rawterm.c_cc[VTIME] = 0;	/* TIME timeout value */
157 #endif
158 #endif
159 }
160 
161 
162 
163 
164 /*-----------------------------------------------------------------------*\
165  |  command  --  called when user-level commands are needed by the z80
166  |  for some reason or another
167 \*-----------------------------------------------------------------------*/
168 
169 static void
command(z80info * z80)170 command(z80info *z80)
171 {
172 	int i, j, t, e;
173 	char str[256], *s;
174 	FILE *fp;
175 	static word pe = 0;
176 	static word po = 0;
177 
178 	resetterm();
179 	printf("\n");
180 
181 loop:	/* "infinite" loop */
182 
183 	/* prompt for a command from the user & then do it */
184 	printf("Cmd: ");
185 	fflush(stdout);
186 	*str = '\0';
187 	fgets(str, sizeof str - 1, stdin);
188 
189 	for (s = str; *s == ' ' || *s == '\t'; s++)
190 		;
191 
192 	switch (isupper(*(unsigned char *)s) ? tolower(*(unsigned char *)s) : *s)
193 	{
194 	case '?':					/* help */
195 		printf("   Q(uit)  T(race on/off)  S(tep trace)  D(ump regs)\n");
196 		printf("   E(xamine memory)  P(oke memory)  R(egister modify)\n");
197 		printf("   L(oad binary)  C(ontinue running - <CR> if Step)\n");
198 		printf("   G(o) B(oot CP/M)  Z(80 disassembled dump)\n");
199 		printf("   W(write memory to file)  X,Y(-set/clear breakpoint)\n");
200 		printf("   O(output to \"logfile\")\n\n");
201 		printf("   !(fork shell)  ?(command list)  V(ersion)\n\n");
202 		break;
203 
204 	case 'o':
205 		if (logfile != NULL)
206 		{
207 			fclose(logfile);
208 			logfile = NULL;
209 			printf("    Logging off.\n");
210 		}
211 		else
212 		{
213 			printf("    Logfile name? ");
214 			jgets(str, sizeof(str), stdin);
215 
216 			for (s = str; isspace(*(unsigned char *)s); s++)
217 				;
218 
219 			if (*s == '\0')
220 				break;
221 
222 			logfile = fopen(s, "w");
223 
224 			if (logfile == NULL)
225 				printf("Cannot open logfile!\n");
226 			else
227 				printf("    Logging on.\n");
228 		}
229 
230 		break;
231 
232 	case '!':				/* fork a shell */
233 		system("exec ${SHELL:-/bin/sh}");
234 		initterm();
235 		printf("\n");
236 		break;
237 
238 	case 'q':				/* quit */
239 		if (logfile != NULL)
240 			fclose(logfile);
241 
242 		exit(0);
243 		break;
244 
245 	case 'v':				/* version */
246 		printf("  Version %s\n", VERSION);
247 		break;
248 
249 	case 'b':				/* boot cp/m */
250 		setterm();
251 		sysreset(z80);
252 		return;
253 		break;
254 
255 	case 't':				/* toggle trace mode */
256 		z80->trace = !z80->trace;
257 		printf("    Trace %s\n", z80->trace ? "on" : "off");
258 		break;
259 
260 	case 's':				/* toggle step-trace mode */
261 		z80->step = !z80->step;
262 		printf("    Step-trace %s\n", z80->step ? "on" : "off");
263 		printf("    Trace %s\n", z80->trace ? "on" : "off");
264 		break;
265 
266 	case 'd':					/* dump registers */
267 		dumptrace(z80);
268 		break;
269 
270 	case 'e':					/* examine memory */
271 		printf("    Starting at loc? (%.4X) : ", pe);
272 		jgets(str, sizeof(str), stdin);
273 		t = pe;
274 		sscanf(str, "%x", &t);
275 		pe = t;
276 
277 		for (i = 0; i <= 8; i++)
278 		{
279 			printf("  %.4X:   ", pe);
280 
281 			for (j = 0; j <= 0xF; j++)
282 				printf("%.2X  ", z80->mem[pe++]);
283 
284 			printf("\n");
285 		}
286 
287 		break;
288 
289 	case 'w':			/* write memory to file */
290 		printf("    Starting at loc? ");
291 		jgets(str, sizeof(str), stdin);
292 		sscanf(str, "%x", &t);
293 		printf("    Ending at loc? ");
294 		jgets(str, sizeof(str), stdin);
295 		sscanf(str, "%x", &e);
296 		fp = fopen("mem", "w");
297 
298 		if (fp == NULL)
299 			printf("Cannot open file 'mem' for writing!\n");
300 		else
301 		{
302 			j = 0;
303 
304 			for (i = t; i < e; i++)
305 			{
306 				if (j++ > 9)
307 				{
308 					fprintf(fp, "\n");
309 					j = 0;
310 				}
311 
312 				fprintf(fp, "0x%X, ", z80->mem[i]);
313 			}
314 
315 			fprintf(fp, "\n");
316 			fclose(fp);
317 		}
318 
319 		break;
320 
321 	case 'x':			/* set breakpoint */
322 #ifdef MEM_BREAK
323 		printf("    Set breakpoint at loc? (A for abort): ");
324 		jgets(str, sizeof(str), stdin);
325 
326 		if (tolower(*(unsigned char *)str) == 'a' || *str == '\0')
327 			break;
328 
329 		sscanf(str, "%x", &t);
330 
331 		if (t < 0 || t >= sizeof z80->mem)
332 		{
333 			printf("Cannot set breakpoint at addr 0x%X\n", t);
334 			break;
335 		}
336 
337 		if (!(z80->membrk[t] & M_BREAKPOINT))
338 		{
339 			printf("    Breakpoint set at addr 0x%X\n", t);
340 			z80->membrk[t] |= M_BREAKPOINT;
341 			z80->numbrks++;
342 		}
343 #else
344 		printf("Sorry, Z80 has not been compiled with MEM_BREAK.\n");
345 #endif /* MEM_BREAK */
346 		break;
347 
348 	case 'y':			/* clear breakpoints */
349 #ifdef MEM_BREAK
350 		printf("    Clear breakpoint at loc? (A for all) : ");
351 		jgets(str, sizeof(str), stdin);
352 
353 		if (tolower(*(unsigned char *)str) == 'a')
354 		{
355 			for (i = 0; i < sizeof z80->membrk; i++)
356 				z80->membrk[i] &= ~M_BREAKPOINT;
357 
358 			z80->numbrks = 0;
359 			printf("    All breakpoints cleared\n");
360 			break;
361 		}
362 
363 		sscanf(str, "%x", &t);
364 
365 		if (t < 0 || t >= sizeof z80->mem)
366 		{
367 			printf("    Cannot clear breakpoint at addr 0x%X\n", t);
368 			break;
369 		}
370 
371 		if (z80->membrk[t] & M_BREAKPOINT)
372 		{
373 			printf("Breakpoint cleared at addr 0x%X\n", t);
374 			z80->membrk[t] &= ~M_BREAKPOINT;
375 			z80->numbrks--;
376 		}
377 #else
378 		printf("Sorry, Z80 has not been compiled with MEM_BREAK.\n");
379 #endif /* MEM_BREAK */
380 		break;
381 
382 	case 'z':			/* z80 disassembled memory dump */
383 		printf("    Starting at loc? (%.4X) : ", pe);
384 		jgets(str, sizeof(str), stdin);
385 		t = pe;
386 		sscanf(str, "%x", &t);
387 		pe = t;
388 
389 		for (i = 0; i < 0x10; i++)
390 		{
391 			printf("  %.4X:    ", pe);
392 			j = pe;
393 			pe += disassem(z80, pe, stdout);
394 			t = disassemlen(z80);
395 
396 			while (t++ < 15)
397 				putchar(' ');
398 
399 			while (j < pe)
400 				printf("  %.2X", z80->mem[j++]);
401 
402 			printf("\n");
403 		}
404 
405 		break;
406 
407 	case 'p':				/* poke memory */
408 		printf("    Start at loc? (%.4X) : ", po);
409 		jgets(str, sizeof(str), stdin);
410 		sscanf(str, "%x", &i);
411 		po = i;
412 
413 		for (;;)
414 		{
415 			printf("    Mem[%.4X] (%.2X) = ", po, z80->mem[po]);
416 			jgets(str, sizeof(str), stdin);
417 
418 			for (s = str; *s == ' ' || *s == '\t'; s++)
419 				;
420 
421 			if (*s == '~')			/* exit? */
422 			{
423 				po = i;
424 				break;
425 			}
426 
427 			if (*s == '\0')		/* leave the value alone */
428 				continue;
429 
430 			j = 0;
431 			sscanf(str, "%x", &j);
432 			z80->mem[po] = j;
433 			po++;
434 		}
435 		break;
436 
437 	case 'r':				/* set a register */
438 		printf("    Value? = ");
439 		jgets(str, sizeof(str), stdin);
440 		i = 0;
441 		sscanf(str, "%x", &i);
442 		printf("    Reg? (A,F,B,C,D,E,H,L,IX,IY,SP,PC) : ");
443 		jgets(str, sizeof(str), stdin);
444 
445 		for (s = str; *s == ' ' || *s == '\t'; s++)
446 			;
447 
448 		switch (tolower(*(unsigned char *)s))
449 		{
450 		case 'a': A = i; break;
451 		case 'f': F = i; break;
452 		case 'b': B = i; break;
453 		case 'c': C = i; break;
454 		case 'd': D = i; break;
455 		case 'e': E = i; break;
456 		case 'h': H = i; break;
457 		case 'l': L = i; break;
458 		case 'i':
459 			if (tolower(((unsigned char *)s)[1]) == 'x')
460 				IX = i;
461 			else if (tolower(((unsigned char *)s)[1]) == 'y')
462 				IY = i;
463 
464 			break;
465 
466 		case 'x': IX = i; break;
467 		case 'y': IY = i; break;
468 		case 's': SP = i; break;
469 		case 'p': PC = i; break;
470 
471 		default:
472 			printf("No such register\n");
473 			break;
474 		}
475 
476 		break;
477 
478 	case 'l':			/* load a file into z80 memory */
479 		printf("    File-name: ");
480 		jgets(str, sizeof(str), stdin);
481 
482 		if (!loadfile(z80, str))
483 			fprintf(stderr, "Cannot load file %s!\r\n", str);
484 
485 		break;
486 
487 	case '\0':			/* carriage-return */
488 	case '\r':
489 	case '\n':
490 		if (z80->trace && z80->step)
491 			goto cont;
492 
493 		break;
494 
495 	case 'c':			/* continue z80 execution */
496 	case 'g':
497 	cont:
498 		setterm();
499 
500 		if (z80->trace)
501 		{
502 			z80->event = TRUE;
503 			z80->halt = TRUE;
504 		}
505 
506 		return;
507 
508 	default:
509 		/*putchar('\007');*/
510 		printf("\007Command \"%s\" not recognized\n", s);
511 		break;
512 	}
513 
514 	goto loop;
515 }
516 
517 
518 
519 
520 /*-----------------------------------------------------------------------*\
521  |  dumptrace  --  dump the z80 registers in an easy-to-trace format
522  |  --  note that the dump takes exactly one line so that changes in
523  |  register values are easier to spot  --  disassembles the z80 code
524 \*-----------------------------------------------------------------------*/
525 
526 static void
dumptrace(z80info * z80)527 dumptrace(z80info *z80)
528 {
529 	printf("a%.2X f%.2X bc%.4X de%.4X hl%.4X ",
530 			A, F, BC, DE, HL);
531 	printf("ix%.4X iy%.4X sp%.4X pc%.4X:%.2X  ",
532 			IX, IY, SP, PC, z80->mem[PC]);
533 	disassem(z80, PC, stdout);
534 	printf("\r\n");
535 
536 	if (logfile)
537 	{
538 		fprintf(logfile, "a%.2X f%.2X bc%.4X de%.4X hl%.4X ",
539 				A, F, BC, DE, HL);
540 		fprintf(logfile, "ix%.4X iy%.4X sp%.4X pc%.4X:%.2X  ",
541 				IX, IY, SP, PC, z80->mem[PC]);
542 		disassem(z80, PC, logfile);
543 		fprintf(logfile, "\r\n");
544 	}
545 }
546 
547 
548 
549 #define HEXVAL(c)	(('0' <= (c) && (c) <= '9') ? (c) - '0' :\
550 			(('a' <= (c) && (c) <= 'f') ? (c) - 'a' + 10 :\
551 			(('A' <= (c) && (c) <= 'F') ? (c) - 'A' + 10 :\
552 				-1 )))
553 
554 static int
gethex(FILE * fp)555 gethex(FILE *fp)
556 {
557 	int i, j;
558 
559 	i = getc(fp);
560 	j = getc(fp);
561 
562 	if (i < 0 || j < 0)
563 		return -1;
564 
565 	i = HEXVAL(i);
566 	j = HEXVAL(j);
567 
568 	if (i < 0 || j < 0)
569 		return -1;
570 
571 	return (i << 4) | j;
572 }
573 
574 
575 static int
loadhex(z80info * z80,FILE * fp)576 loadhex(z80info *z80, FILE *fp)
577 {
578 	int start = TRUE;
579 	int len, line, i;
580 	word addr, check, t;
581 
582 	for (line = 1; getc(fp) >= 0; line++)		/* should be a ':' */
583 	{
584 		if ((len = gethex(fp)) <= 0)
585 			break;
586 
587 		check = len;
588 
589 		if ((i = gethex(fp)) < 0)
590 			break;
591 
592 		addr = (word)i;
593 		check += addr;
594 
595 		if ((i = gethex(fp)) < 0)
596 			break;
597 
598 		t = (word)i;
599 		check += t;
600 		addr = (addr << 8) | t;
601 
602 		if (start)
603 			PC = addr, start = FALSE;
604 
605 		if ((i = gethex(fp)) < 0)		/* ??? */
606 			break;
607 
608 		check += (word)i;
609 
610 		while (len-- > 0)
611 		{
612 			if ((i = gethex(fp)) < 0)
613 				break;
614 
615 			t = (word)i;
616 			check += t;
617 			z80->mem[addr] = t;
618 			addr++;
619 		}
620 
621 		if ((i = gethex(fp)) < 0)		/* checksum */
622 			break;
623 
624 		t = (word)i;
625 
626 		if ((t + check) & 0xFF)
627 		{
628 			fprintf(stderr, "%d: Checksum error: %.2X != 0!\r\n",
629 					line, (t + check) & 0xFF);
630 			return FALSE;
631 		}
632 
633 		if (getc(fp) < 0)		/* should be a '\n' */
634 			break;
635 	}
636 
637 	return TRUE;
638 }
639 
640 
641 
642 /*-----------------------------------------------------------------------*\
643  |  getword  --  return a 16-bit word from the specified file
644 \*-----------------------------------------------------------------------*/
645 
646 static int
getword(FILE * file)647 getword(FILE *file)
648 {
649 	int w;
650 
651 	w = getc(file) << 8;
652 	w |= getc(file);
653 	return w;
654 }
655 
656 
657 
658 /*-----------------------------------------------------------------------*\
659  |  loadpisces  --  load the specified file (assumed to be in Pisces+
660  |  format) into the z80 memory for subsequent execution
661 \*-----------------------------------------------------------------------*/
662 
663 static int
loadpisces(z80info * z80,FILE * file)664 loadpisces(z80info *z80, FILE *file)
665 {
666 	int numbytes, i;
667 	unsigned short loadaddr;
668 
669 	/* ignore the 1st 12 words in the file - the 13th word is the starting
670 	   PC value - the 14th is also ignored */
671 	for (i = 0; i < 12; i++)
672 		getword(file);
673 
674 	PC = getword(file);
675 	getword(file);
676 
677 	/* read in each block of words into the z80 memory - each block
678 	   specifies the number of bytes in the block and the address to load
679 	   the data into */
680 	while (getword(file) != EOF)
681 	{
682 		numbytes = getword(file);
683 		loadaddr = getword(file);
684 		getword(file);
685 
686 		for (; numbytes > 0; numbytes -= 2)
687 		{
688 			z80->mem[loadaddr] = getc(file);
689 			loadaddr++;
690 			z80->mem[loadaddr] = getc(file);
691 			loadaddr++;
692 		}
693 	}
694 
695 	return TRUE;
696 }
697 
698 
699 static void
suffix(char * str,const char * suff)700 suffix(char *str, const char *suff)
701 {
702 	while(*str != '\0' && *str != '.')
703 		str++;
704 
705 	strcpy(str, suff);
706 }
707 
708 
709 boolean
loadfile(z80info * z80,const char * fname)710 loadfile(z80info *z80, const char *fname)
711 {
712 	char buf[200];
713 	FILE *fp;
714 	int ret;
715 
716 	if ((fp = fopen(fname, "r")) != NULL)
717 	{
718 		ret = loadhex(z80, fp);
719 		fclose(fp);
720 		return ret;
721 	}
722 
723 	strcpy(buf, fname);
724 	suffix(buf, ".hex");
725 
726 	if ((fp = fopen(buf, "r")) != NULL)
727 	{
728 		ret = loadhex(z80, fp);
729 		fclose(fp);
730 		return ret;
731 	}
732 
733 	strcpy(buf, fname);
734 	suffix(buf, ".X");
735 
736 	if ((fp = fopen(buf, "r")) != NULL)
737 	{
738 		ret = loadpisces(z80, fp);
739 		fclose(fp);
740 		return ret;
741 	}
742 
743 	return FALSE;
744 }
745 
746 
747 
748 /* input  --  z80 input instruction  --  this function is called whenever
749    an input ports is referenced from the z80 to handle the real I/O  --
750    it returns a byte to the z80 just like the real I/O instruction  --
751    the arguments represent the data on the bus as it would be for a real
752    z80 - this routine is restarted later if there is no input pending,
753    and we must wait for some to occur */
754 
755 boolean
input(z80info * z80,byte haddr,byte laddr,byte * val)756 input(z80info *z80, byte haddr, byte laddr, byte *val)
757 {
758 	int data;
759 
760 	/* just uses the lower 8-bits of the I/O address for now... */
761 	switch (laddr)
762 	{
763 
764 	/* return a character from the keyboard - wait for it if necessary  --
765 	   return "last" if we have already read in something via 0x01 */
766 	case 0x00:
767 		if (1)
768 		{
769 #if defined macintosh
770 			EventRecord ev;
771 
772 		again:
773 			fflush(stdout);
774 
775 			while (!WaitNextEvent(keyDownMask | autoKeyMask,
776 					&ev, 20, nil))
777 				;
778 
779 			data = ev.message & charCodeMask;
780 
781 			if ((data == '.' && (ev.modifiers & cmdKey)) ||
782 					data == INTR_CHAR)
783 			{
784 				command(z80);
785 				goto again;
786 			}
787 			else if (data == 'q' && (ev.modifiers & cmdKey))
788 				exit(0);
789 #elif defined DJGPP
790 			fflush(stdout);
791 			data = getkey();
792 
793 			while (data == INTR_CHAR)
794 			{
795 				command(z80);
796 				data = getkey();
797 			}
798 #else	/* TCGETA */
799 			fflush(stdout);
800 			data = kget(0);
801 //			data = getchar();
802 
803 			while ((data < 0 && errno == EINTR) ||
804 					data == INTR_CHAR)
805 			{
806 				command(z80);
807 				data = kget(0);
808 //				data = getchar();
809 			}
810 #endif
811 		}
812 
813 		*val = data & 0x7F;
814 		break;
815 
816 	/* return 0xFF if we have a character waiting to be read - save the
817 	   character in "last" for 0x00 above */
818 	case 0x01:
819 #if defined macintosh
820 		{
821 			EventRecord ev;
822 			*val = EventAvail(keyDownMask | autoKeyMask, &ev) ?
823 					0xFF : 0;
824 		}
825 #elif defined DJGPP
826 		*val = (kbhit()) ? 0xFF : 0;
827 #else	/* UNIX or BeBox */
828 		fflush(stdout);
829 
830 		if (constat())
831 			*val = 0xFF;
832 		else
833 			*val = 0x00;
834 
835 #endif
836 		break;
837 
838 	/* default - prompt the user for an input byte */
839 	default:
840 		resetterm();
841 		printf("INPUT : addr = %X%X    DATA = ", haddr, laddr);
842 		fflush(stdout);
843 		scanf("%x", &data);
844 		setterm();
845 		*val = data;
846 		break;
847 	}
848 
849 	return TRUE;
850 }
851 
852 
853 /*-----------------------------------------------------------------------*\
854  |  output  --  output the data at the specified I/O address
855 \*-----------------------------------------------------------------------*/
856 
857 void
output(z80info * z80,byte haddr,byte laddr,byte data)858 output(z80info *z80, byte haddr, byte laddr, byte data)
859 {
860 	if (laddr == 0xFF) {
861 		/* BIOS call - interrupt the z80 before the next instruction
862 		   since we may have to mess with the PC & other stuff -
863 		   otherwise we would do it right here */
864 		z80->event = TRUE;
865 		z80->halt = TRUE;
866 		z80->syscall = TRUE;
867 		z80->biosfn = data;
868 
869 		if (z80->trace)
870 		{
871 			printf("BIOS call %d\r\n", z80->biosfn);
872 
873 			if (logfile)
874 				fprintf(logfile, "BIOS call %d\r\n",
875 					z80->biosfn);
876 		}
877 	} else if (laddr == 0) {
878 		/* output a character to the screen */
879 		// putchar(data);
880 		vt52(data);
881 
882 		if (logfile != NULL)
883 			putc(data, logfile);
884 	} else {
885 		/* dump the data for our user */
886 		printf("OUTPUT: addr = %X%X  DATA = %X\r\n", haddr, laddr,data);
887 	}
888 }
889 
890 
891 
892 /*-----------------------------------------------------------------------*\
893  |  haltcpu  --  this is called after the z80 halts  --  it is used for
894  |  tracing & such
895 \*-----------------------------------------------------------------------*/
896 
897 void
haltcpu(z80info * z80)898 haltcpu(z80info *z80)
899 {
900 	z80->halt = FALSE;
901 
902 	/* we were interrupted by a Unix signal */
903 	if (z80->sig)
904 	{
905 		if (z80->sig != SIGINT)
906 			printf("\r\nCaught signal %d.\r\n", z80->sig);
907 
908 		z80->sig = 0;
909 		command(z80);
910 		return;
911 	}
912 
913 	/* we are tracing execution of the z80 */
914 	if (z80->trace)
915 	{
916 		/* re-enable tracing */
917 		z80->event = TRUE;
918 		z80->halt = TRUE;
919 		dumptrace(z80);
920 
921 		if (z80->step)
922 			command(z80);
923 	}
924 
925 	/* a CP/M syscall - done here so tracing still works */
926 	if (z80->syscall)
927 	{
928 		z80->syscall = FALSE;
929 		bios(z80, z80->biosfn);
930 	}
931 }
932 
933 word
read_mem(z80info * z80,word addr)934 read_mem(z80info *z80, word addr)
935 {
936 #ifdef MEM_BREAK
937 	if (z80->membrk[addr] & M_BREAKPOINT)
938 	{
939 		fprintf(stderr, "\r\nBreak at 0x%X\r\n", addr);
940 	}
941 	else if (z80->membrk[addr] & M_READ_PROTECT)
942 	{
943 		fprintf(stderr,
944 			"\r\nAttempt to read protected memory at 0x%X\r\n",
945 			addr);
946 	}
947 	else if (z80->membrk[addr] & M_MEM_MAPPED_IO)
948 	{
949 		fprintf(stderr,
950 			"\r\nAttempt to perform mem-mapped input at 0x%X\r\n",
951 			addr);
952 		/* fake some sort of I/O here and return its value */
953 	}
954 
955 	dumptrace(z80);
956 	command(z80);
957 #endif	/* MEM_BREAK */
958 
959 	return z80->mem[addr];
960 }
961 
962 word
write_mem(z80info * z80,word addr,byte val)963 write_mem(z80info *z80, word addr, byte val)
964 {
965 #ifdef MEM_BREAK
966 	if (z80->membrk[addr] & M_BREAKPOINT)
967 	{
968 		fprintf(stderr, "\r\nBreak at 0x%X\r\n", addr);
969 	}
970 	else if (z80->membrk[addr] & M_WRITE_PROTECT)
971 	{
972 		fprintf(stderr,
973 			"\r\nAttempt to write to protected memory at 0x%X\r\n",
974 			addr);
975 	}
976 	else if (z80->membrk[addr] & M_MEM_MAPPED_IO)
977 	{
978 		fprintf(stderr,
979 			"\r\nAttempt to perform mem-mapped output at 0x%X\r\n",
980 			addr);
981 		/* fake some sort of I/O here and set mem to its value, */
982 		/* then return */
983 	}
984 
985 	dumptrace(z80);
986 	command(z80);
987 #endif	/* MEM_BREAK */
988 
989 	return z80->mem[addr] = val;
990 }
991 
992 void
undefinstr(z80info * z80,byte instr)993 undefinstr(z80info *z80, byte instr)
994 {
995 	printf("\r\nIllegal instruction 0x%.2X at PC=0x%.4X\r\n",
996 		instr, PC - 1);
997 	command(z80);
998 }
999 
1000 
1001 
1002 /*-----------------------------------------------------------------------*\
1003  |  quit -- terminate this program after cleaning up -- this it is       |
1004  |  intended to catch unused signals & not leave the terminal hosed      |
1005 \*-----------------------------------------------------------------------*/
1006 
1007 static void
quit(int sig)1008 quit(int sig)
1009 {
1010 	printf("\r\nCaught signal %d.\r\n", sig);
1011 	resetterm();
1012 	exit(2);
1013 }
1014 
1015 
1016 /* this is needed by both interrupt() and main() */
1017 static z80info *z80 = NULL;
1018 
1019 
1020 /*-----------------------------------------------------------------------*\
1021  |  interrupt  --  this is called when we get a usable signal from Unix
1022 \*-----------------------------------------------------------------------*/
1023 
1024 static void
interrupt(int s)1025 interrupt(int s)
1026 {
1027 	/* we tell the z80 to stop when convenient, then reset & continue */
1028 	if (z80 != NULL)
1029 	{
1030 	    z80->event = TRUE;
1031 	    z80->halt = TRUE;
1032 	    z80->sig = s;
1033 	}
1034 
1035 	signal(s, interrupt);
1036 }
1037 
1038 
1039 /*-----------------------------------------------------------------------*\
1040  |  main  --  set up the global vars & run the z80
1041 \*-----------------------------------------------------------------------*/
1042 
1043 int
main(int argc,const char * argv[])1044 main(int argc, const char *argv[])
1045 {
1046 	int x;
1047 	char cmd[256];
1048 	int help = 0;
1049 
1050 	cmd[0] = 0;
1051 
1052 	for (x = 1; argv[x]; ++x) {
1053 		if (argv[x][0] == '-' && argv[x][1] == '-') {
1054 			if (!strcmp(argv[x], "--help")) {
1055 				help = 1;
1056 			} else if (!strcmp(argv[x], "--nobdos")) {
1057 				nobdos = 1;
1058 			} else if (!strcmp(argv[x], "--trace_bdos")) {
1059 				trace_bdos = 1;
1060 			} else if (!strcmp(argv[x], "--strace")) {
1061 				strace = 1;
1062 			} else {
1063 				fprintf(stderr, "Unknown option %s\n", argv[x]);
1064 				exit(1);
1065 			}
1066 		} else {
1067 			if (!cmd[0]) {
1068 				strcpy(cmd, argv[x]);
1069 			} else {
1070 				strcat(cmd, " ");
1071 				strcat(cmd, argv[x]);
1072 			}
1073 		}
1074 	}
1075 
1076 	if (help) {
1077 		fprintf(stderr, "\n%s [options] [CP/M command]\n", argv[0]);
1078 		fprintf(stderr, "\n   Options:\n\n");
1079 		fprintf(stderr, "    --help         Show this help\n");
1080 		fprintf(stderr, "    --nobdos       Do not emulate BDOS: only emulate BIOS\n");
1081 		fprintf(stderr, "                   Real disk images will be used.        \n");
1082 		fprintf(stderr, "    --trace_bdos   Trace BDOS calls\n");
1083 		fprintf(stderr, "\n");
1084 		exit(0);
1085 	}
1086 
1087 	if (cmd[0]) {
1088 		stuff_cmd = cmd;
1089 	}
1090 
1091 	z80 = new_z80info();
1092 
1093 	if (z80 == NULL)
1094 		return -1;
1095 
1096 	initterm();
1097 
1098 	/* set up the signals */
1099 #ifdef SIGQUIT
1100 	signal(SIGQUIT, quit);
1101 #endif
1102 #ifdef SIGHUP
1103 	signal(SIGHUP, quit);
1104 #endif
1105 #ifdef SIGTERM
1106 	signal(SIGTERM, quit);
1107 #endif
1108 #ifdef SIGINT
1109 	signal(SIGINT, interrupt);
1110 #endif
1111 
1112 	setterm();
1113 
1114 	sysreset(z80);
1115 
1116 	while (1)
1117 	{
1118 #ifdef macintosh
1119 		EventRecord ev;
1120 		WaitNextEvent(0, &ev, 0, nil);
1121 #endif
1122 		z80_emulator(z80, 100000);
1123 	}
1124 }
1125