1 /*  Basic i/o system module (yaze-bios).
2     Copyright (C) 1995  Frank D. Cringle.
3     Modifications for keytranslation by Jon Saxton (c) 2015
4     Modifications for CP/M 3.1 Copyright (C) 2000/2013 by Andreas Gerlich (agl)
5 
6     This file is part of yaze-ag - yet another Z80 emulator by ag.
7 
8     Yaze-ag is free software; you can redistribute it and/or modify it under
9     the terms of the GNU General Public License as published by the Free
10     Software Foundation; either version 2 of the License, or (at your
11     option) any later version.
12 
13     This program is distributed in the hope that it will be useful, but
14     WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16     General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <limits.h>
27 #include <fcntl.h>
28 #include <termios.h>
29 #include <ctype.h>
30 #include <signal.h>
31 #include <sys/types.h>
32 #include <time.h>
33 #include <sys/time.h>
34 #include <sys/stat.h>
35 #include <dirent.h>
36 #include <sys/mman.h>
37 #include <string.h>
38 #include <glob.h>
39 /* #include <stdbool.h> */
40 #include <sys/ioctl.h>
41 
42 
43 #include "mem_mmu.h"
44 #include "simz80.h"
45 #include "yaze.h"
46 #include "ybios.h"
47 #include "ktt.h"
48 
49 /* Z80 registers */
50 #define AF	af[af_sel]
51 #define BC	regs[regs_sel].bc
52 #define DE	regs[regs_sel].de
53 #define HL	regs[regs_sel].hl
54 
55 /* cp/m 3 */
56 
57 int cpm3 = 0;		/* is 1 then CP/M 3 is running		*/
58 WORD dtbl = 0xfffe;	/* address of @dtbl in CP/M 3.1 bios	*/
59 static WORD yct;	/* z80 - pointer to @YCT		*/
60 static WORD t_sssd;	/* z80 - pointer to translation table SSSD */
61 static WORD scb;	/* Pointer to SCB (System Control Block) */
62 WORD maxdsm = 0x0;		/* maximal DSM, reported by the bios	*/
63 WORD maxdrm = 0x0;		/* maximal DRM, reported by the bios	*/
64 
65 /* location on current disk */
66 static WORD	track;
67 static WORD	sector;
68 /* memory pointer */
69 static WORD	dma;
70 
71 #ifdef MMU
72 static WORD	cbnk = 0; /* current and */
73 static WORD	dbnk = 0; /* destination bank, default 0 */
74 static WORD	bnkdest, bnksrc;
75 
76 /* static pagetab_struct *dmmu = &MMUtable[0];  \* <-- see mem_mmu.c */
77 #endif
78 
79 typedef unsigned long COUNT;
80 static COUNT	bioscount;
81 static BYTE	old_iobyte;
82 
new_iobyte(int io)83 static void new_iobyte(int io)
84 {
85     switch ((io >> 6) & 3)
86     {
87     case 0:
88         chn[CHNlst] = TTYout;
89         break;
90     case 1:
91         chn[CHNlst] = CRTout;
92         break;
93     case 2:
94         chn[CHNlst] = LPTout;
95         break;
96     case 3:
97         chn[CHNlst] = UL1out;
98         break;
99     }
100     switch ((io >> 4) & 3)
101     {
102     case 0:
103         chn[CHNpun] = TTYout;
104         break;
105     case 1:
106         chn[CHNpun] = PUNout;
107         break;
108     case 2:
109         chn[CHNpun] = UP1out;
110         break;
111     case 3:
112         chn[CHNpun] = UP2out;
113         break;
114     }
115     switch ((io >> 2) & 3)
116     {
117     case 0:
118         chn[CHNrdr] = TTYin;
119         break;
120     case 1:
121         chn[CHNrdr] = RDRin;
122         break;
123     case 2:
124         chn[CHNrdr] = UR1in;
125         break;
126     case 3:
127         chn[CHNrdr] = UR2in;
128         break;
129     }
130     switch (io & 3)
131     {
132     case 0:
133         chn[CHNconin] = TTYin;
134         chn[CHNconout] = TTYout;
135         break;
136     case 1:
137         chn[CHNconin] = CRTin;
138         chn[CHNconout] = CRTout;
139         break;
140     case 2:
141         chn[CHNconin] = chn[CHNrdr];
142         chn[CHNconout] = chn[CHNlst];
143         break;
144     case 3:
145         chn[CHNconin] = UC1in;
146         chn[CHNconout] = UC1out;
147         break;
148     }
149     old_iobyte = io;
150     chn[CHNaux] = AUX;	/* necessary for CP/M 3 */
151 }
152 
getsiop(int chan)153 static struct sio *getsiop(int chan)
154 {
155     if (GetBYTE(3) != old_iobyte)
156         new_iobyte(GetBYTE(3));
157     return &siotab[chn[chan]];
158 }
159 
bios_init(const char * initfile)160 int bios_init(const char *initfile)
161 {
162     int go = 0;
163     FILE *su;
164     const char *fn = initfile;
165     char *name;
166     char buf[BUFSIZ];
167 
168 #ifdef DEBUG
169     signal(SIGINT, sighand);
170 #endif
171     if (z3env && z3env > bios_top && z3env < dptable)
172     {
173         /* reserve space for a z-system environment page */
174         WORD p = z3env - bios_top;
175         int i = 1024;
176         while (i--)
177         {
178             global_alv[p >> 3] |= (0x80 >> (p & 7));
179             p++;
180         }
181     }
182     siotab[CRTin].fp = stdin;
183     name = ttyname(fileno(stdin));
184     siotab[CRTin].filename = name ? newstr(name) : "(stdin)";
185     siotab[CRTin].tty = isatty(fileno(stdin));
186     siotab[CRTout].fp = stdout;
187     name = ttyname(fileno(stdout));
188     siotab[CRTout].filename = name ? newstr(name) : "(stdout)";
189     siotab[CRTout].tty = isatty(fileno(stdout));
190     if (siotab[CRTin].tty)
191     {
192         ttyflags = ISATTY;
193         if (tcgetattr(fileno(stdin), &cookedtio) != 0)
194         {
195             perror("tcgetattr");
196             exit(1);
197         }
198         rawtio = cookedtio;
199         rawtio.c_iflag = 0;
200         rawtio.c_oflag = 0;
201         rawtio.c_lflag = interrupt ? ISIG : 0;
202         memclr(rawtio.c_cc, NCCS);
203         rawtio.c_cc[VINTR] = interrupt;
204         rawtio.c_cc[VMIN] = 1;
205     }
206     if (*initfile != '/' && access(initfile, R_OK) != 0)
207     {
208         char *h = getenv("HOME");
209         if (h)
210         {
211             fn = buf;
212             sprintf((char *) fn, "%s/%s", h, initfile);
213         }
214     }
215     if ((su = fopen(fn, "r")) == NULL)
216         return 0;
217     while (!go && fgets(buf, BUFSIZ - 1, su))
218         go = docmd(buf);
219     fclose(su);
220     atexit(ttycook);
221     ttyraw();
222     return go;
223 }
224 
225 
226 /********* handler for window size update *********/
227 
228 volatile WORD lines = 0;
229 volatile WORD columns = 0;
230 volatile bool winsize_updated = false;
231 volatile int signal_number = 0;
232 
update_winsize()233 bool update_winsize() {
234    struct winsize winsize;
235    if (ioctl(fileno(stdin), TIOCGWINSZ, &winsize) < 0) {
236 	return false;
237    } else {
238 	lines = winsize.ws_row;
239 	columns = winsize.ws_col;
240 	return true;
241    }
242 }
243 
winsize_handler(int signo)244 void winsize_handler(int signo) {
245    signal_number = signo;
246    winsize_updated = update_winsize();
247 }
248 
initSIGWINSZ()249 void initSIGWINSZ()
250 {
251    static struct sigaction action; /* must be static !!! */
252 
253    if (!update_winsize()) {
254 	perror("stdin"); exit(1);
255    }
256    /*
257    else {
258 	printf("initSIGWINSZ: columns %hu, lines %hu\r\n", columns, lines);
259    }
260    */
261 
262    action.sa_handler = winsize_handler;
263 
264    if (sigaction(SIGWINCH, &action, 0) != 0) {
265 	perror("sigaction"); exit(1);
266    }
267 
268    /* Set windows size in the SCB of CP/M 3 */
269    if (cpm3) {
270 	PutBYTE(scb + SCB_columns, lreg(columns-1));
271 	PutBYTE(scb + SCB_lines, lreg(lines-1));
272    }
273    winsize_updated = true;
274    /* printf("initSIGWINSZ: durchlaufen! SCB-Adresse: %4X\r\n", scb); */
275 }
276 
277 
278 /*  The constat() routine is complicated by the need to deal with CP/M programs
279     that poll console status without doing any other bios calls.
280     Examples are Wordstar and Backgrounder II.  In Wordstar the purpose is to
281     act as a crude timer that can be interrupted by the user pressing a key
282     (delay before a menu is shown).  In Backgrounder it's an artifact of the
283     pseudo multitasking logic.
284 
285     A simple-minded constat() implementation leads to such CP/M programs soaking
286     up 100% of the host CPU and getting confused about the system speed.
287 
288     The 2 constants CSTTIME and CSTCOUNT control the emulation.
289     constat() normally returns the current console status immediately.
290     However, after it has been called CSTCOUNT times without any other bios
291     activity, it waits CSTTIME microseconds (or until a character is available)
292     before returning.
293 */
294 
295 #define CSTTIME			100000
296 #define CSTCOUNT		  2000
297 #define INTERKEY_TIMEOUT	 50000
298 
299 static const struct timeval immediate = { 0, 0 };
300 static const struct timeval delay = { 0, CSTTIME };
301 
constat(void)302 static int constat(void)
303 {
304     static int consecutive;		/* number of consecutive calls */
305     static COUNT lastcount;		/* previous call */
306     struct timeval t = immediate;
307     fd_set rdy;
308     struct sio *s = getsiop(CHNconin);
309     int fd;
310 
311     if (s->fp == NULL)			/* no file */
312         return 0;
313     if (s->tty == 0)			/* disk files are always ready */
314         return 1;
315     if (bioscount != lastcount + 1)
316         consecutive = 0;
317     else if (++consecutive == CSTCOUNT)
318     {
319         consecutive = 0;
320         t = delay;
321     }
322     lastcount = bioscount;
323 
324     fd = fileno(s->fp);
325     FD_ZERO(&rdy);
326     FD_SET(fd, &rdy);
327     (void) select(fd + 1, &rdy, NULL, NULL, &t);
328     return FD_ISSET(fd, &rdy);
329 }
330 
331 /*
332     This is a slight variant of the constat() routine which does
333     not spin many times before doing a timed wait.  It is used
334     by the BIOS console input routine to handle input sequences
335     which can be prefixes of multi-byte keystrokes.
336 */
337 
contest()338 int contest()
339 {
340     struct timeval t = { 0, INTERKEY_TIMEOUT };
341     fd_set rdy;
342     struct sio *s = getsiop(CHNconin);
343     int fd;
344 
345     if (s->fp == NULL)			/* no file */
346         return 0;
347     if (s->tty == 0)			/* disk files are always ready */
348         return 1;
349     fd = fileno(s->fp);
350     FD_ZERO(&rdy);
351     FD_SET(fd, &rdy);
352     (void) select(fd + 1, &rdy, NULL, NULL, &t);
353     return FD_ISSET(fd, &rdy);
354 }
355 
356 static int
lststat(void)357 lststat(void)
358 {
359     static int consecutive;		/* number of consecutive calls */
360     static COUNT lastcount;		/* previous call */
361     struct timeval t = immediate;
362     fd_set rdy;
363     struct sio *s = getsiop(CHNlst);
364     int fd;
365 
366     if (s->fp == NULL)			/* no file */
367         return 0;
368     if (s->tty == 0)			/* disk files are always ready */
369         return 1;
370     if (bioscount != lastcount + 1)
371         consecutive = 0;
372     else if (++consecutive == CSTCOUNT)
373     {
374         consecutive = 0;
375         t = delay;
376     }
377     lastcount = bioscount;
378 
379     fd = fileno(s->fp);
380     FD_ZERO(&rdy);
381     FD_SET(fd, &rdy);
382     (void) select(fd + 1, &rdy, NULL, NULL, &t);
383     return FD_ISSET(fd, &rdy);
384 }
385 
386 #ifdef MMU
387 static int
auxistat(void)388 auxistat(void)
389 {
390     static int consecutive;		/* number of consecutive calls */
391     static COUNT lastcount;		/* previous call */
392     struct timeval t = immediate;
393     fd_set rdy;
394     struct sio *s = getsiop(CHNaux);
395     int fd;
396 
397     if (s->fp == NULL)			/* no file */
398         return 0;
399     if (s->tty == 0)			/* disk files are always ready */
400         return 1;
401     if (bioscount != lastcount + 1)
402         consecutive = 0;
403     else if (++consecutive == CSTCOUNT)
404     {
405         consecutive = 0;
406         t = delay;
407     }
408     lastcount = bioscount;
409 
410     fd = fileno(s->fp);
411     FD_ZERO(&rdy);
412     FD_SET(fd, &rdy);
413     (void) select(fd + 1, &rdy, NULL, NULL, &t);
414     return FD_ISSET(fd, &rdy);
415 }
416 
417 static int
auxostat(void)418 auxostat(void)
419 {
420     static int consecutive;		/* number of consecutive calls */
421     static COUNT lastcount;		/* previous call */
422     struct timeval t = immediate;
423     fd_set rdy;
424     struct sio *s = getsiop(CHNaux);
425     int fd;
426 
427     if (s->fp == NULL)			/* no file */
428         return 0;
429     if (s->tty == 0)			/* disk files are always ready */
430         return 1;
431     if (bioscount != lastcount + 1)
432         consecutive = 0;
433     else if (++consecutive == CSTCOUNT)
434     {
435         consecutive = 0;
436         t = delay;
437     }
438     lastcount = bioscount;
439 
440     fd = fileno(s->fp);
441     FD_ZERO(&rdy);
442     FD_SET(fd, &rdy);
443     /* (void) select(fd+1, &rdy, NULL, NULL, &t); */
444     (void) select(fd + 1, NULL, &rdy, NULL, &t);
445     return FD_ISSET(fd, &rdy);
446 }
447 #endif
448 
449 int
serin(int chan)450 serin(int chan)
451 {
452     char c;
453     int ch;
454     int count;
455     struct sio *s = getsiop(chan);
456 
457     if (s->fp == NULL)
458         return 0x1a;
459     if (s->tty)
460 /*  {	if ( fileno(s->fp) == fileno(stdin) )  */
461     {	if ( s->fp == stdin ) {
462 
463 	  if (!winsize_updated && cpm3) {
464 		initSIGWINSZ();  /* install win size haendler only when cpm3
465 				    runs */
466 		if (columns > 99) {
467 		   printf("\r\n\nWARNING of the Window size signal handler: ");
468 		   printf("Current window size: %hu columns, %hu lines\r\n",
469 							columns, lines);
470 		   printf("ATTENTION: Wordstar 4.00 can only"
471 			  " correct display the screen"
472 			  " up to 99 columns!\r\n");
473 		   printf("\nPAUSE Press any Key ... ");
474 		   fflush(stdout);
475 		}
476 		/*
477 		printf("serin: initSIGWINSZ durchlaufen!\r\n");
478 		printf("serin: columns %hu, lines %hu\r\n", columns, lines);
479 		printf("serin: GetByte(columns): %u, GetByte(lines): %u\r\n",
480 			GetBYTE(scb + SCB_columns), GetBYTE(scb + SCB_lines));
481 		*/
482 	  }
483 
484 	  for(;;)
485 	    switch ( count = read(fileno(s->fp), &c, 1) ) {
486 
487 		case 0:	  /*  printf("EOF (End Of File)\r\n");
488 			      exit(0);
489 			  */
490 			 return 0x1a;
491 
492 		case -1: switch (errno) {
493 			  case EAGAIN:
494 				perror("read");
495 				printf("Error EAGAIN: fd refers to a file "
496 					"other than a socket and has been "
497 					"marked nonblocking (O_NONBLOCK), "
498 					"and the read would block."
499 					"See open(2) for further details on "
500 					"the O_NONBLOCK flag.");
501 				exit(1);
502 			  case EBADF:
503 				perror("read");
504 				printf("Error EBADF: fd is not a valid file "
505 					"descriptor\r\n"
506 					"or is not open for reading.\r\n");
507 				exit(1);
508 			  case EFAULT:
509 				perror("read");
510 				printf("Error EFAULT: buf is outside your "
511 					"accessible adress space.\r\n");
512 				exit(1);
513 			  case EINTR: /* signal */
514 				if ( (signal_number == SIGWINCH)
515 					&& winsize_updated ) {
516 				   printf("window size was updated: "
517 					  "%hu columns, %hu lines\r\n",
518 					   columns, lines);
519 				   if (columns > 99) {
520 				     printf("ATTENTION: Wordstar 4.00 can only"
521 					    " correct display the screen"
522 					    " up to 99 columns!\r\n");
523 				   }
524 				   if (cpm3) {
525 				     PutBYTE(scb + SCB_columns,lreg(columns-1));
526 				     PutBYTE(scb + SCB_lines, lreg(lines-1));
527 				   }
528 				   break;
529 				}
530 				if (signal_number != SIGWINCH) {
531 				    perror("read");
532 				    printf("An another signal as SIGWINCH was "
533 					   "generated!\r\nsignal number is: "
534 					   "%d\r\n",signal_number);
535 				}
536 				if (!winsize_updated) {
537 				    perror("read");
538 				    printf("Error in ioctl call in handler "
539 				 	   "routine.\r\n");
540 				}
541 				exit(1);
542 			  case EINVAL:
543 				perror("read");
544 				printf("Error EINVAL: fd is attached to an "
545 					"object which is unsuitable for "
546 					"reading.\r\n");
547 				exit(1);
548 			  case EIO:
549 				perror("read");
550 				printf("Error EIO: I/O error.\r\n");
551 				exit(1);
552 			  case EISDIR:
553 				perror("read");
554 				printf("Error EISDIR: fd refers to a "
555 					"directory.\r\n");
556 				exit(1);
557 			  default:
558 				perror("read");
559 				exit(1);
560 			 } /* end of switch (errno) */
561 			 break;
562 
563 		case 1: return c; /* if one character -> return the character */
564 
565 		default: printf("count: %d\n\r",count);
566 			 return c;
567 
568 	   } /* end of switch ( read ...) and end of for(;;) */
569 	}
570 	else if (read(fileno(s->fp), &c, 1) == 0)
571             return 0x1a;
572         else
573 	    return c;
574     } /* end of if (s->tty) */
575     if ((ch = getc(s->fp)) == EOF)
576         return 0x1a;
577     else
578         return ch & 0xff;
579 }
580 
581 static void
serout(int chan,char c)582 serout(int chan, char c)
583 {
584     struct sio *s = getsiop(chan);
585 
586     if (s->fp == NULL)
587         return;
588     if (s->tty)
589         write(fileno(s->fp), &c, 1);
590     else
591         fwrite(&c, 1, 1, s->fp);
592 }
593 
594 static WORD
seldsk(int disk)595 seldsk(int disk)
596 {
597     /*  ACHTUNG: Wenn die Translationtable gesetzt ist, muss, diese
598     	    bei jedem select uebertragen werden ! Die Tabelle
599     	    darf in bank0 im banked Bereich stehen (bisher ist noch
600     	    Platz fuer 1 Tabelle im Common ohne das bios_base
601     	    unterhalb von FE00H faellt). In Yaze wird allerdings
602     	    lediglich die Tabelle fuer die IBM-Diskette unterstuetzt
603     	    (siehe mount in monitor.c */
604     /*  NOTE: If the Translationtable is set then it has to be
605     	 transferred with each SELECT.  The table may reside in
606     	 bank 0.  (There is room for one table in common memory
607     	 without the bios_base dropping below FE00H).  Yaze only
608     	 supports the table for the IBM diskette.
609     	 (See mount in monitor.c */
610 
611     if (disk < 0 || disk > 15 || !(mnttab[disk].flags & MNT_ACTIVE))
612         return 0;
613 
614     /*	 Superdos (CP/M 2.2 derivative) uses, like CP/M 3.1 (or ZPM3), the
615         "initial select flag" (bit 0 of register E) to indicate if a disks
616         is selected for the first time (bit0=0). I use this information to
617         force a reread of a disk which is connected to a unix directory */
618 
619     if (!(DE & 0x0001) && (mnttab[disk].flags & MNT_UNIXDIR))
620     {
621         /*	 printf("\r\nSELDSK: BC=%04X DE=%04X (FIRST SELECT of a unix "
622         		"directory)\r\n",BC,DE);
623         */
624         if (!remount(disk))
625         {
626             printf("\rBIOS-SELDSK: mount/remount of the directory failed !"
627                    "\007\r\n"
628                    "             Is the directory deleted or renamed ?"
629                    "\r\n");
630             return 0;
631         }
632     }
633     curdisk = mnttab + disk;
634     return curdisk->dph;
635 }
636 
637 #ifdef BGii_BUG
638 /*  I cannot persuade Backgrounder ii to output the H at the end of a
639     VT-100/xterm cursor-positioning sequence.  This kludges it. */
640 void
BGiiFix(int c)641 BGiiFix(int c)
642 {
643     static const char cc[] = "\033[0;0";
644     static const char *p = cc;
645 
646     if (c == 0)
647         return;
648     if ('0' <= c && c <= '9')
649         return;
650     if (*p == '0')
651         p++;
652     if (*p == c)
653     {
654         p++;
655         return;
656     }
657     if (*p)
658     {
659         p = cc;
660         return;
661     }
662     p = cc;
663     if (c == 'H')
664         return;
665     serout(CHNconout, 'H');
666 }
667 #else
668 #define BGiiFix(x)
669 #endif
670 
671 #define FCACHE_SIZE	4
672 static struct fc
673 {
674     FILE *f;
675     struct mnt *disk;
676     struct fdesc *fd;
677 } fcache[FCACHE_SIZE];
678 
679 
680 /* clear the file cache when disk is unmounted */
681 void
clearfc(struct mnt * dp)682 clearfc(struct mnt *dp)
683 {
684     int i;
685 
686     for (i = 0; i < FCACHE_SIZE; i++)
687     {
688         struct fc *f = fcache + i;
689         if (f->f && f->disk == dp)
690         {
691             fclose(f->f);
692             f->f = NULL;
693         }
694     }
695 }
696 
697 static int
698 #ifdef MULTIO
readsec(int sec_cnt)699 readsec(int sec_cnt)
700 #else
701 readsec(void)
702 #endif
703 {
704     unsigned long offset, blockno;
705     BYTE buf[128];
706 #ifdef MMU
707     FASTREG tmp2;
708 #endif
709 
710     if (curdisk == NULL || !(curdisk->flags & MNT_ACTIVE))
711         return 1;
712 
713     if (cpm3)
714         /*
715         	offset = (track*GetWORD(curdisk->dpb) + sector)*128;
716         */
717 
718         offset = (track * GetWORD(curdisk->dpb)
719                   + (sector << curdisk->psh)) * 128;
720 
721     /* the shift with psh make it posible to use sektorsize >128 */
722     /* IMPORTANT: The disk geometrie must fit the sektors. */
723     else
724         /* offset = (track*GetWORD(curdisk->dph+16) + sector)*128; */
725         offset = (track * GetWORD(curdisk->dpb) + sector) * 128;
726 
727     if ((curdisk->flags & MNT_UNIXDIR) == 0)
728     {
729         /* the 'disk image' case is easy */
730         /*  modified by agl
731             memcpy(ram + dma, curdisk->data + offset, 128);
732         */
733         /*  once more changed by agl
734             memcpy_put_z(dma, curdisk->data + offset, 128);
735         */
736         /* once more modified by agl: dmmu - destination mmu/bank  */
737         /* once more modified with cpm3 and shift with curdisk->psh */
738         if (cpm3)
739         {
740           #ifdef MULTIO
741             memcpy_M_put_z(
742                 dmmu,			     /* use Destination-MMU	*/
743                 dma,			     /* destination DMA-Address	*/
744                 curdisk->data + offset,    /* position of source DATA	*/
745                 (128 << curdisk->psh)	     /* sector size		*/
746                 * sec_cnt	     /* Count of sectors */
747             );
748           #else
749             memcpy_M_put_z(
750                 dmmu,			     /* use Destination-MMU	*/
751                 dma,			     /* destination DMA-Address	*/
752                 curdisk->data + offset,    /* position of source DATA	*/
753                 (128 << curdisk->psh)	     /* sector size		*/
754             );
755           #endif
756         }
757         else
758         {
759             memcpy_M_put_z(
760                 dmmu,				/* use Destination-MMU	*/
761                 dma,				/* DMA-Address		*/
762                 curdisk->data + offset,	/* position of DATA	*/
763                 128				/* sector size		*/
764             );
765         }
766         /*
767         	printf("YAZE: readsec dbnk= %d, DMA=%4X, track=%d, sec=%d\r\n",dbnk,
768         							dma,track,sector);
769         */
770         return 0;
771     }
772 
773     /* handle a 'unix directory' disc */
774 
775     blockno = offset / BLOCK_SIZE;
776 
777     if (blockno < 16)
778     {
779         /* directory access */
780         if (offset / 32 < curdisk->nde)
781             /* memcpy(ram+dma, curdisk->data+offset, 128); */
782             /* memcpy_put_z(dma, curdisk->data+offset, 128); */
783             memcpy_M_put_z(dmmu, dma, curdisk->data + offset, 128);
784         else
785             memset_M_z(dmmu, dma, 0xe5, 128);
786     }
787     else
788     {
789         /* file access */
790         int i;
791         for (i = 0; i < FCACHE_SIZE; i++)
792         {
793             struct fc *f = fcache + i;
794             if ((f->f == NULL) ||
795                     (f->disk == curdisk && blockno >= f->fd->firstblock &&
796                      blockno <= f->fd->lastblock))
797                 break;
798         }
799         if (i == FCACHE_SIZE)
800         {
801             /* cache overflow */
802             fclose(fcache[FCACHE_SIZE - 1].f);
803             memmove(fcache + 1, fcache, (FCACHE_SIZE - 1) * sizeof(struct fc));
804             i = 0;
805             fcache[0].f = NULL;
806         }
807         if (fcache[i].f == NULL)
808         {
809             struct fc *f = fcache + i;
810             struct fdesc *fd = curdisk->fds;
811             int j;
812             f->disk = curdisk;
813             for (j = 0; j < curdisk->nfds; j++, fd++)
814                 if (blockno >= fd->firstblock && blockno <= fd->lastblock)
815                     break;
816             if (j >= curdisk->nfds)
817             {
818                 /* cant happen */
819                 return 1;
820             }
821             if ((f->f = fopen(fd->fullname, "rb")) == NULL)
822             {
823                 perror(fd->fullname);
824                 return 1;
825             }
826             f->fd = fd;
827         }
828         if (i != 0)
829         {
830             /* sort least-recently-used to front */
831             struct fc temp;
832             temp = fcache[i];
833             /* orig of fdc: memmove(fcache+i, fcache, i * sizeof(struct fc));*/
834             /*                             ^-- that's the error !!!	    */
835             /* it moves over the end of fcache and causes a segmentation   */
836             /* fault */
837             memmove(fcache + 1, fcache, i * sizeof(struct fc)); /*changed by agl*/
838             /*             ^-- it must be 1, that works, 20.12.2003 */
839             fcache[0] = temp;
840         }
841         if (fseek(fcache[0].f, offset - fcache[0].fd->firstblock * BLOCK_SIZE,
842                   SEEK_SET) < 0)
843             return 1;
844         /*
845             modified by agl
846         	if ((i = fread(ram+dma, 1, 128, fcache[0].f)) < 128)
847         	    memset_z(dma+i, 0x1a, 128-i); (* EOF *) <-- ACHTUNG !!!
848         */
849 
850         if ((i = fread(buf, 1, 128, fcache[0].f)) < 128)
851         {
852             int l_dma = dma + i;
853             memset_M_z(dmmu, l_dma, 0x1a, 128 - i); /* EOF */
854         }
855         memcpy_M_put_z(dmmu, dma, buf, i);
856     }
857     return 0;
858 }
859 
860 static int
861 #ifdef MULTIO
writesec(int stype,int sec_cnt)862 writesec(int stype, int sec_cnt)	/* sec_cnt is only used by CP/M 3.1 */
863 #else
864 writesec(int stype)
865 #endif
866 {
867 #ifdef MMU
868     FASTREG tmp2;
869 #endif
870 
871     if (curdisk == NULL ||
872             ((curdisk->flags & (MNT_ACTIVE | MNT_RDONLY)) != MNT_ACTIVE))
873         return 1;
874     /*  memcpy(curdisk->data +
875         (track*GetWORD(curdisk->dph+16) + sector)*128, ram + dma, 128);
876     */
877     /*  once more changed by agl
878         memcpy_get_z(curdisk->data + (track*GetWORD(curdisk->dph+16)+sector)*128,
879     	 dma, 128);
880     */
881     if (cpm3)
882     {
883 #ifdef MULTIO
884         memcpy_M_get_z(dmmu,
885                        curdisk->data + (track * GetWORD(curdisk->dpb)
886                                         + (sector << curdisk->psh)) * 128,
887                        dma,
888                        (128 << curdisk->psh) * sec_cnt);
889 #else
890         memcpy_M_get_z(dmmu,
891                        curdisk->data + (track * GetWORD(curdisk->dpb)
892                                         + (sector << curdisk->psh)) * 128,
893                        dma,
894                        (128 << curdisk->psh));
895 #endif
896     }
897     else
898         /**********************
899             memcpy_M_get_z(dmmu,
900         	curdisk->data + (track*GetWORD(curdisk->dph+16) + sector)*128,
901         	dma,
902         	128);
903         ***********************/
904         memcpy_M_get_z(dmmu,
905                        curdisk->data + (track * GetWORD(curdisk->dpb) + sector) * 128,
906                        dma,
907                        128);
908     return 0;
909 }
910 
911 /*  This only works for single-byte sector numbers
912     (but then, so does the CP/M 2.2 CBIOS code).
913 */
914 static WORD
sectrans(WORD sec,WORD table)915 sectrans(WORD sec, WORD table)
916 {
917     /*  ACHTUNG: Bei CP/M 3 darf die Sektortranslation-
918         Table in Bank 0 im Systembereich sein, da vom
919         BDOS aus Bank 0 selektiert wird (siehe Seite 68
920         Bios-Handbuch) !!! */
921     if (table == 0)
922         return sec;
923     /***
924         printf("sectrans: sec: %2d, table %4X, tranlated sec: %2X\r\n",
925     		sec, table, GetBYTE(table+sec) );
926     ***/
927     return GetBYTE(table + sec);
928 }
929 
930 
931 void
setup_cpm3_dph_dpb(int disk)932 setup_cpm3_dph_dpb(int disk)
933 {
934     struct mnt *dp = mnttab + disk;
935     WORD   w, ww;
936     BYTE   version = dp->buf[16]; /* get version indentifier */
937 #ifdef MMU
938     FASTREG tmp2;
939 #endif
940 
941     dp->dph = GetWORD(yct + 2 * disk); /* get pointer to dph from YCT */
942 
943     dp->dpb = GetWORD(dp->dph + DPH_dpb);	/* get DPB of drive */
944 
945     if (dp->xlt)	/* xlt gesetzt (#0) */
946         dp->xlt = t_sssd;
947     else
948         dp->xlt = 0;
949     PutWORD(dp->dph + DPH_xlt, dp->xlt);
950 
951     PutBYTE(dp->dph + DPH_mf, 0xff);	/* set Media Flag in DPH */
952     PutBYTE(scb + SCB_MEDIA, 0xff);	/* set @MEDIA in SCB */
953 
954     w = GetWORD(dp->dpb + DPB_cks);	/* get DPB.cks		*/
955     ww = w & 0x8000;		/* mask MSB		*/
956 
957     if (version == 0 || always128)	/* if diskfile is created by */
958     {
959         /* yaze-1.10/1.06 or flag -1 */
960         dp->buf[32 + 15] = 0;	/* setup PSH and PHM for */
961         dp->buf[32 + 16] = 0;	/* 128 byte sectors	 */
962     }
963 
964     /* if version = 1 nothing is to do and the whole DPB will be copied */
965 
966     memcpy_put_z(dp->dpb, dp->buf + 32, 17);
967     /* copy dpb from buf+32 to dp->dpb */
968     /*  The first 128 Byte are read into buf from
969         the disk-file when a drive(file) is mounted.
970     */
971 
972     if (w & 0x7fff)   /* if CKS is set  */
973     {
974         /* calculate and set CKS */
975         w = GetWORD(dp->dpb + DPB_drm);	/* get DRM (dir entries)     */
976         w = (w >> 2);			/* calculate CKS (DRM/4)     */
977         if (w) ++w;			/* only if # 0 ((DRM/4)+1)   */
978         w |= ww;			/* set MSB if set in DPB.cks */
979         PutWORD(dp->dpb + DPB_cks, w);	/* set CKS		     */
980     }
981 
982     dp->bls = (128 << GetBYTE(dp->dpb + DPB_bsh));  /* calculate BLS */
983     dp->dsm = GetWORD(dp->dpb + DPB_dsm);
984     dp->drm = GetWORD(dp->dpb + DPB_drm);
985 
986     dp->psh = GetBYTE(dp->dpb + DPB_psh); /* get PSH */
987 
988     if (!(dp->flags & MNT_UNIXDIR))    /* only if NOT UNIXDIR */
989     {
990         /* calculate memory requirement */
991         /* (((DSM+1)<<BSH) + OFFS*SPT + 1)*128 */
992         dp->isize = (((GetWORD(dp->dpb + DPB_dsm) + 1) << GetBYTE(dp->dpb + DPB_bsh))
993                      + GetWORD(dp->dpb + DPB_off) * GetWORD(dp->dpb + DPB_spt) + 1)
994                     * 128;
995     }
996 }
997 
998 
999 /*	 date function:
1000 
1001 	dayfaktor calculates the theoretical days since 1.1.0000
1002 	(Gregorian calendar)
1003 
1004 	You'll find on drive M: (Turbo-Modula-2) the source in Modula-2.
1005 	(Look for DAYS.MOD/DEF and BIRTHDAY.MOD)
1006 
1007 	I extracted the formula from a TI-58 pocket calculator!
1008 
1009 	calculate days between two dates: dayfaktor(date2) - dayfaktor(date1)
1010 	weekdays: dayfaktor(date) mod 7 --> 0 Saturday ... 6 Friday
1011 */
1012 
1013 /*  The original date function
1014     unsigned long
1015     dayfaktor( unsigned long day, unsigned long month, unsigned long year)
1016     { unsigned long m3, y3;
1017 
1018     if (month > 2L) {
1019 	m3 = (unsigned long)(0.4 * (float)month + 2.3);
1020 	y3 = year;
1021     } else {
1022 	m3 = 0L;
1023 	y3 = year - 1L;
1024     }
1025 
1026     return( 365L * year + day + 31L*(month-1L) - m3
1027 	    + (unsigned long)(y3 / 4)
1028 	    - (unsigned long)((float)((unsigned long)(year / 100) + 1L) * 0.75)
1029 	  );
1030     }
1031 */
1032 
1033 unsigned long
dayfaktor(unsigned long day,unsigned long month,unsigned long year)1034 dayfaktor(unsigned long day, unsigned long month, unsigned long year)
1035 {
1036     unsigned long m3, y3;
1037 
1038     if (month > 2L)
1039     {
1040         m3 = (unsigned long)(0.4 * (float)month + 2.3);
1041         y3 = year;
1042     }
1043     else
1044     {
1045         m3 = 0L;
1046         y3 = year - 1L;
1047     }
1048 
1049     return (365L * year + day + 31L * (month - 1L) - m3
1050             + (y3 >> 2)
1051             - (unsigned long)(((unsigned long)(year / 100L) + 1L) * 3L / 4L)
1052            );
1053 }
1054 
1055 static char cpmCommandLine[CPM_COMMAND_LINE_LENGTH];
1056 
getCPMCommandLine(void)1057 static void getCPMCommandLine(void)
1058 {
1059     int
1060     s, d,
1061     len = GetBYTE(0x80) & 0x7f; /* Get length of filepath at 0080H */
1062     /* Drop any leading whitespace */
1063     for (s = d = 0; s < len && isspace(GetBYTE(0x81 + s)); ++s);
1064     while (s < len)
1065     {
1066         cpmCommandLine[d++] = GetBYTE(0x81 + s);
1067         ++s;
1068     }
1069     cpmCommandLine[d] = 0; /* make C string */
1070     /* printf("ufilepath:%s:\r\n",ufilepath); */
1071 }
1072 
1073 
1074 static BYTE savcpm[CPM_LENGTH];		/* saved copy of cpm */
1075 
1076 #ifdef MMU
1077 #define CCP3_LENGTH 0xC80
1078 #define CCP3_BASE   0x100
1079 #define SYSBNK      0		/* MMU of SYS Bank */
1080 #define TPABNK      1		/* MMU of TPA Bank */
1081 #endif
1082 
1083 extern BYTE conin();
1084 extern struct _ci ci;
1085 
bios(int func)1086 void bios(int func)
1087 {
1088 
1089     static int cold_boot = 1;
1090 
1091     /* for read/write files from/to host system */
1092     static FILE *uf; /* unix file */
1093     static int d, i;
1094     static glob_t globS;
1095     static int globPosNameList       = 0;
1096     static int globPosName           = 0;
1097     static int globError             = 0;
1098 
1099 #ifdef MMU
1100     static pagetab_struct *tpammu = &MMUtable[TPABNK]; /* TPA MMU-pagetable */
1101     static struct mnt *dp;
1102     static int xmove_op = 0;
1103     static WORD h_dtbl;
1104     static int h_mmut;
1105     static struct tm *t;
1106     static time_t now;
1107     static FASTREG tmp2;
1108 #endif
1109 #ifdef MULTIO
1110     static int mcnt = 0;		/* must be 0, very important !!! */
1111     static int mio_rw_done = 0;	/* boolean */
1112 #endif
1113 
1114 #ifdef UCSD
1115     static int ucsdmode = 0;	/* must be 0 */
1116 #endif
1117     BYTE ch;
1118 
1119     ++bioscount;
1120     switch (func)
1121     {
1122     case 0:			/* cold boot (only used under CP/M 2.2) */
1123         if (cold_boot)
1124         {
1125             cold_boot = 0;	/* only one time! */
1126             memcpy_get_z(savcpm, ccp_base, CPM_LENGTH);
1127             /* PutBYTE(0x0F, 0xFF);	\* only for Test */
1128 #ifdef DEBUG
1129             puts("DEBUG: Cold Boot\r");
1130 #endif
1131         }
1132     /*
1133     	else
1134     	    printf("Cold Boot (jmp bios_base (%4X)) --> make a warm "
1135     					"boot!\n\r",bios_base);
1136     */
1137     case 1:			/* warm boot */
1138 wboot:
1139         memcpy_put_z(ccp_base, savcpm, CPM_LENGTH);
1140         dma = 0x80;
1141         PutBYTE(0, 0xc3);		/* ram[0] = 0xc3; */
1142         PutBYTE(1, 0x03);		/* ram[1] = 0x03; */
1143         PutBYTE(2, hreg(bios_base));	/* ram[2] = hreg(bios_base);  */
1144         PutBYTE(5, 0xc3);		/* ram[5] = 0xc3;  */
1145         PutBYTE(6, 0x06);		/* ram[6] = 0x06; */
1146         PutBYTE(7, hreg(bdos_base));	/* ram[7] = hreg(bdos_base); */
1147         Setlreg(BC, GetBYTE(4));	/* Setlreg(BC, ram[4]); */
1148         pc = ccp_base;
1149 #ifdef DEBUG
1150         puts("DEBUG: Warm Boot\r");
1151 #endif
1152 #ifdef UCSD
1153         ucsdmode = 0; /* switch of UCSD-Mode */
1154 #endif
1155         return;
1156 
1157     case 2:			/* console status */
1158         Sethreg(AF, ci.size || constat() ? 0xff : 0x00);
1159         break;
1160 
1161     case 3:			/* console input */
1162         ch = conin();
1163         Sethreg(AF, ch);
1164         break;
1165 
1166     case 4:			/* console output */
1167         BGiiFix(lreg(BC));
1168         /* serout(CHNconout, (lreg(BC) & 0x7f) ); */
1169         serout(CHNconout, lreg(BC));
1170         break;
1171 
1172     case 5:			/* list output */
1173         serout(CHNlst, lreg(BC));
1174         break;
1175 
1176     case 6:			/* punch output */
1177         if (cpm3)
1178             serout(CHNaux, lreg(BC));	/* CP/M 3: AuxOut */
1179         else
1180             serout(CHNpun, lreg(BC));	/* CP/M 2.2 Punch */
1181         break;
1182 
1183     case 7:			/* tape reader input */
1184         if (cpm3)
1185             Sethreg(AF, serin(CHNaux));	/* CP/M 3 */
1186         else
1187             Sethreg(AF, serin(CHNrdr));	/* CP/M 2.2 */
1188         break;
1189 
1190     case 8:			/* home disk */
1191         track = 0;
1192         break;
1193 
1194     case 9:			/* select disk */
1195         /* printf("\n\rSELDSK: BC=%04X DE=%04X\n\r",BC,DE); */
1196         HL = seldsk(lreg(BC));
1197         break;
1198 
1199     case 10:			/* set track */
1200 #ifndef UCSD
1201         track = BC;
1202 #else
1203         if (!ucsdmode)
1204             track = BC;
1205         else
1206             track = lreg(BC);
1207 #endif
1208         break;
1209     case 11:			/* set sector */
1210 #ifndef UCSD
1211         sector = BC;
1212 #else
1213         if (!ucsdmode)
1214             sector = BC;
1215         else
1216         {
1217             sector = lreg(BC);
1218             --sector    /* decrement one because the first sector for the
1219 			   UCSD P-System is sector one and not zero, like the
1220 			   definitions of the IBM 8" disk */
1221         }
1222 #endif
1223         break;
1224     case 12:			/* set dma */
1225         dma = BC;
1226         break;
1227     case 13:			/* read sector */
1228         /* printf("YAZE: READ into bank %d\r\n",dbnk); */
1229 #ifdef MULTIO
1230         if (mcnt)
1231         {
1232             if (mio_rw_done)
1233             {
1234                 --mcnt;
1235               #ifdef RWDEBUG
1236                 printf(".%d",mcnt);
1237                 fflush(stdout);  /***/
1238               #endif
1239                 Sethreg(AF, 0);	/* OK for read */
1240                 break;
1241             }
1242             else
1243             {
1244                 Sethreg(AF, readsec(mcnt--));
1245               #ifdef RWDEBUG
1246                 printf("r");
1247                 fflush(stdout); /***/
1248               #endif
1249                 mio_rw_done = 1;
1250                 break;
1251             }
1252         }
1253         else
1254         {
1255             Sethreg(AF, readsec(1));
1256 
1257             #ifdef RWDEBUG
1258             printf("R");
1259             fflush(stdout); /***/
1260             #endif
1261         }
1262 #else /* of MULTIO */
1263         Sethreg(AF, readsec());
1264         #ifdef RWDEBUG
1265         printf("R");
1266         fflush(stdout); /***/
1267         #endif
1268 #endif
1269         break;
1270 
1271     case 14:			/* write sector */
1272 #ifdef MULTIO
1273         if (mcnt)
1274         {
1275             if (mio_rw_done)
1276             {
1277                 mcnt--;
1278 #ifdef RWDEBUG
1279                 printf(":");
1280                 fflush(stdout);  /***/
1281 #endif
1282                 Sethreg(AF, 0);	/* OK for write */
1283                 break;
1284             }
1285             else
1286             {
1287                 Sethreg(AF, writesec(lreg(BC), mcnt--));
1288 #ifdef RWDEBUG
1289                 printf("w");
1290                 fflush(stdout); /***/
1291 #endif
1292                 mio_rw_done = 1;
1293                 break;
1294             }
1295         }
1296         else
1297         {
1298             Sethreg(AF, writesec(lreg(BC), 1));
1299 #ifdef RWDEBUG
1300             printf("W");
1301             fflush(stdout); /***/
1302 #endif
1303         }
1304 #else
1305         Sethreg(AF, writesec(lreg(BC)));
1306 #ifdef RWDEBUG
1307         printf("W");
1308         fflush(stdout); /***/
1309 #endif
1310 #endif
1311         break;
1312 
1313     case 15:			/* list status */
1314         Sethreg(AF, lststat() ? 0xff : 0x00);
1315         break;
1316     case 16:			/* translate sector */
1317         HL = sectrans(BC, DE);
1318         break;
1319 
1320 
1321 #ifdef MMU
1322 
1323     /* CP/M 3.1 functions: */
1324 
1325     case 17:			/* console output status */
1326         Sethreg(AF, 0xff);	/* always ready */
1327         break;
1328     case 18:			/* aux input status */
1329         Sethreg(AF, auxistat() ? 0xff : 0x00);
1330         /* Sethreg(AF, 0xff);	// always ready */
1331         break;
1332     case 19:			/* aux output status */
1333         Sethreg(AF, auxostat() ? 0xff : 0x00);
1334         /* Sethreg(AF, 0xff);	// always ready */
1335         break;
1336     case 20:			/* device table */
1337 	printf("______________________________________________________");
1338 	printf("_____________________\r\n\n");
1339 	printf("You can attach devices only in the monitor of YAZE-AG!\r\n");
1340 	printf("You go into the monitor with the SYS.COM utility without ");
1341 	printf("any parameter!\r\n");
1342 	printf("Give there the command \"help attach\"!\r\n\n");
1343 	printf("Example 1: \"attach aux /dev/ttyUSB0\" if you want to connect");
1344 	printf(" the AUX: device\r\n");
1345 	printf("	   of CP/M 3 to a serial line which is connected to ");
1346 	printf("an USB port.\r\n\n");
1347 	printf("Example 2: \"attach lpt test.lpt\" if you want to connect the");
1348 	printf(" LPT: \r\n	   printer device to a UNIX/Linux file.\r\n");
1349 	printf("	   Any printer output goes into");
1350 	printf(" the file \"test.lpt\".\r\n\n");
1351 	printf("You go back to CP/M with the monitor command \"go\".\r\n");
1352 	printf("______________________________________________________");
1353 	printf("_____________________\r\n\n");
1354 	printf("CP/M 3 reports:\r\n");
1355 	HL = 0x0000;
1356 	break;			/* The Instruction there must be a LD HL,@dtbl
1357 				   and a RET. Have a look at Page 53 of the
1358 				   System Guide. Function 22: DEVTBL.
1359 				   HL = 0 If you don't want to use DEVTBL. */
1360 
1361     case 21:			/* Initialize Character I/O Device */
1362 	break;
1363 
1364     case 22:			/* Return address of Disk Drive Table */
1365         /* defined in BIOSKRNL.Z80 */  /* (HL = *(yct-2)); */
1366         /* first entry of the YCT */
1367         break;			/* In the implementation of the bnkbios.z80 the
1368 				   address of the @dtbl will be given directly
1369 				   back there. This is nessessary because
1370 				   GENCPM.COM uses this table for CP/M 3 -
1371 				   generation */
1372     case 23:			/* Multio */
1373         /* Reg C is the multisector count */
1374 #if MULTIO
1375         if (curdisk->flags & MNT_UNIXDIR)
1376             break;		/* Wenn eine Unix Directory gemountet ist */
1377         if (curdisk->xlt)	/* is set translation table ? */
1378             break;		/* yes --> go back (no multi i/o) */
1379         mcnt = lreg(BC);
1380         mio_rw_done = 0;	/* false: not yet read */
1381 #endif
1382 
1383 #ifdef RWDEBUG
1384         printf("YAZE: Multi=%d\r\n", lreg(BC));
1385 #endif
1386 
1387         break;
1388     case 24:			/* Flush */
1389         Sethreg(AF, 0x00);	/* A=00 if no error occurred */
1390         /* A=01 if physical error occurred */
1391         /* A=02 if disk is Read-Only */
1392         break;
1393     case 25:			/* MOVE */
1394         /* ------------------implemented in the bioskrnl.z80 (uses LDIR) */
1395         /* ENTRY:   HL=Destination address, DE=Source address, BC=Count */
1396         /* RETURN: HL and DE must point to next bytes following move operation*/
1397 
1398         /*
1399         	if (xmove_op == 0) {
1400         		printf("YAZE: Move BC(Count)=%4X HL(Dest)=%4X DE(Source)=%4X"
1401         			" Bank %d %d %d\r\n",BC,HL,DE,mmutab,bnkdest,bnksrc);
1402         	} else {
1403         		printf("YAZE: IB-Move BC(Count)=%4X HL(Dest)=%4X DE(Source)=%4X"
1404         		" Cbnk %d DBnk %d SBnk %d\r\n",BC,HL,DE,mmutab,bnkdest,bnksrc);
1405         	}
1406         */
1407 
1408         while (BC--)
1409         {
1410             FASTREG byte = MRAM_pp(mmuget, DE);
1411 
1412             MRAM_pp(mmuput, HL) = byte;
1413         }
1414 
1415         xmove_op = 0;	/* xmove operation abgeschlossen, fuer den Fall es
1416 			   wurde eine iniziiert. MOVE wird immer direkt nach
1417 			   xmove aufgerufen. See Page 66 System Guide. */
1418         break;
1419 
1420     case 26:			/* Get and Set Time */
1421 
1422 #define SCB_DATE (0x58)	/* position of @DATE in SCB        */
1423 #define SCB_HOUR (0x5A)	/* position of @HOUR in SCB (BCD) */
1424 #define SCB_MIN  (0x5B)	/* position of @MIN in SCB (BCD) */
1425 #define SCB_SEC  (0x5C)	/* position of @SEC in SCB (BCD)*/
1426 
1427         time(&now);
1428         t = localtime(&now);
1429         PutBYTE(scb + SCB_SEC, (((t->tm_sec / 10) << 4) | (t->tm_sec % 10)));
1430         PutBYTE(scb + SCB_MIN, (((t->tm_min / 10) << 4) | (t->tm_min % 10)));
1431         PutBYTE(scb + SCB_HOUR, (((t->tm_hour / 10) << 4) | (t->tm_hour % 10)));
1432         /*  the old one
1433         	{ register int i,days;
1434         	  for (days=0,i=1978; i < (1900 + t->tm_year); i++)
1435         	  {
1436         		days += 365;
1437         		if (i % 4 == 0) days++;
1438         	  }
1439         	  days += t->tm_yday + 1;
1440         	  PutWORD(scb + SCB_DATE, days);
1441         	}
1442         */
1443         /*	 {
1444         	  register int days;
1445         	  days = dayfaktor( t->tm_mday, (t->tm_mon + 1), (1900 + t->tm_year) )
1446         		 - DayFaktor_CPMSTART;
1447         	  PutWORD(scb + SCB_DATE, days);
1448         	}
1449         */
1450         PutWORD(scb + SCB_DATE, (WORD)(dayfaktor(t->tm_mday,
1451                                        (t->tm_mon + 1),
1452                                        (1900 + t->tm_year)
1453                                                 ) - DayFaktor_CPMSTART)
1454                );
1455         break;
1456 
1457     case 27:			/* SELMEM: Select Memory Bank */
1458         /* see bioskrnl.z80 and func 0xF0 */
1459         if (xmove_op == 0)
1460         {
1461             mmuget = mmuput = ChooseMMUtab(bnkdest = bnksrc = cbnk = hreg(AF));
1462             /* printf("YAZE: SELMEM: bnkdest = bnksrc = %d\r\n",cbnk); */
1463         }
1464         else
1465         {
1466             ChooseMMUtab(cbnk = hreg(AF));
1467             /* printf("YAZE: SELMEM: cbnk %d (xmove_op!)\r\n",cbnk); */
1468         }
1469         break;
1470 
1471     case 28:			/* SETBNK: Specify bank for DISK DMA operation*/
1472         dmmu = &MMUtable[dbnk = hreg(AF)];
1473         /* printf("YAZE: SETBNK A=%d\r\n",dbnk); */
1474         break;
1475     case 29:			 /* XMOVE: Set banks for following MOVE */
1476         /* B=destination bank, C=source bank   */
1477         mmuput = &MMUtable[ bnkdest = hreg(BC) ];
1478         mmuget = &MMUtable[ bnksrc  = lreg(BC) ];
1479         xmove_op = 1;
1480         /* printf("YAZE: XMOVE B=%d C=%d\n\r",bnkdest,bnksrc); */
1481         break;
1482     case 30:			/* USERF */
1483         break;
1484     case 31:			/* Reserv1  Reseved for Future Use */
1485         break;
1486     case 32:			/* RESERV2 */
1487         break;
1488 
1489 #endif
1490 
1491     /* ------------------------------------------------------------------ */
1492     /* special functions for comunication between cp/m 3 and Unix: ------ */
1493 
1494     case 0xA0:			/* Version of YAZE-AG */
1495         HL = 256 * MAINVersion + SUBVersion;
1496         /* printf("HL = %04X \r\n",HL); */
1497         break;
1498 
1499     case 0xA1:			/* HostOSPathSeparator */
1500         HL = '/';   /* slash in UNIX and also in Windows (with Cygwin) */
1501         break;
1502 
1503     case 0xA8:		/* Report/load keyboard translation */
1504 	HL = 0;
1505         getCPMCommandLine();
1506         if (strcmp(cpmCommandLine, "-") == 0)
1507             ktt_load("-");
1508         else if (strlen(cpmCommandLine))
1509         {
1510             struct stat
1511                     fdata;
1512             if (stat(cpmCommandLine, &fdata))
1513         {
1514                 char
1515                 *original = newstr(cpmCommandLine);
1516                 strcat(cpmCommandLine, ".ktt");
1517                 if (stat(cpmCommandLine, &fdata))
1518                 {
1519                     HL = 1;
1520                     perror(original);
1521                     putchar('\r');
1522                     fflush(stdout);
1523                     perror(cpmCommandLine);
1524                 }
1525                 free(original);
1526             }
1527             if (HL == 0)
1528                 ktt_load(cpmCommandLine);
1529         }
1530         printf("\r\nK-T file: %s\r\nElements: %d\r\n",
1531                ktt_name() ? ktt_name() : "<none>",
1532                ktt_elements());
1533         fflush(stdout);
1534         break;
1535 
1536     /* ------------------------------------------------------------------ */
1537     /* function for init the windows size handler ----------------------- */
1538 
1539     case 0xB0:
1540 	winsize_updated =  false; /* reinstall the win size haendler */
1541 	/* printf("winsize_updated: Set to false.\r\n"); */
1542 	break;
1543 
1544     /* ------------------------------------------------------------------ */
1545     /* special functions for comunication between cp/m 3 and Unix: ------ */
1546     /* write CP/M files to a Unix directory ----------------------------- */
1547 
1548     case 0xD0:			/* open file (with path) write */
1549         /* printf("openUFileWrite\r\n"); fflush(stdout); */
1550         HL = 0; /* default no error */
1551         getCPMCommandLine();
1552         if ((uf = fopen(cpmCommandLine, "w")) == NULL)
1553         {
1554             perror(cpmCommandLine);
1555             HL = 1;
1556         }
1557         d = 0;
1558         break;
1559 
1560     case 0xD1:			/* sendbyte (write)*/
1561         /* putchar('.'); fflush(stdout); */
1562         putc(lreg(DE), uf);
1563         d++;
1564         break;
1565 
1566     case 0xD2:			/* close unix file */
1567         printf("closeFile, transfered bytes: %d\r\n", d);
1568         fflush(stdout);
1569         HL = 0;
1570         if (ferror(uf) || fclose(uf) != 0)
1571         {
1572             perror(cpmCommandLine);
1573             HL = 1;
1574         }
1575         break;
1576 
1577     case 0xD3:			/* open file (with path) read */
1578         /* printf("openUFileRead\r\n"); fflush(stdout); */
1579         HL = 0; /* default no error */
1580         getCPMCommandLine();
1581         if ((uf = fopen(cpmCommandLine, "r")) == NULL)
1582         {
1583             perror(cpmCommandLine);
1584             HL = 1;
1585         }
1586         /* printf("\rrufilepath:%s:\r\n",cpmCommandLine); fflush(stdout); */
1587         d = 0;
1588         break;
1589 
1590     case 0xD4:		/* getc_and_status */
1591         i = getc(uf);
1592         if (i == EOF)
1593             HL = 0x011A;
1594         else
1595         {
1596             Setlreg(HL, (BYTE) i);
1597             d++;
1598         }
1599         break;
1600 
1601     case 0xD5:		/* globHostFilenames */
1602         HL = 0; /* default no error */
1603         globPosNameList = globPosName = 0;
1604         getCPMCommandLine();
1605         printf("Host: search pattern \"%s\"\r\n", cpmCommandLine);
1606         fflush(stdout);
1607         globError = glob(cpmCommandLine, GLOB_ERR, NULL, &globS);
1608         if (globError)
1609         {
1610             switch (globError)
1611             {
1612             case GLOB_NOSPACE:
1613                 printf("Host: no memory space\r\n");
1614                 break;
1615             case GLOB_ABORTED:
1616                 printf("Host: read error\r\n");
1617                 break;
1618             case GLOB_NOMATCH:
1619                 printf("Host: no matches\r\n");
1620                 break;
1621             }
1622             fflush(stdout);
1623             globfree(&globS);
1624             HL = 1;  /* error or no matches */
1625         }
1626         break;
1627 
1628     case 0xD6:		/* getHostFilename */
1629         if (globPosNameList < globS.gl_pathc)
1630         {
1631             if (!(i = globS.gl_pathv[globPosNameList][globPosName++]))
1632             {
1633                 globPosNameList++;
1634                 globPosName = 0;
1635             }
1636         }
1637         else
1638         {
1639             globfree(&globS);
1640             i = 0;
1641         }
1642         HL = i;
1643         break;
1644 
1645 #ifdef MMU
1646 
1647     /* ------------------------------------------------------------------*/
1648     /* special functions for comunication between cp/m 3 and yaze: ------*/
1649 
1650 
1651     case 0xE0:   /* 224 = INIT CP/M 3.1 */
1652 
1653         cpm3 = 1;			/* NOW CP/M 3.1 is running ! */
1654 
1655         yct = HL;			/* Z80-Pointer to the @YCT, usage */
1656         /* with GetWORD/GetBYTE */
1657         dtbl = GetWORD(yct - 2);	     /* address of @dtbl     */
1658         scb = GetWORD(yct - 4);	    /* address of SCB       */
1659         bios_base = GetWORD(yct - 6); /* address of the BIOS  */
1660         t_sssd = GetWORD(yct - 8);  /* address of translation table SSSD */
1661         maxdsm = GetWORD(yct - 10); /* maximal DSM */
1662         maxdrm = GetWORD(yct - 12); /* maximal DRM */
1663 
1664         bdos_base = GetWORD(scb + SCB_MXTPA);
1665         ccp_base = CCP3_BASE;
1666 
1667         /*  printf("CP/M 3 init: yct=%X, PC=%X, cbnk=%d, SP=%X\n",
1668         						yct, pc, cbnk, sp);
1669         */
1670 
1671 #ifdef SHOWDRV
1672         printf("\nYAZE: 0xE0 - function:\n\n\rHL = %X (YCT)\n\r", HL);
1673         /* printf("@dtbl from YCT : %X\n\r", (*(yct-2) + (*(yct-1) << 8)) ); */
1674         printf("@dtbl from YCT : %X\n\r", dtbl);
1675         printf("@bnkbf (^BC) %X\r\n", GetWORD(BC));
1676         printf("SCB from DE : %X\n\r", DE);
1677         printf("SCB from yct : %X\n\r", scb);
1678         printf("dph0 from YCT : %X\n\n\r", GetWORD(yct));
1679         printf("bios_base : %X\r\n", bios_base);
1680         printf("bdos_base : %X\r\n", bdos_base);
1681         printf("ccp_base  : %X\r\n\n", ccp_base);
1682         printf("---------------------------------\n\r");
1683 #else
1684         printf("\r\n DRIVES: ");
1685 #endif
1686         for (d = 0; d < 16; d++)
1687         {
1688             dp = mnttab + d;
1689             h_dtbl = dtbl + (2 * d);
1690             if (dp->flags & MNT_ACTIVE)
1691             {
1692                 setup_cpm3_dph_dpb(d);
1693                 PutWORD(h_dtbl, dp->dph);/* put pointer to dph in @dtbl */
1694                 /* now the drive is for CP/M 3 present*/
1695             }
1696             else
1697             {
1698                 PutWORD(h_dtbl, 0); /* delete pointer in @dtbl */
1699             }
1700 
1701 #ifdef SHOWDRV
1702             showdisk(d, 1);
1703 #else
1704             if (dp->flags & MNT_ACTIVE)
1705                 printf(dp->flags & MNT_UNIXDIR ? " %c/" : " %c", ('A' + d));
1706             else
1707                 printf(" .");
1708 #endif
1709         }
1710 
1711 #ifdef SHOWDRV
1712         printf("---------------------------------\r\n");
1713 #else
1714         printf("\r\n"); /* to print DRIVES*/
1715 #endif
1716 
1717         break;
1718 
1719     case 0xE1:   /*225*/  /* save CP/M 3 CCP */
1720         memcpy_M_get_z(tpammu, savcpm, CCP3_BASE, CCP3_LENGTH);
1721         break;
1722 
1723     case 0xE2:   /*226*/  /* put CP/M 3 CCP into TPA (bank 1) and start CCP */
1724         /* selected Bank must be TPA */
1725 
1726         mmuget = mmuput = ChooseMMUtab(TPABNK);
1727 
1728         memcpy_put_z(CCP3_BASE, savcpm, CCP3_LENGTH);
1729 
1730         PutBYTE(0, 0xc3);		/* ram[0] = 0xc3; */
1731         PutBYTE(1, 0x03);		/* ram[1] = 0x03; */
1732         PutBYTE(2, hreg(bios_base));	/* ram[2] = hreg(bios_base);  */
1733         PutBYTE(3, 0x95);		/* standart I/O-Byte */
1734         PutBYTE(5, 0xc3);		/* ram[5] = 0xc3;   */
1735         /* PutBYTE(6, 0x06);		\* ram[6] = 0x06;  */
1736         /* PutBYTE(7, hreg(bdos_base));	\* ram[7] = hreg(bdos_base); */
1737 
1738         bdos_base = GetWORD(scb + SCB_MXTPA); /* IMPORTANT !!! */
1739         /* ^^^^^^ changed at any warm boot !!! */
1740 
1741         PutWORD(6, bdos_base);		/* From SCB_MXTPA !!! */
1742 
1743         /*	 printf("\n\rYAZE: bios_base: %X, bdos_base: %X\r\n",
1744         						bios_base, bdos_base);
1745         ***/
1746         pc = CCP3_BASE;
1747         return; /* no break! */
1748 
1749     /* -------------------------------------------------------------------*/
1750     /* functions for the MMU: -------------------------------------------*/
1751 
1752     case 0xF0:			/* (240) MMU-Function: select MMU-Table */
1753         if ((h_mmut = hreg(AF)) < MMUTABLES)
1754         {
1755             /* hreg(par) made an (par & 0xff) --> only 0..255 possible */
1756             ChooseMMUtab(h_mmut);
1757         }
1758         else
1759         {
1760             Sethreg(AF, 0xff);	/* A=FFh <-- Error: Nr for MMU */
1761 #ifdef MMUTEST
1762             puts("\r\nYAZE: Select MMU-table --> Number for selection "
1763                  "is out of range!");
1764 #endif
1765         }
1766         break;
1767     case 0xF1:			/* (241) MMU: load MMU-table */
1768         /*  structure for load MMUtable in the z80-mem (HL is the pointer):
1769         	first-Byte: adr of MMUtable
1770         	2..16:	    16 bytes which will be translated to pointers
1771         		    and put in to the MMUTab.
1772             return-codes (reg A & HL):
1773         	A = 0xFE: PagePointer is wrong (out of Memory). HL points
1774         		  to the wrong PP.
1775         	A = 0xFF: MMUtable (first Byte) addresses an MMUtable which does
1776         		  not exist.
1777         */
1778         loadMMU();
1779         break;
1780     case 0xF2:			/* (242) MMU: print MMU  */
1781         printMMU();
1782         break;
1783     case 0xF3:			/* (243) MMU: give back	the No. of sel. MMU*/
1784         Sethreg(AF, mmutab);	/* A = selected MMU		*/
1785         break;			/* ... */
1786     case 0xF4:			/* (244) MMU: give back	the MMU-status */
1787         Sethreg(AF, mmutab);	/* A = selected MMU		*/
1788         BC = MMUTABLES * 256	/* B = MMUTABLES		*/
1789              + MMUPAGEPOINTERS;	/* C = MMUPAGEPOINTER		*/
1790         DE = RAMPAGES;		/* DE = No. of pages of the RAM	*/
1791         HL = MEMSIZE;		/* HL = size of memory		*/
1792         break;
1793     case 0xF5:			/* (245) MMU-Function: sel. MMU-Table + WBOOT */
1794         if ((h_mmut = hreg(AF)) < MMUTABLES)
1795         {
1796             /* hreg(par) made an (par & 0xff) --> only 0..255 possible */
1797             WORD o_iobyte = GetWORD(3);	  /* save old iobyte and drive */
1798             ChooseMMUtab(h_mmut);		 /* choose the MMU table      */
1799             dmmu = mmu;			/* set dmmu		     */
1800             PutWORD(3, o_iobyte);		/* set old iobyte and drive */
1801             printMMU();
1802             /*  puts("wboot after mmutsel ...\r"); */
1803             goto wboot;
1804         }
1805         else
1806         {
1807             Sethreg(AF, 0xff);	/* A=FFh <-- Error: Nr for MMU */
1808 #ifdef MMUTEST
1809             puts("\r\nYAZE: Select MMU-table --> Number for selection "
1810                  "is out of range!");
1811 #endif
1812         }
1813         break;
1814 #else /* of MMU */
1815     case 17:	/* cp/m 3 functions */
1816     case 18:
1817     case 19:
1818     case 20:
1819     case 21:
1820     case 22:
1821     case 23:
1822     case 24:
1823     case 25:
1824     case 26:
1825     case 27:
1826     case 28:
1827     case 29:
1828     case 30:
1829     case 31:
1830     case 32:
1831     case 0xE0: /* special function for comunication between cp/m 3.1 and yaze */
1832     case 0xE1:
1833     case 0xE2:
1834     case 0xF0: /* functions for the MMU */
1835     case 0xF1:
1836     case 0xF2:
1837     case 0xF3:
1838     case 0xF4:
1839     case 0xF5:
1840         fprintf(stderr, "\r\n\nYAZE: Invalid bios function: %d (0x%X)\r\n\n",
1841                 func, func);
1842         fprintf(stderr, "YAZE: You are trying use MMU or CP/M 3.1 functions.\r\n");
1843         fprintf(stderr, "YAZE: Please first compile YAZE-AG with the flag "
1844                 "-DMMU to use it.\r\n\n");
1845         /* goto wboot; */
1846         exit(0);
1847 
1848 #endif /* of MMU */
1849 
1850 #ifdef UCSD
1851     case 250:   /* 0xFA: switch on ucsd-modus */
1852         ucsdmode = 1;
1853         break;
1854 #endif
1855     /* case 253:			\* return from recursive call */
1856     /* return; */
1857     case 254:			/* meta-level command */
1858         ++pc; /*<-- mu\DF vor GetByte geschehen, da bei -DMMU & GetBYTE(++pc) das
1859 		    GetBYTE-Macro aufgrund des zweimaligen Zugriffs auf "++pc"
1860 		    den pc zweimal incementieren */
1861         if (GetBYTE(pc) == 0)
1862         {
1863 #ifdef MMU
1864             if (cpm3) mmuget = mmuput = ChooseMMUtab(SYSBNK);
1865 #endif
1866             monitor(0);
1867 #ifdef MMU
1868             if (cpm3) mmuget = mmuput = ChooseMMUtab(TPABNK);
1869 #endif
1870         }
1871         else
1872         {
1873             /* need a copy because docmd() scratches its argument */
1874             char *sav = newstr((char *) ram + pc);
1875 #ifdef MMU
1876             if (cpm3) mmuget = mmuput = ChooseMMUtab(SYSBNK);
1877 #endif
1878             ttycook();
1879             (void) docmd(sav);
1880             ttyraw();
1881             free(sav);
1882 #ifdef MMU
1883             if (cpm3) mmuget = mmuput = ChooseMMUtab(TPABNK);
1884 #endif
1885             pc += strlen((char *) ram + pc);
1886         } /* endif */
1887         break;
1888     case 255:			/* quit */
1889         exit(0);
1890     default:
1891         fprintf(stderr, "YAZE: Invalid bios function: %d (0x%X)\r\n", func, func);
1892         /* goto wboot; */
1893         exit(0);
1894     }
1895     pc++;
1896 }
1897