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