1 /* System monitor.
2 Copyright (C) 1995 Frank D. Cringle.
3 Modifications for CP/M 3.1 Copyright (C) 2000/2004 by Andreas Gerlich (agl)
4
5 This file is part of yaze-ag - yet another Z80 emulator by ag.
6
7 Yaze-ag is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2 of the License, or (at your
10 option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #ifndef CYGWIN
25 #include <libgen.h>
26 #endif
27 #include <strings.h>
28 #include <time.h>
29 #include <limits.h>
30 #include <fcntl.h>
31 #include <termios.h>
32 #include <ctype.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <sys/times.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <dirent.h>
39 #include <sys/mman.h>
40
41 #include "mem_mmu.h"
42 #include "simz80.h"
43 #include "yaze.h"
44 #include "ybios.h"
45
46 /* TTY management */
47
48 struct termios cookedtio, rawtio;
49 int ttyflags;
50 int interrupt;
51
52 void
ttyraw(void)53 ttyraw(void)
54 {
55 if ((ttyflags & (ISATTY | ISRAW)) == ISATTY)
56 {
57 tcsetattr(fileno(stdin), TCSAFLUSH, &rawtio);
58 ttyflags |= ISRAW;
59 }
60 }
61
62 void
ttycook(void)63 ttycook(void)
64 {
65 if (ttyflags & ISRAW)
66 {
67 tcsetattr(fileno(stdin), TCSAFLUSH, &cookedtio);
68 putc('\n', stdout);
69 ttyflags &= ~ISRAW;
70 }
71 }
72
73
74 /* memory management routines for disk descriptors
75 (we need to allocate chunks of cp/m ram for these) */
76
77 /* inefficient but robust bit-wise algorithm (we're not doing this all day) */
78 #define bytefree(x) (!(global_alv[(x-bios_top) >> 3] & \
79 (0x80 >> ((x-bios_top) & 7))))
80
81 static WORD
cpmalloc(WORD len)82 cpmalloc(WORD len)
83 {
84 WORD p = bios_top;
85
86 while (p < dptable - len)
87 if (!bytefree(p))
88 p++;
89 else
90 {
91 int i;
92 for (i = 1; i < len; i++)
93 if (!bytefree(p + i))
94 break;
95 if (i == len)
96 {
97 WORD p1 = p - bios_top;
98 while (len--)
99 {
100 global_alv[p1 >> 3] |= (0x80 >> (p1 & 7));
101 p1++;
102 }
103 return p;
104 }
105 p += i;
106 }
107 return 0;
108 }
109
110
111 static void
cpmfree(WORD adr,WORD len)112 cpmfree(WORD adr, WORD len)
113 {
114 WORD p = adr - bios_top;
115
116 while (len--)
117 {
118 global_alv[p >> 3] &= ~(0x80 >> (p & 7));
119 p++;
120 }
121 }
122
123
124 /* Disk management */
125
126 /* There are two kinds of simulated disk:
127 a unix file containing an image of a cp/m disk and
128 a unix directory which is made to look like a cp/m disk on the fly */
129
130 #define MNT_ACTIVE 1
131 #define MNT_RDONLY 2
132 #define MNT_UNIXDIR 4
133
134 struct mnt mnttab[16], *curdisk; /* mount table (simulated drives A..P) */
135
136 /* display a mount table entry */
137 void
showdisk(int disk,int verbose)138 showdisk(int disk, int verbose)
139 {
140 struct mnt *dp = mnttab + disk;
141 BYTE version = dp->buf[16]; /* get version indentifier */
142
143 printf("%c: ", disk + 'A');
144 if (!(dp->flags & MNT_ACTIVE))
145 {
146 puts("not mounted\r\n");
147 return;
148 }
149 printf(dp->flags & MNT_UNIXDIR ? "%s %s/\r\n" : "%s %s\r\n",
150 dp->flags & MNT_RDONLY ? "r/o " : "r/w ", dp->filename);
151 if (!verbose)
152 return;
153 if (cpm3)
154 {
155 if (dp->flags & MNT_UNIXDIR)
156 printf("\r\n Drive connected to a directory!");
157 else
158 {
159 printf("\r\n Disk file created under ");
160 switch (version)
161 {
162 case 0:
163 printf("yaze 1.10/1.06 (version 0)");
164 break;
165 case 1:
166 printf("yaze-ag 2.xx (version 1)");
167 break;
168 default:
169 printf("- not known");
170 }
171 }
172 printf("\r\n\n CP/M 3.1 DPH (%04X)\r\n", dp->dph);
173 printf(" xlt=%04X, mf=%02X, dpb=%04X,"
174 " csv=%04X, alv=%04X, dirbcb=%04X\r\n",
175 GetWORD(dp->dph), GetBYTE(dp->dph + 11), GetWORD(dp->dph + 12),
176 GetWORD(dp->dph + 14), GetWORD(dp->dph + 16), GetWORD(dp->dph + 18));
177 printf(" dtabcb=%04X, hash=%04X, hbank=%02X\r\n",
178 GetWORD(dp->dph + 20), GetWORD(dp->dph + 22), GetBYTE(dp->dph + 24));
179 printf("\r\n CP/M 3.1 DPB (%04X)\r\n", dp->dpb);
180 printf(" spt=%04X, bsh=%02X, blm=%02X, exm=%02X, dsm=%04X, drm=%04X,"
181 " al=%02X%02X, cks=%04X,\r\n off=%04X, psh=%02X,"
182 " phm=%02X ",
183 GetWORD(dp->dpb), GetBYTE(dp->dpb + 2), GetBYTE(dp->dpb + 3),
184 GetBYTE(dp->dpb + 4), GetWORD(dp->dpb + 5), GetWORD(dp->dpb + 7),
185 GetBYTE(dp->dpb + 9), GetBYTE(dp->dpb + 10), GetWORD(dp->dpb + 11),
186 GetWORD(dp->dpb + 13), GetBYTE(dp->dpb + 15), GetBYTE(dp->dpb + 16));
187 printf("(sektor size: %d)\r\n\n", (128 << GetBYTE(dp->dpb + 15)));
188
189 }
190 else
191 {
192
193 printf(" dph=%04X, xlt=%04X, dirbuf=%04X, dpb=%04X,"
194 " csv=%04X, alv=%04X, spt=%04X\r\n",
195 dp->dph, GetWORD(dp->dph), GetWORD(dp->dph + 8), GetWORD(dp->dph + 10),
196 GetWORD(dp->dph + 12), GetWORD(dp->dph + 14), GetWORD(dp->dph + 16));
197 printf(" bsh=%02X, blm=%02X, exm=%02X, dsm=%04X, drm=%04X,"
198 " al=%02X%02X, cks=%04X, off=%04X\r\n",
199 GetBYTE(dp->dph + 18), GetBYTE(dp->dph + 19), GetBYTE(dp->dph + 20),
200 GetWORD(dp->dph + 21), GetWORD(dp->dph + 23),
201 GetBYTE(dp->dph + 25), GetBYTE(dp->dph + 26),
202 GetWORD(dp->dph + 27), GetWORD(dp->dph + 29));
203 }
204 }
205
206 /* unmount a disk */
207 static int
umount(int disk)208 umount(int disk)
209 {
210 struct mnt *dp = mnttab + disk;
211 /* WORD xlt; <- deletet by agl, is in dp */
212
213 if (!(dp->flags & MNT_ACTIVE))
214 return 0;
215 if (dp->flags & MNT_UNIXDIR)
216 {
217 int i;
218 clearfc(dp); /* clear the bios's file cache */
219 for (i = 0; i < dp->nfds; i++)
220 free(dp->fds[i].fullname);
221 free(dp->data);
222 free(dp->fds);
223 }
224 else if (munmap(dp->header, dp->isize) == -1 ||
225 close(dp->ifd) == -1)
226 perror(dp->filename);
227 dp->flags = 0;
228 free(dp->filename);
229 /* modified by agl ...
230 if ((xlt = GetWORD(dp->dph)) != 0)
231 cpmfree(xlt, GetWORD(dp->dph+16));
232 */
233 /* by agl */
234 if ((dp->xlt = GetWORD(dp->dph)) != 0)
235 {
236 if (!cpm3) cpmfree(dp->xlt, GetWORD(dp->dph + 16));
237 dp->xlt = 0;
238 }
239 if (cpm3)
240 PutWORD((dtbl + (2 * disk)), 0); /* delete entry in dtbl (CP/M 3.1) */
241 else
242 cpmfree(GetWORD(dp->dph + 14), (GetWORD(dp->dph + 16 + 5) >> 3) + 1);
243 return 0;
244 }
245
246 /* stash a string away on the heap */
247 char *
newstr(const char * str)248 newstr(const char *str)
249 {
250 char *p = xmalloc(strlen(str) + 1);
251 (void) strcpy(p, str);
252 return p;
253 }
254
255 /* format of a cp/m 3.1 date stamp entry */
256 struct s_cpmdate
257 {
258 WORD day; /* count days since 1.1.1978 */
259 BYTE hour;
260 BYTE min;
261 /* BYTE sec; */
262 };
263
264 /* converts a time information of unix to a date information under cp/m 3.1 */
265 void
cpmdate(time_t * unixtime,struct s_cpmdate * cpmdat)266 cpmdate(time_t *unixtime, struct s_cpmdate *cpmdat)
267 {
268 struct tm *t;
269
270 t = localtime(unixtime);
271
272 cpmdat->day = (WORD)(dayfaktor(t->tm_mday,
273 (t->tm_mon + 1),
274 (1900 + t->tm_year)
275 ) - DayFaktor_CPMSTART);
276
277 cpmdat->hour = (BYTE)(((t->tm_hour / 10) << 4) | (t->tm_hour % 10));
278 cpmdat->min = (BYTE)(((t->tm_min / 10) << 4) | (t->tm_min % 10));
279 /* cpmdat->sec = (BYTE)( ((t->tm_sec/10)<<4) | (t->tm_sec%10) ); */
280 }
281
282 /* Decide if a unix file is eligible to be included as a cp/m file in a
283 simulated disk constructed from a unix directory. Return the filesize in
284 bytes if so, 0 if not. */
285 static off_t
cpmsize(const char * dir_name,const char * filename,unsigned char * cpmname,char ** unixname,struct s_cpmdate * acc_time,struct s_cpmdate * mod_time)286 cpmsize(const char *dir_name, const char *filename, unsigned char *cpmname,
287 char **unixname, struct s_cpmdate *acc_time, struct s_cpmdate *mod_time)
288 {
289 int i, fd;
290 const char *p = filename;
291 unsigned char *p1 = cpmname;
292 struct stat st;
293 char *path;
294
295 /* construct cpm filename, rejecting any that dont fit */
296 for (i = 0; i < 8; i++)
297 {
298 if (*p == 0 || *p == '.')
299 break;
300 if (*p <= ' ' || *p >= '{' || strchr("=_:,;<>", *p))
301 return 0;
302 *p1++ = toupper(*p);
303 p++;
304 }
305 for (; i < 8; i++)
306 *p1++ = ' ';
307 if (*p)
308 {
309 if (*p == '.')
310 p++;
311 else
312 return 0;
313 }
314 for (; i < 11; i++)
315 {
316 if (*p == 0)
317 break;
318 if (*p <= ' ' || *p >= '{' || strchr(".=_:,;<>", *p))
319 return 0;
320 *p1++ = toupper(*p);
321 p++;
322 }
323 if (*p)
324 return 0;
325 for (; i < 11; i++)
326 *p1++ = ' ';
327
328 /* construct unix filename */
329 path = xmalloc(strlen(dir_name) + strlen(filename) + 2);
330 sprintf(path, "%s/%s", dir_name, filename);
331
332 /* check that file is readable, regular and non-empty */
333 if ((fd = open(path, O_RDONLY)) < 0)
334 {
335 free(path);
336 return 0;
337 }
338 if (fstat(fd, &st) < 0)
339 {
340 close(fd);
341 free(path);
342 return 0;
343 }
344 close(fd);
345 #ifdef __EXTENSIONS__
346 if (((st.st_mode & S_IFMT) != S_IFREG) || st.st_size == 0)
347 {
348 #else
349 if (((st.st_mode & __S_IFMT) != __S_IFREG) || st.st_size == 0)
350 {
351 #endif
352 free(path);
353 return 0;
354 }
355
356 cpmdate(&st.st_atime, acc_time); /* convert access time in cp/m format */
357 cpmdate(&st.st_mtime, mod_time); /* modification time (will be update) */
358 /* added by agl (26.1.2004 */
359 *unixname = path;
360 return st.st_size;
361 }
362
363 /* mount a unix directory as a simulated cpm disk */
364 /* expanded Jan 2004 by Andreas Gerlich to handle cp/m date stamps entries */
365 /* (It's a terrible hack ;-) */
366
367 static struct cpmlabel /* record of a cp/m 3.1 label */
368 {
369 char user;
370 char labelname[8 + 3];
371 char LB; /* Label byte */
372 char PB; /* Used to decode label password */
373 char RR1, RR2; /* reserved */
374 char Password[8]; /* password */
375 /* Label create/access datestamp */
376 BYTE cr_day_low;
377 BYTE cr_day_high;
378 BYTE cr_hour;
379 BYTE cr_min;
380 /* Label update datestamp */
381 BYTE up_day_low;
382 BYTE up_day_high;
383 BYTE up_hour;
384 BYTE up_min;
385 } stdlabel =
386 {
387 0x20,
388 " ",
389 0x21, /* LB: update + label exists */
390 0, /* PB */
391 0, 0, /* RR */
392 "\x00\x00\x00\x00\x00\x00\x00\x00", /* no password */
393 0x31, 0x25, 0x22, 0x55, /* 25.1.2004, 22:55 */
394 0x31, 0x25, 0x22, 0x56 /* 25.1.2004, 22:56 */
395 };
396
397 /* LB - byte, modified by function dosetacc */
398 #define LBL_existsbit 0x01
399 #define LBL_createbit 0x10
400 #define LBL_updatebit 0x20
401 #define LBL_accessbit 0x40
402 unsigned int lbl = 0x21; /* default: exists & update */
403
404
405 struct s_datestamps /* record of one datestamp in the dates stamps */
406 {
407 /* CP/M create/access time */ /* directory Entry (10 bytes) */
408 BYTE cr_day_low;
409 BYTE cr_day_high;
410 BYTE cr_hour;
411 BYTE cr_min;
412 /* CP/M update time */
413 BYTE up_day_low;
414 BYTE up_day_high;
415 BYTE up_hour;
416 BYTE up_min;
417 BYTE pm; /* password mode */
418 BYTE null;
419 };
420
421
422 static int
423 mountdir(struct mnt *dp, const char *filename, unsigned int labelb)
424 {
425 DIR *dirp;
426 struct dirent *direntp;
427 unsigned char *cpmdir = xmalloc(N_ENTRIES * 32);
428 unsigned long blockno = COVER((N_ENTRIES * 32), BLOCK_SIZE);
429 int direno = 0;
430 int direno2 = 0;
431 int nfiles = 0;
432 int i;
433 WORD alv, w;
434 static unsigned long serialno = 0;
435 unsigned char *cp; /* pointer to cp/m directory entry */
436 char *cp2;
437 /* for time */
438 time_t now;
439 struct s_cpmdate cpm_now, acc_time, mod_time;
440 struct cpmlabel *cpmlbl;
441 unsigned char *datestamps;
442 struct s_datestamps filedate;
443
444 dp->fds = xmalloc(N_ENTRIES * sizeof(struct fdesc));
445 if ((dirp = opendir(filename)) == NULL)
446 {
447 perror(filename);
448 free(cpmdir);
449 free(dp->fds);
450 return 0;
451 }
452 memclr(cpmdir, (N_ENTRIES * 32));
453
454 /* setup the first 4 directory entries (first 128 byte) */
455 cp = cpmdir;
456 *cp = 0xE5; /* E5H --> erased directory entry */
457 cp += 32;
458 *cp = 0xE5;
459 cp += 32;
460 *cp = 0xE5;
461 cp += 32;
462 *cp = 0x21; /* 21H -> date stamps directory entry */
463 /* datestamps = cpmdir + 3*32 + 1; */
464 datestamps = cp + 1; /* pointer to the date stamp of the first file */
465
466 /* Setup label entry */
467 memcpy(cpmdir, &stdlabel, sizeof(stdlabel));
468 cpmlbl = (struct cpmlabel *) cpmdir;
469 /* points to the cp/m label entry (first entry in */
470 /* the cp/m directory */
471 /* copy name of directory into the labelname of the CP/M disk label */
472 #ifndef CYGWIN
473 cp = cpmdir + 1;
474 cp2 = basename((char *) filename);
475 for (i = 0; *cp2 && i < 11 ; i++)
476 *cp++ = *cp2++;
477 #endif
478 /* cpmlbl->LB = labelb; \* Label byte */
479 /* LB wird nicht gesetzt (bleibt auf 0x21). Wenn das
480 accessBit gesetzt ist versucht CP/M beim lesen einer File
481 in die directory mit den access time informationen zu
482 schreiben. Dies verursacht ein Fehler (da R/O):
483 CP/M verschluckt sich dabei in der Weise, dass die
484 betroffenen Dateien nicht
485 mehr sichtbar sind (mit DIR) aber noch z.b. mit type
486 geoeffnet werden koennen.
487 */
488
489 time(&now); /* get time/date */
490 cpmdate(&now, &cpm_now); /* convert into cp/m 3.1 format */
491
492 /* set create time in label */
493 cpmlbl->cr_day_low = cpm_now.day & 0x00ff; /* % 256; */
494 cpmlbl->cr_day_high = cpm_now.day >> 8; /* / 256; */
495 cpmlbl->cr_hour = cpm_now.hour;
496 cpmlbl->cr_min = cpm_now.min;
497
498 /* set update time in label */
499 cpmlbl->up_day_low = cpm_now.day & 0x00ff; /* % 256; */
500 cpmlbl->up_day_high = cpm_now.day >> 8; /* / 256; */
501 cpmlbl->up_hour = cpm_now.hour;
502 cpmlbl->up_min = cpm_now.min;
503
504
505 /* time stamps into the date stamps directory entry */
506
507 memclr(&filedate, sizeof(struct s_datestamps));
508
509 if (labelb & LBL_accessbit)
510 {
511 filedate.cr_day_low = cpm_now.day & 0x00ff; /* mod 256 */
512 filedate.cr_day_high = cpm_now.day >> 8; /* div 256 */
513 filedate.cr_hour = cpm_now.hour;
514 filedate.cr_min = cpm_now.min;
515 }
516
517 filedate.up_day_low = cpm_now.day & 0x00ff; /* mod 256 */
518 filedate.up_day_high = cpm_now.day >> 8; /* div 256 */
519 filedate.up_hour = cpm_now.hour;
520 filedate.up_min = cpm_now.min;
521
522 memcpy(datestamps, &filedate, sizeof(struct s_datestamps));
523
524 direno++;
525
526 while ((direntp = readdir(dirp)) != NULL)
527 {
528 char *fullname;
529 off_t size; /* file size in bytes */
530 int ndirents; /* number of directory entries required for file */
531 int nlogexts; /* number of full logical extents occupied by */
532 /* file */
533 unsigned long nblocks; /* number of blocks occupied by file */
534
535 if ((size = cpmsize(filename, direntp->d_name, cpmdir + 32 * direno + 1,
536 &fullname, &acc_time, &mod_time)) == 0)
537 continue;
538 for (i = 0; i < direno; i++)
539 if (memcmp(cpmdir + 32 * i + 1, cpmdir + 32 * direno + 1, 11) == 0)
540 {
541 free(fullname); /* discard case-collapsed duplicates */
542 size = 0; /* added by agl, 20.12.2003 */
543 break; /* added by agl */
544 /* continue; \* deleted by agl, this does not work */
545 }
546 if (size == 0) /* added by agl, 20.12.2003 */
547 continue;
548
549 /* setup date stamp of the directory entry */
550 /* adr of the related datestamp := datestamps + (direno % 4)*10; */
551
552 memclr(&filedate, sizeof(struct s_datestamps));
553
554 /* unix access time as create/access time under cp/m */
555 if (labelb & LBL_accessbit)
556 {
557 filedate.cr_day_low = acc_time.day & 0x0ff; /* mod 256 */
558 filedate.cr_day_high = acc_time.day >> 8; /* div 256 */
559 filedate.cr_hour = acc_time.hour; /* time under cp/m 3.1 */
560 filedate.cr_min = acc_time.min;
561 }
562
563 /* unix modification time as update time under cp/m */
564 filedate.up_day_low = mod_time.day & 0x0ff; /* mod 256 */
565 filedate.up_day_high = mod_time.day >> 8; /* div 256 */
566 filedate.up_hour = mod_time.hour; /* time under cp/m 3.1 */
567 filedate.up_min = mod_time.min;
568
569 memcpy(datestamps + (direno % 4) * 10, &filedate,
570 sizeof(struct s_datestamps));
571
572 ndirents = COVER(size, 8 * BLOCK_SIZE);
573 /* nlogexts = size/(16*1024); orig changed at 2008-06-15 */
574 nlogexts = (size - 1) / (16 * 1024);
575 nblocks = COVER(size, BLOCK_SIZE);
576
577 if ((direno + ndirents > (N_ENTRIES - 1)) ||
578 ((blockno + nblocks) * BLOCK_SIZE > 0xffff * 128))
579 {
580 fprintf(stderr, "not all files in %s will fit on disk\n", filename);
581 free(fullname);
582 break;
583 }
584 dp->fds[nfiles].fullname = fullname;
585 dp->fds[nfiles].firstblock = blockno;
586 dp->fds[nfiles].lastblock = blockno + nblocks - 1;
587 dp->fds[nfiles++].serial = serialno++;
588 direno2 = direno;
589 for (i = 0; i < ndirents; i++)
590 {
591 int ex = nlogexts < 2 * i + 1 ? nlogexts : 2 * i + 1;
592 int j;
593 cp = cpmdir + (direno2) * 32;
594 *cp = 0; /* user = 0 */
595 if (i)
596 memcpy(cp + 1, cpmdir + direno * 32 + 1, 11);
597 cp[12] = ex & 0x1f;
598 cp[13] = 0;
599 cp[14] = ex >> 5;
600 /*
601 cp[15] = nblocks <= 8 ?
602 COVER((size-16*1024*nlogexts), 128) : 128;
603 */
604 /* added the followng 2 lines 2008-06-02, AG */
605 if ((size % 16384) == 0)
606 cp[15] = 128;
607 else
608 {
609 cp[15] = nblocks <= 8 ?
610 COVER((size - 16 * 1024 * nlogexts), 128) : 128;
611 }
612 cp += 16;
613 for (j = 0; j < 8; j++)
614 {
615 if (nblocks > 0)
616 {
617 *cp++ = blockno;
618 *cp++ = blockno >> 8;
619 ++blockno;
620 --nblocks;
621 }
622 else
623 {
624 *cp++ = 0;
625 *cp++ = 0;
626 }
627 }
628 direno2++;
629 /* handle last dir entry (date stamps entry) */
630 if ((direno2 % 4) == 3) /* date stamps entry ? */
631 {
632 cp = cpmdir + (direno2) * 32;
633 *cp = 0x21;
634 if (++direno2 < N_ENTRIES)
635 {
636 /* setup next sektor */
637 cp = cpmdir + (direno2) * 32;
638 *cp = 0xE5;
639 cp += 32;
640 *cp = 0xE5;
641 cp += 32;
642 *cp = 0xE5;
643 cp += 32;
644 *cp = 0x21; /* date stamps */
645 datestamps = cp + 1;
646 }
647 }
648 }
649 direno = direno2;
650 }
651 while ((direno % 4) != 0) direno++; /* direno begins at next sector */
652
653 closedir(dirp);
654 if (nfiles == 0)
655 {
656 fprintf(stderr, "no suitable files in %s\n", filename);
657 free(cpmdir);
658 free(dp->fds);
659 return 0;
660 }
661 dp->nde = direno;
662 while (direno & 3)
663 {
664 memset(cpmdir + direno * 32, 0xe5, 32);
665 ++direno;
666 }
667 dp->nfds = nfiles;
668 /* dp->fds = realloc(dp->fds, nfiles*sizeof(struct fdesc)); \* del by agl */
669 /* dp->data = realloc(cpmdir, direno*32); \* deleted by agl */
670 dp->data = cpmdir; /* */
671
672 /* always setup buf[] if setup_cpm3_dph_dpb is running */
673
674 dp->xlt = 0; /* No translation table */
675
676 dp->buf[16] = 1; /* version indentifier DPB for CP/M 3.1 */
677
678 dp->buf[32 + 0] = 0x00; /* LOW(SPT) 256 SPT */
679 dp->buf[32 + 1] = 0x01; /* HIGH(SPT) */
680
681 dp->buf[32 + 2] = 5; /* block shift factor */
682 dp->buf[32 + 3] = 31; /* block mask */
683 dp->buf[32 + 4] = 1; /* extend mask */
684
685 w = blockno > 256 ? blockno - 1 : 256;
686 dp->buf[32 + 5] = (BYTE)(w & 0xff); /* LOW(DSM) */
687 dp->buf[32 + 6] = (BYTE)(w >> 8); /* HIGH(DSM) */
688
689 w = N_ENTRIES - 1;
690 dp->buf[32 + 7] = (BYTE)(w & 0xff); /* LOW(DRM) */
691 dp->buf[32 + 8] = (BYTE)(w >> 8); /* HIGH(DRM) */
692
693 dp->buf[32 + 9] = 0xff; /* AL0 */
694 dp->buf[32 + 10] = 0xff; /* AL1 */
695
696 dp->buf[32 + 11] = 0; /* LOW(CKS) */
697 dp->buf[32 + 12] = 0; /* HIGH(CKS) */
698
699 dp->buf[32 + 13] = 0; /* LOW(OFF) */
700 dp->buf[32 + 14] = 0; /* HIGH(OFF) */
701
702 dp->buf[32 + 15] = 0; /* PSH */
703 dp->buf[32 + 16] = 0; /* PHM */
704
705 if (!cpm3)
706 {
707 /* set up disk parameter header for CP/M 2.2 */
708 dp->dph = dptable + (16 + 15) * (dp - mnttab);
709 memclr(ram + dp->dph, 16 + 15);
710 PutWORD(dp->dph + 8, dirbuff); /* pointer to directory buffer */
711 dp->dpb = dp->dph + 16; /* Pointer to dpb (for yaze-ag) (agl)*/
712 /* PutWORD(dp->dph+10, dp->dph+16); \* pointer to dpb */
713 PutWORD(dp->dph + 10, dp->dpb); /* set pointer to dpb in cp/m 2.2 (a) */
714
715 /* setting up dpb CP/M 2.2 */
716 PutWORD(dp->dpb + 0, 256); /* sectors per track */
717 PutBYTE(dp->dpb + 2, 5); /* block shift factor */
718 PutBYTE(dp->dpb + 3, 31); /* block mask */
719 PutBYTE(dp->dpb + 4, 1); /* extent mask */
720 PutWORD(dp->dpb + 5, blockno > 256 ? blockno - 1 : 256); /* DSM */
721 PutWORD(dp->dpb + 7, N_ENTRIES - 1); /* DRM */
722 PutWORD(dp->dpb + 9, 0xffff); /* AL0,AL1 */
723 /*
724 PutWORD(dp->dph+16, 256); \* sectors per track *\
725 PutBYTE(dp->dph+18, 5); \* block shift factor *\
726 PutBYTE(dp->dph+19, 31); \* block mask *\
727 PutBYTE(dp->dph+20, 1); \* extent mask *\
728 PutWORD(dp->dph+21, blockno > 256 ? blockno-1 : 256); \* DSM *\
729 PutWORD(dp->dph+23, N_ENTRIES-1); \* DRM *\
730 PutWORD(dp->dph+25, 0xffff); \* AL0,AL1 *\
731 */
732
733 alv = cpmalloc((GetWORD(dp->dph + 16 + 5) >> 3) + 1);
734 if (alv == 0)
735 {
736 fprintf(stderr, "insufficient space to mount %s\n", filename);
737 for (i = 0; i < dp->nfds; i++)
738 free(dp->fds->fullname);
739 free(dp->data);
740 free(dp->fds);
741 return 0;
742 }
743 PutWORD(dp->dph + 14, alv); /* pointer to allocation vector */
744 }
745 dp->filename = newstr(filename);
746 dp->flags = MNT_ACTIVE | MNT_RDONLY | MNT_UNIXDIR;
747
748 return 1;
749 }
750
751 static struct
752 {
753 char magic[32];
754 char dpb[15];
755 } sssd =
756 {
757 "<CPM_Disk> Drive x",
758 "\x1a\x00\x03\x07\x00\xf2\x00\x3f\x00\xc0\x00\x00\x00\x02\x00"
759 };
760
761 static char *xlt26 =
762 "\x00\x06\x0c\x12\x18\x04\x0a\x10\x16\x02\x08\x0e\x14"
763 "\x01\x07\x0d\x13\x19\x05\x0b\x11\x17\x03\x09\x0f\x15";
764
765 static int
766 mount(int disk, const char *filename, int readonly)
767 {
768 struct mnt *dp = mnttab + disk;
769 int prot = PROT_READ | PROT_WRITE;
770 int doffs, r;
771 WORD alv;
772 int disksize, ldisksize; /* for calculation of disk size */
773 /* BYTE buf[128]; <-- it is now defined in struct mnt (for cp/m 3) */
774 struct stat st;
775
776 if (dp->flags & MNT_ACTIVE)
777 umount(disk);
778
779 dp->flags = 0;
780 if (stat(filename, &st) < 0)
781 {
782 perror(filename);
783 return 0;
784 }
785
786 #ifdef __EXTENSIONS__
787 if ((st.st_mode & S_IFMT) == S_IFDIR)
788 {
789 #else
790 if ((st.st_mode & __S_IFMT) == __S_IFDIR)
791 {
792 #endif
793
794 if ((r = mountdir(dp, filename, lbl))
795 && cpm3)
796 /* if (cpm3) */
797 setup_cpm3_dph_dpb(disk);
798 return r;
799 }
800
801 #ifdef __EXTENSIONS__
802 if ((st.st_mode & S_IFMT) != S_IFREG)
803 {
804 #else
805 if ((st.st_mode & __S_IFMT) != __S_IFREG)
806 {
807 #endif
808
809 fprintf(stderr, "%s is neither a regular file nor a directory\n", filename);
810 return 0;
811 }
812
813 if (readonly || (dp->ifd = open(filename, O_RDWR)) < 0)
814 {
815 prot = PROT_READ;
816 dp->flags |= MNT_RDONLY;
817 if ((dp->ifd = open(filename, O_RDONLY)) < 0)
818 {
819 perror(filename);
820 return 0;
821 }
822 }
823
824 /* peek at descriptor page */
825 if (read(dp->ifd, dp->buf, 128) != 128)
826 {
827 perror(filename);
828 close(dp->ifd);
829 return 0;
830 }
831 if (memcmp(dp->buf, "<CPM_Disk>", 10) != 0)
832 {
833 /* WORD xlt; */
834 if (st.st_size != 256256)
835 {
836 fprintf(stderr, "%s is not a valid <CPM_Disk> file\n", filename);
837 close(dp->ifd);
838 return 0;
839 }
840 /* assume this is an image of a sssd floppy */
841 memcpy(dp->buf, &sssd, sizeof(sssd));
842 if (cpm3)
843 {
844 dp->buf[sizeof(sssd)] = 0; /* psh = 0 */ /* added 27.3.2005 */
845 dp->buf[sizeof(sssd) + 1] = 0; /* phm = 0 */
846 dp->xlt = 1; /* look to setup_cpm3_dph_dpb() in bios.c */
847 }
848 else
849 {
850 dp->dph = dptable + (16 + 15) * disk;
851 memclr(ram + dp->dph, 16 + 15);
852 dp->xlt = cpmalloc(26); /* space for sector translation table */
853 memcpy(ram + dp->xlt, xlt26, 26);
854 PutWORD(dp->dph, dp->xlt);
855 }
856 doffs = 0;
857 }
858 else
859 {
860 dp->xlt = 0; /* no translation table */
861 if (!cpm3)
862 {
863 dp->dph = dptable + (16 + 15) * disk;
864 memclr(ram + dp->dph, 16 + 15);
865 }
866 doffs = 128;
867 }
868 if (cpm3)
869 {
870 setup_cpm3_dph_dpb(disk);
871 if (dp->dsm > maxdsm
872 || (dp->bls == 1024 && dp->drm > 511) /* see page 43 of the SYSTEM */
873 || (dp->bls == 2048 && dp->drm > 1023) /* GUIDE, table 3-6 */
874 || (dp->bls == 4096 && dp->drm > 2047)
875 || (dp->bls == 8192 && dp->drm > 4095)
876 || (dp->bls == 16384 && dp->drm > 8191)
877 || (dp->drm > maxdrm))
878 {
879 fprintf(stderr, "Error to mount '%s'\n", filename);
880 fprintf(stderr, " Either the parameters at creation time of '%s' "
881 "are wrong\n or you have created a disk file greater "
882 "8 MB with\n the default parameters.\n", filename);
883 fprintf(stderr, " If you want to mount disks with greater size "
884 "than\n");
885 fprintf(stderr, " 8 MB then use the following create commands:\n\n");
886 ldisksize = (int)(2048 * (maxdsm + 1) / (1024 * 1024) + 1);
887 disksize = (int)(4096 * (maxdsm + 1) / (1024 * 1024));
888 fprintf(stderr, " 'create -b %d -d %d <filename> <x>M' for a "
889 "%d - %d MB disk\n", 4096, maxdrm, ldisksize, disksize);
890 ldisksize = disksize + 1;
891 disksize = (int)(8192 * (maxdsm + 1) / (1024 * 1024));
892 fprintf(stderr, " 'create -b %d -d %d <filename> <x>M' for a "
893 "%d - %d MB disk\n", 8192, maxdrm, ldisksize, disksize);
894 ldisksize = disksize + 1;
895 disksize = (int)(16384 * (maxdsm + 1) / (1024 * 1024));
896 fprintf(stderr, " 'create -b %d -d %d <filename> <x>M' for a "
897 "%d - %d MB disk\n\n", 16384, maxdrm, ldisksize, disksize);
898 fprintf(stderr, " Or edit MAXDSM and MAXDRM in sysdef.lib of the Z80 "
899 "bios files and\n compile the CP/M 3.1 BIOS and "
900 "generate CPM3.SYS and CPM3.COM and\n transfere "
901 "CPM3.COM to the UNIX file yaze-cpm3.boot.\n"
902 " ATTENTION! Before you edit refer the sections "
903 "\"Disk Parameter Header\"\n"
904 " and \"Disk Parameter Block\" "
905 "page 36 - 44 of the System Guide.\n");
906 close(dp->ifd);
907 return 0;
908 }
909 }
910 else
911 {
912 PutWORD(dp->dph + 8, dirbuff); /* pointer to directory buffer */
913 dp->dpb = dp->dph + 16; /* pointer to dpb */
914 /* PutWORD(dp->dph+10, dp->dph+16);\* set pointer to dpb in cp/m 2.2 */
915 PutWORD(dp->dph + 10, dp->dpb); /* set pointer to dpb in cp/m 2.2 */
916 memcpy(ram + dp->dph + 16, dp->buf + 32, 15); /* copy dpb into cp/m ram */
917 PutWORD(dp->dph + 16 + 11, 0); /* check vector size = 0 (fixed disk) */
918
919 /* calculate memory requirement */
920 /* (((DSM+1)<<BSH) + OFFS*SPT + 1)*128 */
921 dp->isize = (((GetWORD(dp->dph + 16 + 5) + 1) << GetBYTE(dp->dph + 16 + 2)) +
922 GetWORD(dp->dph + 16 + 13) * GetWORD(dp->dph + 16 + 0) + 1) * 128;
923
924 alv = cpmalloc((GetWORD(dp->dph + 16 + 5) >> 3) + 1);
925 if (alv == 0)
926 {
927 fprintf(stderr, "insufficient space to mount %s\n", filename);
928 close(dp->ifd);
929 return 0;
930 }
931 PutWORD(dp->dph + 14, alv); /* pointer to allocation vector */
932 } /* of if (cpm3) */
933
934 #ifndef MAP_FILE
935 #define MAP_FILE 0
936 #endif
937
938 #ifdef __BOUNDS_CHECKING_ON
939 /* absurd -1 return code blows bgcc's mind */
940 dp->header = mmap(NULL, dp->isize, prot, MAP_FILE | MAP_SHARED, dp->ifd, 0);
941 #else
942 if ((dp->header = mmap(NULL, dp->isize, prot, MAP_FILE | MAP_SHARED,
943 dp->ifd, 0)) == (char *) - 1)
944 {
945 perror(filename);
946 close(dp->ifd);
947 return 0;
948 }
949 #endif
950 dp->filename = newstr(filename);
951 dp->data = (BYTE *) dp->header + doffs;
952 dp->flags |= MNT_ACTIVE;
953
954 return 1;
955 }
956
957 int
958 remount(int disk)
959 {
960 struct mnt *dp = mnttab + disk;
961 char *filename;
962 int r;
963
964 if (!(dp->flags & MNT_ACTIVE))
965 return 0;
966 filename = newstr(dp->filename);
967 r = (dp->flags & MNT_RDONLY) ? 1 : 0;
968 /* printf("remount: disk = %d, filename = %s, r = %d\r\n",disk,filename,r);
969 */
970 r = mount(disk, filename, r);
971 free(filename);
972 return r;
973 }
974
975 static const char *white = " \t";
976
977 static int
978 dosetacc(char *cmd)
979 {
980 char *tok = strtok(NULL, white);
981
982 if (tok)
983 {
984 if (strcmp(tok, "on") == 0)
985 lbl = LBL_existsbit | LBL_updatebit | LBL_accessbit;
986 else if (strcmp(tok, "off") == 0)
987 lbl = LBL_existsbit | LBL_updatebit;
988 else
989 fprintf(stderr, "setaccess needs the parameter \"on\" or "
990 "\"off\" (see \"help setaccess\")\r\n");
991 }
992 else
993 printf("access stamps = %s\n", (lbl & LBL_accessbit) ? "ON" : "OFF");
994 return 0;
995 }
996
997 static int
998 doremount(char *cmd)
999 {
1000 int d;
1001 char *tok = strtok(NULL, white);
1002
1003 if (tok)
1004 {
1005 d = *tok - 'A';
1006 if (d < 0 || d > 15)
1007 d = *tok - 'a';
1008 if (d < 0 || d > 15 || tok[1])
1009 {
1010 fprintf(stderr, "illegal disk specifier: %s\n", tok);
1011 return 0;
1012 }
1013 /* printf("doremount: disk = %d\n",d); */
1014 if (mnttab[d].flags & MNT_ACTIVE)
1015 remount(d);
1016 else
1017 fprintf(stderr, "drive %c is not mounted\n", d + 'A');
1018 }
1019 else
1020 fprintf(stderr, "remount needs a disk specifier\n");
1021 return 0;
1022 }
1023
1024 static int
1025 domount(char *cmd)
1026 {
1027 int d, v, r;
1028 char *tok = strtok(NULL, white);
1029
1030 if ((v = (tok && (strcmp(tok, "-v") == 0))))
1031 tok = strtok(NULL, white);
1032 if ((r = tok && (strcmp(tok, "-r") == 0)))
1033 tok = strtok(NULL, white);
1034 if (tok && !v)
1035 {
1036 d = *tok - 'A';
1037 if (d < 0 || d > 15)
1038 d = *tok - 'a';
1039 if (d < 0 || d > 15 || tok[1])
1040 {
1041 fprintf(stderr, "illegal disk specifier: %s\r\n", tok);
1042 return 0;
1043 }
1044 tok = strtok(NULL, white);
1045 mount(d, tok, r);
1046 /* printf("domount: disk: %c:, ydsk-file: %s, read only = %c\r\n",'A'+d,tok,r?'Y':'N'); */
1047 }
1048 else
1049 for (d = 0; d < 16; d++)
1050 if (mnttab[d].flags & MNT_ACTIVE)
1051 showdisk(d, v);
1052 return 0;
1053 }
1054
1055 static int
1056 doumount(char *cmd)
1057 {
1058 int d;
1059 char *tok = strtok(NULL, white);
1060
1061 if (tok)
1062 {
1063 d = *tok - 'a';
1064 if (d < 0 || d > 15 || tok[1])
1065 {
1066 fprintf(stderr, "illegal disk specifier: %s\n", tok);
1067 return 0;
1068 }
1069 umount(d);
1070 }
1071 else
1072 fprintf(stderr, "umount needs a disk specifier\n");
1073 return 0;
1074 }
1075
1076 struct sio siotab[MAXPSTR] =
1077 {
1078 { NULL, NULL, "ttyin", 0, ST_IN2 },
1079 { NULL, NULL, "ttyout", 0, ST_OUT2 },
1080 { NULL, NULL, "crtin", 0, ST_IN2 },
1081 { NULL, NULL, "crtout", 0, ST_OUT2 },
1082 { NULL, NULL, "uc1in", 0, ST_IN2 },
1083 { NULL, NULL, "uc1out", 0, ST_OUT2 },
1084 { NULL, NULL, "rdr", 0, ST_IN },
1085 { NULL, NULL, "ur1", 0, ST_IN },
1086 { NULL, NULL, "ur2", 0, ST_IN },
1087 { NULL, NULL, "pun", 0, ST_OUT },
1088 { NULL, NULL, "up1", 0, ST_OUT },
1089 { NULL, NULL, "up2", 0, ST_OUT },
1090 { NULL, NULL, "lpt", 0, ST_OUT },
1091 { NULL, NULL, "ul1", 0, ST_OUT },
1092 { NULL, NULL, "aux", 0, ST_INOUT }
1093 };
1094
1095
1096 /* Table of logical streams */
1097 int chn[6];
1098
1099 static int
1100 doattach(char *cmd)
1101 {
1102 int fd, i, opflags;
1103 struct sio *s;
1104 char *tok = strtok(NULL, white);
1105
1106 if (tok)
1107 {
1108 char *p = tok + strlen(tok);
1109 if (p > tok && *--p == ':')
1110 *p = 0;
1111 for (i = 0; i < MAXPSTR; i++)
1112 {
1113 s = siotab + i;
1114 if (strncmp(tok, s->streamname, 3) == 0)
1115 break;
1116 }
1117 if (i == MAXPSTR)
1118 {
1119 fprintf(stderr, "stream not recognized: %s\n", tok);
1120 return 0;
1121 }
1122 if (s->strtype == ST_INOUT)
1123 {
1124 opflags = O_RDWR;
1125 /* printf("ST_INOUT\n"); */
1126 }
1127 else if (s->strtype == ST_IN2)
1128 {
1129 if (strcmp(tok, (s + 1)->streamname) == 0)
1130 {
1131 s++;
1132 opflags = O_WRONLY | O_CREAT | O_TRUNC;
1133 }
1134 else if (strcmp(tok, s->streamname) == 0)
1135 opflags = O_RDONLY;
1136 else
1137 opflags = O_RDWR | O_CREAT;
1138 }
1139 else
1140 opflags = s->strtype ==
1141 ST_IN ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC;
1142 tok = strtok(NULL, white);
1143 if (!tok || !*tok)
1144 {
1145 fputs("need a filename\n", stderr);
1146 return 0;
1147 }
1148 if (s->fp)
1149 {
1150 fclose(s->fp);
1151 s->fp = NULL;
1152 free(s->filename);
1153 }
1154 if ((fd = open(tok, opflags, 0666)) < 0)
1155 perror(tok);
1156 else
1157 {
1158 char *mode = "rb";
1159 if (opflags & O_WRONLY)
1160 mode = "wb";
1161 else if (opflags & O_RDWR)
1162 mode = "r+b";
1163 s->filename = newstr(tok);
1164 s->fp = fdopen(fd, mode);
1165 s->tty = isatty(fd);
1166 /* printf("isatty %d\n",s->tty); */
1167 }
1168 }
1169 else for (i = 0; i < MAXPSTR; i++)
1170 {
1171 s = siotab + i;
1172 if (s->fp)
1173 printf("%s:\t%s\n", s->streamname, s->filename);
1174 }
1175 return 0;
1176 }
1177
1178 static int
1179 dodetach(char *cmd)
1180 {
1181 int i;
1182 struct sio *s;
1183 char *tok = strtok(NULL, white);
1184
1185 if (tok)
1186 {
1187 char *p = tok + strlen(tok);
1188 if (p > tok && *--p == ':')
1189 *p = 0;
1190 for (i = 0; i < MAXPSTR; i++)
1191 {
1192 s = siotab + i;
1193 if (strncmp(tok, s->streamname, 3) == 0)
1194 break;
1195 }
1196 if (i == MAXPSTR)
1197 {
1198 fprintf(stderr, "stream not recognized: %s\n", tok);
1199 return 0;
1200 }
1201 if (s->fp)
1202 {
1203 fclose(s->fp);
1204 s->fp = NULL;
1205 free(s->filename);
1206 }
1207 }
1208 return 0;
1209 }
1210
1211
1212 static long
1213 getval(char *s)
1214 {
1215 char *tok = s + 2;
1216
1217 if (*tok == 0)
1218 tok = strtok(NULL, white);
1219 if (tok && *tok)
1220 {
1221 long unit = 1;
1222 char u = tok[strlen(tok) - 1];
1223 switch (toupper(u))
1224 {
1225 case 'K':
1226 unit = 1024;
1227 break;
1228 case 'M':
1229 unit = 1024 * 1024;
1230 break;
1231 }
1232 return unit * strtol(tok, NULL, 10);
1233 }
1234 else
1235 {
1236 fprintf(stderr, "option needs a value: %s\n", s);
1237 return -1;
1238 }
1239 }
1240
1241 static void
1242 checkval(int ok, long val, char *msg)
1243 {
1244 if (!ok)
1245 fprintf(stderr, "bad %s value: %ld\n", msg, val);
1246 }
1247
1248 /* count ones in a 16-bit value */
1249 static int
1250 popcount(long v)
1251 {
1252 int total;
1253
1254 total = ((v >> 1) & 0x5555) + (v & 0x5555);
1255 total = ((total >> 2) & 0x3333) + (total & 0x3333);
1256 total = ((total >> 4) & 0x0f0f) + (total & 0x0f0f);
1257 return (total & 0xff) + (total >> 8);
1258 }
1259
1260 static void
1261 makedisk(FILE *f, char *fn, long diroffs, long dirsize, long fullsize)
1262 {
1263 long n;
1264 BYTE sector[128];
1265
1266 memset(sector, 0xe5, sizeof sector);
1267
1268 /* skip offset tracks */
1269 if (fseek(f, diroffs, SEEK_CUR) < 0)
1270 {
1271 fclose(f);
1272 perror(fn);
1273 return;
1274 }
1275 /* write empty directory */
1276 for (n = 0; n < dirsize; n += sizeof sector)
1277 if (fwrite(sector, sizeof sector, 1, f) == 0)
1278 {
1279 fclose(f);
1280 perror(fn);
1281 return;
1282 }
1283 /* seek to end of disk and write last sector to define size */
1284 if (fseek(f, fullsize - sizeof sector, SEEK_SET) < 0 ||
1285 fwrite(sector, sizeof sector, 1, f) == 0 ||
1286 fclose(f) != 0)
1287 perror(fn);
1288 }
1289
1290 /* create a new CP/M disk */
1291 static int
1292 docreate(char *tok)
1293 {
1294 char *fn = NULL;
1295 FILE *f;
1296 long size = 1024 * 1024;
1297 char head[128];
1298 long dsm, spt = -1, bsize = -1, drm = -1, offs = -1;
1299 long psh = 0; /* by agl for CP/M 3.1. */
1300 long phm = 0; /* default 128 byte sectors*/
1301 int dblocks;
1302 WORD al01;
1303
1304 while ((tok = strtok(NULL, white)) != NULL)
1305 {
1306 if (*tok == '-')
1307 switch (tok[1])
1308 {
1309 case 'b':
1310 bsize = getval(tok);
1311 break;
1312 case 'd':
1313 drm = getval(tok);
1314 break;
1315 case 'o':
1316 offs = getval(tok);
1317 break;
1318 case 's':
1319 spt = getval(tok);
1320 break;
1321 default:
1322 fprintf(stderr, "unrecognized option: %s\n", tok);
1323 }
1324 else
1325 {
1326 fn = tok;
1327 break;
1328 }
1329 }
1330
1331 if (fn == NULL)
1332 {
1333 fputs("need a filename\n", stderr);
1334 return 0;
1335 }
1336 if ((tok = strtok(NULL, white)) != NULL)
1337 {
1338 char unit = 'b';
1339 int n = sscanf(tok, "%ld%c", &size, &unit);
1340 if (n == 2)
1341 switch (toupper(unit))
1342 {
1343 case 'B':
1344 break;
1345 case 'K':
1346 size *= 1024;
1347 break;
1348 case 'M':
1349 size *= 1024 * 1024;
1350 break;
1351 default:
1352 fprintf(stderr, "units not recognized: %s\n", tok);
1353 return 0;
1354 }
1355 else if (n != 1)
1356 {
1357 fprintf(stderr, "need numeric size: %s\n", tok);
1358 return 0;
1359 }
1360 }
1361 if ((f = fopen(fn, "w")) == NULL)
1362 {
1363 perror(fn);
1364 return 0;
1365 }
1366 if (size == 256256 && (spt == -1 || spt == 26) &&
1367 (bsize == -1 || bsize == 1024) &&
1368 (drm == -1 || drm == 63) &&
1369 (offs == -1 || offs == 2))
1370 {
1371 /* raw standard sssd floppy format */
1372 spt = 26;
1373 drm = 63;
1374 offs = 2;
1375 /* we clear all tracks that might contain directory sectors,
1376 thus avoiding messing with the sector translation table */
1377 makedisk(f, fn, 128 * spt * offs, 128 * (((drm + 4) / 4 + spt - 1) / spt)*spt, size);
1378 return 0;
1379 }
1380 else if (size < 256 * 1024)
1381 {
1382 if (bsize == -1)
1383 bsize = 1024;
1384 if (drm == -1)
1385 drm = 63;
1386 if (spt == -1)
1387 spt = 26;
1388 if (offs == -1)
1389 offs = 0;
1390 }
1391 else
1392 {
1393 if (bsize == -1)
1394 bsize = 2048;
1395 if (drm == -1)
1396 drm = 1023;
1397 if (spt == -1)
1398 {
1399 spt = 128; /* Version 1 uses 128 sectors per track */
1400 psh = 4;
1401 phm = 15; /* sector size also 2048 */
1402 }
1403 if (offs == -1)
1404 offs = 0;
1405 }
1406 dsm = (size - offs * spt * 128) / bsize - 1;
1407 checkval(spt <= 0xffff, spt, "sectors per track");
1408 checkval(size / (spt * 128) + offs <= 0xffff, size / (spt * 128) + offs, "tracks");
1409 checkval(((bsize & (bsize - 1)) == 0) &&
1410 (bsize >= ((dsm < 256) ? 1024 : 2048)) &&
1411 bsize <= 16384, bsize, "block size");
1412 dblocks = ((drm + 1) * 32 + bsize - 1) / bsize;
1413 checkval(dblocks <= 16 && dblocks < dsm, drm, "max directory entry");
1414 memclr(head, sizeof head);
1415 sprintf(head, "<CPM_Disk>");
1416 head[16] = 1; /* version identifier by agl */
1417 /* yaze-1.10 have Version 0 */
1418 /* V 1: uses also PSH and PHM */
1419 head[32] = spt;
1420 head[33] = spt >> 8;
1421 head[34] = popcount(bsize - 1) - 7; /* bsh */
1422 head[35] = (bsize / 128 - 1); /* blm */
1423 head[36] = dsm < 256 ? bsize / 1024 - 1 : bsize / 2048 - 1; /* exm */
1424 head[37] = dsm;
1425 head[38] = dsm >> 8;
1426 head[39] = drm;
1427 head[40] = drm >> 8;
1428 al01 = ~((1 << (16 - dblocks)) - 1);
1429 head[41] = al01 >> 8;
1430 head[42] = al01;
1431 head[45] = offs;
1432 head[46] = offs >> 8;
1433
1434 head[47] = psh; /* PSH */ /* Version 1 */
1435 head[48] = phm; /* PHM */
1436
1437 if (fwrite(head, sizeof head, 1, f) == 0)
1438 {
1439 fclose(f);
1440 perror(fn);
1441 return 0;
1442 }
1443 makedisk(f, fn, 128 * spt * offs, 128 * (drm + 4) / 4, sizeof head + size);
1444 return 0;
1445 }
1446
1447 static int
1448 hexdig(char c)
1449 {
1450 if ('0' <= c && c <= '9')
1451 return c - '0';
1452 if ('A' <= c && c <= 'F')
1453 return c - 'A' + 10;
1454 if ('a' <= c && c <= 'f')
1455 return c - 'a' + 10;
1456 return -1;
1457 }
1458
1459 static int
1460 doint(char *cmd)
1461 {
1462 int d1, d2;
1463 char *tok = strtok(NULL, white);
1464
1465 if (tok)
1466 {
1467 if (strlen(tok) != 2)
1468 {
1469 bad:
1470 printf("%s invalid key specifier\n", tok);
1471 return 0;
1472 }
1473 /* let's face it: this doesn't work if the host character set is not ascii */
1474 if (tok[0] == '^' && '@' <= tok[1])
1475 interrupt = tok[1] & 0x1f;
1476 else
1477 {
1478 if ((d1 = hexdig(tok[0])) < 0)
1479 goto bad;
1480 if ((d2 = hexdig(tok[1])) < 0)
1481 goto bad;
1482 interrupt = (d1 << 4) + d2;
1483 }
1484 rawtio.c_lflag = interrupt ? ISIG : 0;
1485 rawtio.c_cc[VINTR] = interrupt;
1486 }
1487 else
1488 {
1489 fputs("interrupt key is ", stdout);
1490 if (interrupt == 0)
1491 puts("disabled");
1492 else if (interrupt < 0x20)
1493 printf("^%c\n", interrupt + '@');
1494 else
1495 printf("%2x\n", interrupt);
1496 }
1497 return 0;
1498 }
1499
1500 extern char *perl_params;
1501
1502 static int
1503 dotime(char *cmd)
1504 {
1505 static clock_t lastreal;
1506 clock_t now;
1507 static struct tms lastbuf;
1508 struct tms tbuf;
1509 static clock_t tickspersec;
1510 /* extern char *perl_params; */
1511
1512 if (tickspersec == 0)
1513 tickspersec = sysconf(_SC_CLK_TCK);
1514 now = times(&tbuf);
1515
1516 printf("elapsed=%.3f, user=%.3f, sys=%.3f (%s)\n",
1517 ((double)(now - lastreal)) / tickspersec,
1518 ((double)(tbuf.tms_utime - lastbuf.tms_utime)) / tickspersec,
1519 ((double)(tbuf.tms_stime - lastbuf.tms_stime)) / tickspersec,
1520 perl_params);
1521 lastreal = now;
1522 lastbuf = tbuf;
1523 return 0;
1524 }
1525
1526 static int
1527 dogo(char *cmd)
1528 {
1529 return 1;
1530 }
1531
1532 static int
1533 doshell(char *cmd)
1534 {
1535 char *shell = getenv("SHELL");
1536 #ifdef DEBUG
1537 void (*sigint)(int);
1538
1539 sigint = signal(SIGINT, SIG_IGN);
1540 #endif
1541 if (shell == NULL)
1542 shell = "/bin/sh";
1543 if (cmd[1])
1544 system(cmd + 1);
1545 else
1546 {
1547 system(shell);
1548 printf("Back in yaze-ag\n\07");
1549 }
1550 #ifdef DEBUG
1551 (void) signal(SIGINT, sigint);
1552 #endif
1553 return 0;
1554 }
1555
1556 static int
1557 do128(char *cmd)
1558 {
1559 int d;
1560 struct mnt *dp;
1561
1562 if (!cpm3)
1563 {
1564 puts("only used under CP/M 3.1");
1565 return 0;
1566 }
1567 always128 = 1;
1568 for (d = 0; d < 16; d++)
1569 {
1570 dp = mnttab + d;
1571 if (dp->flags & MNT_ACTIVE)
1572 setup_cpm3_dph_dpb(d);
1573 }
1574 puts("All disks have sektor size 128.");
1575 return 0;
1576 }
1577
1578 static int
1579 doclock(char *tok)
1580 {
1581 if ((tok = strtok(NULL, white)) != NULL) {
1582 clockFrequency_save = (FASTREG) strtol(tok, NULL, 10);
1583 brakeini = true;
1584 /*
1585 printf("token:%s\n", tok);
1586 */
1587 }
1588 if (clockFrequency_save) {
1589 printf("Frequency: %d kHz (%d.%d MHz)\n", clockFrequency_save,
1590 clockFrequency_save/1000,
1591 clockFrequency_save%1000);
1592 } else {
1593 puts("Frequency: max speed");
1594 }
1595 return 0;
1596 }
1597
1598 static int
1599 doquit(char *cmd)
1600 {
1601 exit(0);
1602 }
1603
1604 static int dohelp(char *cmd);
1605
1606 typedef struct
1607 {
1608 char *name; /* User printable name of the function. */
1609 int (*func)(char *); /* Function to call to do the job. */
1610 char *doc; /* Short documentation. */
1611 char *detail; /* Long documentation. */
1612 } COMMAND;
1613
1614 static COMMAND commands[] =
1615 {
1616 {
1617 "help", dohelp, "Display this text or give help about a command",
1618 "help <cmd> displays more information about <cmd>"
1619 },
1620 { "?", dohelp, "Synonym for `help'", NULL },
1621 {
1622 "attach", doattach, "Attach CP/M device to a unix file",
1623 "attach without arguments lists the current attachments\n"
1624 "attach <physdev> <file> attaches <physdev> to the unix <file>,\n"
1625 " where <physdev> is one of ttyin, ttyout,\n"
1626 " crtin, crtout, uc1in, uc1out, rdr,\n"
1627 " ur1, ur2, pun, up1, up2, lpt, ul1"
1628 },
1629 { "clock", doclock, "Set/Display the frequency of the Z80 simulator",
1630 "clock without argument lists the current frequency.\n"
1631 " The frequency is shown in kHz and MHz.\n"
1632 " If the frequency is 0 the emulator runs with\n"
1633 " \"max speed\".\n\n"
1634 "clock <frequency in kHz> Set a frequency. The frequency must be in kHz.\n"
1635 " For example for 4 MHz place 4000.\n"
1636 " A \"0\" returns to max speed."
1637 },
1638 {
1639 "detach", dodetach, "Detach CP/M device from file",
1640 "detach <physdev> closes the file attached to <physdev>\n"
1641 " (see attach)"
1642 },
1643 {
1644 "setaccess", dosetacc, "Turns on/off access time stamps for mounted directories",
1645 "setaccess on/off turns on/off access time stamps for "
1646 "mounted\n"
1647 " directories connected to a CP/M drive\n"
1648 " (see CP/M command \"SET [ACCESS=ON/OFF]\")\n\n"
1649 " without parameter status is printed\n\n"
1650 " default : off\n\n"
1651 " (update time stamps are always on)\n"
1652 " (create time stamps are not supported by "
1653 "host system)"
1654 },
1655 {
1656 "mount", domount, "Mount a unix file or directory as a CP/M disk",
1657 "mount without arguments lists the mount table\n"
1658 "mount -v lists the mount table verbosely\n"
1659 "mount <drive> <file> mounts <file> as CP/M disk <drive>\n"
1660 " (a letter from a..p).\n"
1661 " If <file> is a plain file it must contain a CP/M filesystem.\n"
1662 " If <file> is a unix directory its contents may be accessed\n"
1663 " as a read-only CP/M disk\n"
1664 "mount -r <drive> <file> mounts the <file> read/only."
1665 },
1666 {
1667 "remount", doremount, "Remount a CP/M disk",
1668 "remount <drive> remounts the file/directory associated with"
1669 " <drive>\n"
1670 " (a directory will be fully rereaded)"
1671 },
1672 {
1673 "umount", doumount, "Unmount a CP/M disk",
1674 "umount <drive> closes the file associated with <drive>\n"
1675 " and frees the resources"
1676 },
1677 {
1678 "create", docreate, "Create a new disk",
1679 "create {flags} <file> {size} creates a unix <file> initialized as a\n"
1680 " CP/M disk of size {size} (default 1MB).\n"
1681 " -b <block size> default 1024 if size < 256K, else 2048\n"
1682 " -d <# dir entries - 1> default 1023\n"
1683 " -o <track offset> default 0\n"
1684 " -s <sectors per track> default 128\n"
1685 "create <file> 256256 create a raw SSSD disk image"
1686 },
1687 {
1688 "128", do128, "Set sektor size to 128 for all disks (only CP/M 3.1)",
1689 "128 Set sektor size to 128 for all disks (only CP/M 3.1)\n\n"
1690 " If you create a disk file under yaze-ag and you use the default\n"
1691 " blocksize and the default sectors per track (see create) the\n"
1692 " sektor size is also set to 2048 bytes like the blocksize.\n\n"
1693 " If you use software like a disk edit utility under CP/M 3.1\n"
1694 " it can be necessary to set the sektor size to 128 bytes.\n\n"
1695 " To reverse this option you must restart yaze-ag."
1696 },
1697 {
1698 "interrupt", doint, "Set user interrupt key",
1699 "interrupt <key> makes <key> interrupt CP/M back to the monitor\n"
1700 " <key> may be a 2-digit hex number or ^x where x is one of a..z[\\]^_\n"
1701 " ^@ makes CP/M uninterruptible (from the keyboard)\n"
1702 "interrupt without an argument displays the current setting"
1703 },
1704 { "go", dogo, "Start/Continue CP/M execution", NULL },
1705 {
1706 "!", doshell, "Execute a unix command",
1707 "! escape to a unix shell\n"
1708 "!cmd execute unix cmd"
1709 },
1710 { "quit", doquit, "Terminate yaze-ag", NULL },
1711 {
1712 "time", dotime, "Display elapsed time since last `time' command",
1713 "displays elapsed, user and system time in seconds,\n"
1714 " along with simulator options"
1715 },
1716 { NULL, NULL, NULL, NULL }
1717 };
1718
1719 static int
1720 dohelp(char *cmd)
1721 {
1722 char *tok = strtok(NULL, white);
1723 int tlen;
1724 COMMAND *cp;
1725
1726 if (tok)
1727 {
1728 for (tlen = strlen(tok), cp = commands; cp->name; cp++)
1729 if (strncmp(tok, cp->name, tlen) == 0)
1730 break;
1731 if (cp->name)
1732 {
1733 puts(cp->detail ? cp->detail : cp->doc);
1734 return 0;
1735 }
1736 }
1737 for (cp = commands; cp->name; cp++)
1738 printf("%-10s %s\n", cp->name, cp->doc);
1739 return 0;
1740 }
1741
1742 int
1743 docmd(char *cmd)
1744 {
1745 char *tok;
1746 int tlen;
1747 COMMAND *cp;
1748 int (*func)(char *) = NULL;
1749
1750 if (cmd == NULL)
1751 return 0;
1752 if (*cmd == '#')
1753 return 0;
1754 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
1755 cmd++;
1756 for (tok = cmd + strlen(cmd) - 1; tok >= cmd; tok--)
1757 if (*tok == ' ' || *tok == '\t' || *tok == '\n')
1758 *tok = 0;
1759 else
1760 break;
1761 if (*cmd == 0)
1762 return 0;
1763 add_history(cmd);
1764 if (*cmd == '!')
1765 {
1766 /* special case */
1767 doshell(cmd);
1768 return 0;
1769 }
1770 tok = strtok(cmd, white);
1771 if (tok == NULL || *tok == 0)
1772 return 0;
1773 for (tlen = strlen(tok), cp = commands; cp->name; cp++)
1774 if (strncmp(tok, cp->name, tlen) == 0)
1775 /* don't allow quit command to be abbreviated */
1776 if (cp->func != doquit || strcmp(tok, cp->name) == 0)
1777 {
1778 if (func == NULL)
1779 func = cp->func;
1780 else
1781 {
1782 func = NULL; /* ambiguous */
1783 break;
1784 }
1785 }
1786 if (func)
1787 return func(cmd);
1788 printf("%s ?\n", tok);
1789 return 0;
1790 }
1791
1792 #ifdef DEBUG
1793 void
1794 sighand(int sig)
1795 {
1796 stopsim = 1;
1797 }
1798 #endif
1799
1800 void
1801 monitor(FASTWORK adr)
1802 {
1803 static char *cmd = NULL;
1804
1805 ttycook();
1806 #ifdef DEBUG
1807 if (adr & 0x10000)
1808 printf("stopped at pc=0x%04x\n", adr & 0xffff);
1809 stopsim = 0;
1810 signal(SIGINT, sighand);
1811 #endif
1812 #ifdef USE_GNU_READLINE
1813 do
1814 {
1815 if (cmd)
1816 {
1817 free(cmd);
1818 cmd = NULL;
1819 }
1820 cmd = readline("$>");
1821 if (cmd == NULL)
1822 {
1823 if ((ttyflags & ISATTY) == 0)
1824 doquit(NULL);
1825 else
1826 putchar('\n');
1827 }
1828 }
1829 while (!docmd(cmd));
1830 #else
1831 if (cmd == NULL)
1832 cmd = xmalloc(BUFSIZ);
1833 do
1834 {
1835 fputs("$>", stdout);
1836 fflush(stdout);
1837 if (fgets(cmd, BUFSIZ - 1, stdin) == NULL)
1838 {
1839 if ((ttyflags & ISATTY) == 0)
1840 doquit(NULL);
1841 else
1842 {
1843 putchar('\n');
1844 cmd[0] = 0;
1845 }
1846 }
1847 }
1848 while (!docmd(cmd));
1849 #endif
1850 ttyraw();
1851 }
1852