1 /* BDOS emulation */
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <ctype.h>
6 #include <dirent.h>
7 #include <sys/stat.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include "defs.h"
12 #include "vt.h"
13 
14 #define BIOS 0xFE00
15 #define DPH0 (BIOS + 0x0036)
16 #define DPB0 (DPH0 + 0x0010)
17 #define DIRBUF 0xff80
18 #define CPMLIBDIR "./"
19 static int storedfps = 0;
20 unsigned short usercode = 0x00;
21 int restricted_mode = 0;
22 int silent_exit = 0;
23 char *stuff_cmd = 0;
24 int trace_bdos = 0;
25 
rdcmdline(z80info * z80,int max,int ctrl_c_enable)26 char *rdcmdline(z80info *z80, int max, int ctrl_c_enable)
27 {
28     int i, c;
29     static char s[259];
30 
31     fflush(stdout);
32     max &= 0xff;
33     i = 1;      /* number of next character */
34 
35     if (stuff_cmd) {
36     	/* Kill prompt */
37     	vt52('\b');
38     	vt52(' ');
39     	vt52('\b');
40     	vt52('\b');
41     	vt52(' ');
42     	vt52('\b');
43     	strcpy(s + i, stuff_cmd);
44     	/* printf("'%s'\n", stuff_cmd); */
45     	i = 1 + strlen(s + i);
46     	stuff_cmd = 0;
47     	silent_exit = 1;
48     	goto hit_rtn;
49     }
50 
51 loop:
52     c = kget(0);
53     if (c < ' ' || c == 0x7f) {
54         switch (c) {
55 	case 3:
56 	    if (ctrl_c_enable) {
57 		vt52('^');
58 		vt52('C');
59 		z80->regpc = BIOS+3;
60 		s[0] = 0;
61 		return s;
62 	    }
63 	    break;
64         case 8:
65 	case 0x7f:
66 	    if (i > 1) {
67 		--i;
68 		vt52('\b');
69 		vt52(' ');
70 		vt52('\b');
71 		fflush(stdout);
72 	    }
73 	    break;
74         case '\n':
75         case '\r':
76 	    hit_rtn:
77 	    s[0] = i-1;
78 	    s[i] = 0;
79 	    if (!strcmp(s + 1, "bye")) {
80 	    	printf("\r\n");
81 	    	finish(z80);
82 	    }
83 	    if (i <= max)
84 		s[i] = '\r';
85 	    return s;
86         }
87         goto loop;
88     } else if (i <= max) {
89         s[i++] = c;
90         vt52(c);
91 	fflush(stdout);
92     }
93     goto loop;
94 }
95 
96 
97 #if 0
98 static struct FCB {
99     char drive;
100     char name[11];
101     char data[24];
102 } samplefcb;
103 #endif
104 
FCB_to_filename(unsigned char * p,char * name)105 static void FCB_to_filename(unsigned char *p, char *name) {
106     int i;
107     char *org = name;
108     /* strcpy(name, "test/");
109        name += 5; */
110     for (i = 0; i < 8; ++i)
111 	if (p[i+1] != ' ')
112 	    *name++ = tolower(p[i+1]);
113     if (p[9] != ' ') {
114 	*name++ = '.';
115 	for (i = 0; i < 3; ++i)
116 	    if (p[i+9] != ' ')
117 		*name++ = tolower(p[i+9]);
118     }
119     *name = '\0';
120     if (trace_bdos)
121     	printf("File name is %s\r\n", org);
122 }
123 
FCB_to_ufilename(unsigned char * p,char * name)124 static void FCB_to_ufilename(unsigned char *p, char *name) {
125     int i;
126     char *org = name;
127     /* strcpy(name, "test/");
128        name += 5; */
129     for (i = 0; i < 8; ++i)
130 	if (p[i+1] != ' ')
131 	    *name++ = toupper(p[i+1]);
132     if (p[9] != ' ') {
133 	*name++ = '.';
134 	for (i = 0; i < 3; ++i)
135 	    if (p[i+9] != ' ')
136 		*name++ = toupper(p[i+9]);
137     }
138     *name = '\0';
139     if (trace_bdos)
140     	printf("File name is %s\r\n", org);
141 }
142 
143 static struct stfps {
144     FILE *fp;
145     unsigned where;
146     char name[12];
147 } stfps[100];
148 
storefp(z80info * z80,FILE * fp,unsigned where)149 static void storefp(z80info *z80, FILE *fp, unsigned where) {
150     int i;
151     int ind = -1;
152     for (i = 0; i < storedfps; ++i)
153 	if (stfps[i].where == 0xffffU)
154 	    ind = i;
155 	else if (stfps[i].where == where) {
156 	    ind = i;
157 	    goto putfp;
158 	}
159     if (ind < 0) {
160 	if (++storedfps > 100) {
161 	    fprintf(stderr, "out of fp stores!\n");
162             resetterm();
163 	    exit(1);
164 	}
165 	ind = storedfps - 1;
166     }
167     stfps[ind].where = where;
168  putfp:
169     stfps[ind].fp = fp;
170     memcpy(stfps[ind].name, z80->mem+z80->regde+1, 11);
171     stfps[ind].name[11] = '\0';
172 }
173 
174 // Lookup an FCB to find the host file.
175 
lookfp(z80info * z80,unsigned where)176 static FILE *lookfp(z80info *z80, unsigned where) {
177     int i;
178     for (i = 0; i < storedfps; ++i)
179 	if (stfps[i].where == where)
180             if (memcmp(stfps[i].name, z80->mem+z80->regde+1, 11) == 0)
181 	    return stfps[i].fp;
182     /* fcb not found. maybe it has been moved? */
183     for (i = 0; i < storedfps; ++i)
184 	if (stfps[i].where != 0xffffU &&
185 	    !memcmp(z80->mem+z80->regde+1, stfps[i].name, 11)) {
186 	    stfps[i].where = where;	/* moved FCB */
187 	    return stfps[i].fp;
188 	}
189     return NULL;
190 }
191 
192 // Report an error finding an FCB.
193 
fcberr(z80info * z80,unsigned where)194 static void fcberr(z80info *z80, unsigned where) {
195     int i;
196 
197     fprintf(stderr, "error: cannot find fp entry for FCB at %04x"
198 	    " fctn %d, FCB named %s\n", where, z80->regbc & 0xff,
199 	    z80->mem+where+1);
200     for (i = 0; i < storedfps; ++i)
201 	if (stfps[i].where != 0xffffU)
202 	    printf("%s %04x\n", stfps[i].name, stfps[i].where);
203     resetterm();
204     exit(1);
205 }
206 
207 // Get the host file for an FCB when it should be open.
208 
getfp(z80info * z80,unsigned where)209 static FILE *getfp(z80info *z80, unsigned where) {
210     FILE *fp;
211 
212     if (!(fp = lookfp(z80, where)))
213         fcberr(z80, where);
214     return fp;
215 }
216 
delfp(z80info * z80,unsigned where)217 static void delfp(z80info *z80, unsigned where) {
218     int i;
219     for (i = 0; i < storedfps; ++i)
220 	if (stfps[i].where == where) {
221 	    stfps[i].where = 0xffffU;
222 	    return;
223 	}
224     fcberr(z80, where);
225 }
226 
227 /* FCB fields */
228 #define FCB_DR 0
229 #define FCB_F1 1
230 #define FCB_T1 9
231 #define FCB_EX 12
232 #define FCB_S1 13
233 #define FCB_S2 14
234 #define FCB_RC 15
235 #define FCB_CR 32
236 #define FCB_R0 33
237 #define FCB_R1 34
238 #define FCB_R2 35
239 
240 /* S2: only low 6 bits have extent number: upper bits are flags.. */
241 /* Upper bit: means file is open? */
242 
243 /* Support 8MB files */
244 
245 #define ADDRESS  (((long)z80->mem[z80->regde+FCB_R0] + \
246 		    (long)z80->mem[z80->regde+FCB_R1] * 256) * 128L)
247 /* (long)z80->mem[z80->regde+35] * 65536L; */
248 
249 
250 /* For sequential access, the record number is also in the FCB:
251  *    EX + 32 * S2 is extent number (0 .. 8191).  Each extent has 16 blocks.  Each block is 1K.
252  *          extent number = (file offset) / 16384
253  *
254  *    RC  has number of records in current extent: (0 .. 128)
255  *          maybe just set to 128?
256  *          otherwise:
257  *
258  *    CR  is current record offset within current extent: (0 .. 127)
259  *          ((file offset) % 16384) / 128
260  */
261 
262 /* Current extent number */
263 #define SEQ_EXT  ((long)z80->mem[DE + FCB_EX] + 32L * (long)(0x3F & z80->mem[DE + FCB_S2]))
264 
265 /* Current byte offset */
266 #define SEQ_ADDRESS  (16384L * SEQ_EXT + 128L * (long)z80->mem[DE + FCB_CR])
267 
268 /* Convert offset to CR */
269 #define SEQ_CR(n) (((n) % 16384) / 128)
270 
271 /* Convert offset to extent number */
272 #define SEQ_EXTENT(n) ((n) / 16384)
273 
274 /* Convert offset to low byte of extent number */
275 #define SEQ_EX(n) (SEQ_EXTENT(n) % 32)
276 
277 /* Convert offset to high byte of extent number */
278 #define SEQ_S2(n) (SEQ_EXTENT(n) / 32)
279 
280 static DIR *dp = NULL;
281 static unsigned sfn = 0;
282 
bdos_decode(int n)283 char *bdos_decode(int n)
284 {
285 	switch (n) {
286 	    case  0: return "System Reset";
287 	    case 1: return "Console Input";
288 	    case 2: return "Console Output";
289 	    case 3: return "Reader input";
290 	    case 4: return "Punch output";
291 	    case 5: return "List output";
292 	    case 6: return "direct I/O";
293 	    case 7: return "get I/O byte";
294 	    case 8: return "set I/O byte";
295 	    case 9: return "Print String";
296 	    case 10: return "Read Command Line";
297 	    case 11: return "Console Status";
298 	    case 12: return "Return Version Number";
299 	    case 13: return "reset disk system";
300 	    case 14: return "select disk";
301 	    case 15: return "open file";
302 	    case 16: return "close file";
303 	    case 17: return "search for first";
304 	    case 18: return "search for next";
305 	    case 19: return "delete file (no wildcards yet)";
306 	    case 20: return "read sequential";
307 	    case 21: return "write sequential";
308 	    case 22: return "make file";
309 	    case 23: return "rename file";
310 	    case 24: return "return login vector";
311 	    case 25: return "return current disk";
312 	    case 26: return "Set DMA Address";
313 	    case 27: return "Get alloc addr";
314 	    case 28: return "Set r/o vector";
315 	    case 29: return "return r/o vector";
316 	    case 30: return "Set file attributes";
317 	    case 31: return "get disk parameters";
318 	    case 32: return "Get/Set User Code";
319 	    case 33: return "read random record";
320 	    case 34: return "write random record";
321 	    case 35: return "compute file size";
322 	    case 36: return "set random record";
323 	    case 41:
324 	    default: return "unknown";
325 	}
326 }
327 
328 /* True if DE points to an FCB for a given BDOS call */
329 
bdos_fcb(int n)330 int bdos_fcb(int n)
331 {
332 	switch (n) {
333 	    case 15: return 1; // "open file";
334 	    case 16: return 1; // "close file";
335 	    case 17: return 1; // "search for first";
336 	    case 18: return 1; // "search for next";
337 	    case 19: return 1; // "delete file (no wildcards yet)";
338 	    case 20: return 1; // "read sequential";
339 	    case 21: return 1; // "write sequential";
340 	    case 22: return 1; // "make file";
341 	    case 23: return 1; // "rename file";
342 	    case 30: return 1; // set attribute
343 	    case 33: return 1; // "read random record";
344 	    case 34: return 1; // "write random record";
345 	    case 35: return 1; // "compute file size";
346 	    case 36: return 1; // "set random record";
347 	    default: return 0;
348 	}
349 }
350 
bdos_fcb_dump(z80info * z80)351 void bdos_fcb_dump(z80info *z80)
352 {
353 	char buf[80];
354 	printf("FCB %x: ", DE);
355 	buf[0] = (0x7F & z80->mem[DE + 1]);
356 	buf[1] = (0x7F & z80->mem[DE + 2]);
357 	buf[2] = (0x7F & z80->mem[DE + 3]);
358 	buf[3] = (0x7F & z80->mem[DE + 4]);
359 	buf[4] = (0x7F & z80->mem[DE + 5]);
360 	buf[5] = (0x7F & z80->mem[DE + 6]);
361 	buf[6] = (0x7F & z80->mem[DE + 7]);
362 	buf[7] = (0x7F & z80->mem[DE + 8]);
363 	buf[8] = '.';
364 	buf[9] = (0x7F & z80->mem[DE + 9]);
365 	buf[10] = (0x7F & z80->mem[DE + 10]);
366 	buf[11] = (0x7F & z80->mem[DE + 11]);
367 	buf[12] = 0;
368 	printf("DR=%x F='%s' EX=%x S1=%x S2=%x RC=%x CR=%x R0=%x R1=%x R2=%x\n",
369 	       z80->mem[DE + 0], buf, z80->mem[DE + 12], z80->mem[DE + 13],
370 	       z80->mem[DE + 14], z80->mem[DE + 15], z80->mem[DE + 32], z80->mem[DE + 33],
371 	       z80->mem[DE + 34], z80->mem[DE + 35]);
372 }
373 
374 /* Get count of records in current extent */
375 
fixrc(z80info * z80,FILE * fp)376 int fixrc(z80info *z80, FILE *fp)
377 {
378     struct stat stbuf;
379     unsigned long size;
380     unsigned long full;
381     unsigned long ext;
382     /* Get file size */
383     if (fstat(fileno(fp), &stbuf) || !S_ISREG(stbuf.st_mode)) {
384         return -1;
385     }
386 
387     size = (stbuf.st_size + 127) >> 7; /* number of records in file */
388 
389     full = size - (size % 128); /* record number of first partially full extent */
390     ext = SEQ_EXT * 128; /* record number of current extent */
391 
392     if (ext < full)
393         /* Current extent is full */
394         z80->mem[DE + FCB_RC] = 128;
395     else if (ext > full)
396         /* We are pointing past the end */
397         z80->mem[DE + FCB_RC] = 0;
398     else
399         /* We are pointing to a partial extent */
400         z80->mem[DE + FCB_RC] = size - full;
401 
402     return 0;
403 }
404 
405 /* emulation of BDOS calls */
406 
check_BDOS_hook(z80info * z80)407 void check_BDOS_hook(z80info *z80) {
408     int i;
409     char name[32];
410     char name2[32];
411     FILE *fp;
412     char *s, *t;
413     const char *mode;
414     if (trace_bdos)
415     {
416         printf("\r\nbdos %d %s (AF=%04x BC=%04x DE=%04x HL =%04x SP=%04x STACK=", C, bdos_decode(C), AF, BC, DE, HL, SP);
417 	for (i = 0; i < 8; ++i)
418 	    printf(" %4x", z80->mem[SP + 2*i]
419 		   + 256 * z80->mem[SP + 2*i + 1]);
420 	printf(")\r\n");
421     }
422     switch (C) {
423     case  0:    /* System Reset */
424 	warmboot(z80);
425 	return;
426 #if 0
427 	for (i = 0; i < 0x1600; ++i)
428 	    z80->mem[i+BIOS-0x1600] = cpmsys[i];
429 	BC = 0;
430 	PC = BIOS-0x1600+3;
431 	SP = 0x80;
432 #endif
433 	break;
434     case 1:     /* Console Input */
435 	HL = kget(0);
436 	B = H; A = L;
437 	if (A < ' ') {
438 	    switch(A) {
439 	    case '\r':
440 	    case '\n':
441 	    case '\t':
442 		vt52(A);
443 		break;
444 	    default:
445 		vt52('^');
446 		vt52((A & 0xff)+'@');
447 		if (A == 3) {	/* ctrl-C pressed */
448 		    /* PC = BIOS+3;
449 		       check_BIOS_hook(); */
450 		    warmboot(z80);
451 		    return;
452 		}
453 	    }
454 	} else {
455 	    vt52(A);
456 	}
457 	break;
458     case 2:     /* Console Output */
459 	vt52(0x7F & E);
460 	HL = 0;
461         B = H; A = L;
462 	break;
463     case 6:     /* direct I/O */
464 	switch (E) {
465 	case 0xff:  if (!constat()) {
466 	    HL = 0;
467             B = H; A = L;
468 	    F = 0;
469 	    break;
470 	}
471 	case 0xfd:  HL = kget(0);
472             B = H; A = L;
473 	    F = 0;
474 	    break;
475 	case 0xfe:  HL = constat() ? 0xff : 0;
476             B = H; A = L;
477 	    F = 0;
478 	    break;
479 	default:    vt52(0x7F & E);
480             HL = 0;
481             B = H; A = L;
482 	}
483 	break;
484     case 9:	/* Print String */
485 	s = (char *)(z80->mem + DE);
486 	while (*s != '$')
487 	    vt52(0x7F & *s++);
488         HL = 0;
489         B = H; A = L;
490 	break;
491     case 10:    /* Read Command Line */
492 	s = rdcmdline(z80, *(unsigned char *)(t = (char *)(z80->mem + DE)), 1);
493 	if (PC == BIOS+3) { 	/* ctrl-C pressed */
494 	    /* check_BIOS_hook(); */		/* execute WBOOT */
495 	    warmboot(z80);
496 	    return;
497 	}
498 	++t;
499 	for (i = 0; i <= *(unsigned char *)s; ++i)
500 	    t[i] = s[i];
501         HL = 0;
502         B = H; A = L;
503 	break;
504     case 12:    /* Return Version Number */
505 	HL = 0x22; /* Emulate CP/M 2.2 */
506         B = H; A = L;
507 	F = 0;
508 	break;
509     case 26:    /* Set DMA Address */
510 	z80->dma = DE;
511 	HL = 0;
512         B = H; A = L;
513 	break;
514     case 32:    /* Get/Set User Code */
515 	if (E == 0xff) {  /* Get Code */
516 	    HL = usercode;
517             B = H; A = L;
518 	} else {
519 	    usercode = E;
520             HL = 0; /* Or does it get usercode? */
521             B = H; A = L;
522         }
523 	break;
524 
525 	/* dunno if these are correct */
526 
527     case 11:	/* Console Status */
528 	HL = (constat() ? 0xff : 0x00);
529         B = H; A = L;
530 	F = 0;
531 	break;
532 
533     case 13:	/* reset disk system */
534 	/* storedfps = 0; */	/* WS crashes then */
535 	HL = 0;
536         B = H; A = L;
537 	if (dp)
538 	    closedir(dp);
539 	{   struct dirent *de;
540             if ((dp = opendir("."))) {
541                 while ((de = readdir(dp))) {
542                     if (strchr(de->d_name, '$')) {
543                         A = 0xff;
544                         break;
545                     }
546                 }
547                 closedir(dp);
548             }
549         }
550 	dp = NULL;
551 	z80->dma = 0x80;
552 	/* select only A:, all r/w */
553 	break;
554     case 14:	/* select disk */
555 	HL = 0;
556         B = H; A = L;
557 	break;
558     case 15:	/* open file */
559 	mode = "r+b";
560     fileio:
561         /* check if the file is already open */
562         if (!(fp = lookfp(z80, DE))) {
563             /* not already open - try lowercase */
564             FCB_to_filename(z80->mem+DE, name);
565 	if (!(fp = fopen(name, mode))) {
566 	    FCB_to_ufilename(z80->mem+DE, name); /* Try all uppercase instead */
567             if (!(fp = fopen(name, mode))) {
568 	            FCB_to_filename(z80->mem+DE, name);
569 		    if (*mode == 'r') {
570 			char ss[50];
571 			snprintf(ss, sizeof(ss), "%s/%s", CPMLIBDIR, name);
572 			fp = fopen(ss, mode);
573 			if (!fp)
574 			  fp = fopen(ss, "rb");
575 		    }
576 		    if (!fp) {
577 			/* still no success */
578 			HL = 0xFF;
579                         B = H; A = L;
580 			F = 0;
581 			break;
582 		    }
583             }
584             }
585             /* where to store fp? */
586             storefp(z80, fp, DE);
587 	}
588 	/* success */
589 
590 	/* memset(z80->mem + DE + 12, 0, 33-12); */
591 
592 	/* User has to set EX, S2, and CR: don't change these- some set them to non-zero */
593 	z80->mem[DE + FCB_S1] = 0;
594 	/* memset(z80->mem + DE + 16, 0, 16); */ /* Clear D0-Dn */
595 
596 	/* Should we clear R0 - R2? Nope: then we overlap the following area. */
597 	/* memset(z80->mem + DE + 33, 0, 3); */
598 
599 	/* We need to set high bit of S2: means file is open? */
600 	z80->mem[DE + FCB_S2] |= 0x80;
601 
602 	z80->mem[DE + FCB_RC] = 0;	/* rc field of FCB */
603 
604 	if (fixrc(z80, fp)) { /* Not a real file? */
605 	    HL = 0xFF;
606             B = H; A = L;
607 	    F = 0;
608 	    fclose(fp);
609             delfp(z80, DE);
610 	    break;
611 	}
612 	HL = 0;
613         B = H; A = L;
614 	F = 0;
615 	/* printf("opening file %s\n", name); */
616 	break;
617     case 16:	/* close file */
618         {
619             size_t host_size, host_exts;
620 	    fp = getfp(z80, DE);
621             fseek(fp, 0, SEEK_END);
622             host_size = ftell(fp);
623             host_exts = SEQ_EXTENT(host_size);
624             if (host_exts == SEQ_EXT) {
625                 /* this is the last extent of the file so we allow the
626                    CP/M program to truncate it by reducing RC */
627                 if (z80->mem[DE + FCB_RC] < SEQ_CR(host_size)) {
628                     host_size = (16384L * SEQ_EXT + 128L * (long)z80->mem[DE + FCB_RC]);
629                     ftruncate(fileno(fp), host_size);
630                 }
631             }
632 	delfp(z80, DE);
633 	fclose(fp);
634             z80->mem[DE + FCB_S2] &= 0x7F; /* Clear high bit: indicates closed */
635 	HL = 0;
636         B = H; A = L;
637 	/* printf("close file\n"); */
638         }
639 	break;
640     case 17:	/* search for first */
641 	if (dp)
642 	    closedir(dp);
643 	if (!(dp = opendir("."))) {
644 	    fprintf(stderr, "opendir fails\n");
645             resetterm();
646 	    exit(1);
647 	}
648 	sfn = DE;
649 	/* fall through */
650     case 18:	/* search for next */
651 	if (!dp)
652 	    goto retbad;
653 	{   struct dirent *de;
654 	    unsigned char *p;
655 	    const char *sr;
656 	nocpmname:
657 	    if (!(de = readdir(dp))) {
658 		closedir(dp);
659 		dp = NULL;
660 	    retbad:
661 	        HL = 0xff;
662                 B = H; A = L;
663 		F = 0;
664 		break;
665 	    }
666 	    /* printf("\r\nlooking at %s\r\n", de->d_name); */
667 	    /* compare data */
668 	    memset(p = z80->mem+z80->dma, 0, 128);	/* dmaaddr instead of DIRBUF!! */
669 	    if (*de->d_name == '.')
670 		goto nocpmname;
671 	    if (strchr(sr = de->d_name, '.')) {
672 		if (strlen(de->d_name) > 12)	/* POSIX: namlen */
673 		    goto nocpmname;
674 	    } else if (strlen(de->d_name) > 8)
675 		    goto nocpmname;
676 	    /* seems OK */
677 	    for (i = 0; i < 8; ++i)
678 		if (*sr != '.' && *sr) {
679 		    *++p = toupper(*(unsigned char *)sr); sr++;
680 		} else
681 		    *++p = ' ';
682 	    /* skip dot */
683 	    while (*sr && *sr != '.')
684 		++sr;
685 	    while (*sr == '.')
686 		++sr;
687 	    for (i = 0; i < 3; ++i)
688 		if (*sr != '.' && *sr) {
689 		    *++p = toupper(*(unsigned char *)sr); sr++;
690 		} else
691 		    *++p = ' ';
692 	    /* OK, fcb block is filled */
693 	    /* match name */
694 	    p -= 11;
695 	    sr = (char *)(z80->mem + sfn);
696 	    for (i = 1; i <= 12; ++i)
697 		if (sr[i] != '?' && sr[i] != p[i])
698 		    goto nocpmname;
699 	    /* yup, it matches */
700 	    HL = 0x00;	/* always at pos 0 */
701             B = H; A = L;
702 	    F = 0;
703 	    p[32] = p[64] = p[96] = 0xe5;
704 	}
705 	break;
706     case 19:	/* delete file (no wildcards yet) */
707 	FCB_to_filename(z80->mem + DE, name);
708 	unlink(name);
709 	HL = 0;
710         B = H; A = L;
711 	break;
712     case 20:	/* read sequential */
713 	fp = getfp(z80, DE);
714     readseq:
715 	if (!fseek(fp, SEQ_ADDRESS, SEEK_SET) && ((i = fread(z80->mem+z80->dma, 1, 128, fp)) > 0)) {
716 	    long ofst = ftell(fp) + 127;
717 	    if (i != 128)
718 		memset(z80->mem+z80->dma+i, 0x1a, 128-i);
719 	    z80->mem[DE + FCB_CR] = SEQ_CR(ofst);
720 	    z80->mem[DE + FCB_EX] = SEQ_EX(ofst);
721 	    z80->mem[DE + FCB_S2] = (0x80 | SEQ_S2(ofst));
722 	    fixrc(z80, fp);
723 	    HL = 0x00;
724             B = H; A = L;
725 	} else {
726 	    HL = 0x1;	/* ff => pip error */
727             B = H; A = L;
728 	}
729 	break;
730     case 21:	/* write sequential */
731 	fp = getfp(z80, DE);
732     writeseq:
733 	if (!fseek(fp, SEQ_ADDRESS, SEEK_SET) && fwrite(z80->mem+z80->dma, 1, 128, fp) == 128) {
734 	    long ofst = ftell(fp);
735 	    z80->mem[DE + FCB_CR] = SEQ_CR(ofst);
736 	    z80->mem[DE + FCB_EX] = SEQ_EX(ofst);
737 	    z80->mem[DE + FCB_S2] = (0x80 | SEQ_S2(ofst));
738             fflush(fp);
739 	    fixrc(z80, fp);
740 	    HL = 0x00;
741             B = H; A = L;
742 	} else {
743 	    HL = 0xff;
744             B = H; A = L;
745 	}
746 	break;
747     case 22:	/* make file */
748 	mode = "w+b";
749 	goto fileio;
750     case 23:	/* rename file */
751 	FCB_to_filename(z80->mem + DE, name);
752 	FCB_to_filename(z80->mem + DE + 16, name2);
753 	/* printf("rename %s %s called\n", name, name2); */
754 	rename(name, name2);
755 	HL = 0;
756         B = H; A = L;
757 	break;
758     case 24:	/* return login vector */
759 	HL = 1;	/* only A: online */
760         B = H; A = L;
761 	F = 0;
762 	break;
763     case 25:	/* return current disk */
764 	HL = 0;	/* only A: */
765         B = H; A = L;
766 	F = 0;
767 	break;
768     case 29:	/* return r/o vector */
769 	HL = 0;	/* none r/o */
770         B = H; A = L;
771 	F = 0;
772 	break;
773     case 31:    /* get disk parameters */
774         HL = DPB0;    /* only A: */
775         B = H; A = L;
776         break;
777     case 33:	/* read random record */
778         {
779         long ofst;
780 	fp = getfp(z80, DE);
781 	/* printf("data is %02x %02x %02x\n", z80->mem[z80->regde+33],
782 	       z80->mem[z80->regde+34], z80->mem[z80->regde+35]); */
783 	ofst = ADDRESS;
784         z80->mem[DE + FCB_CR] = SEQ_CR(ofst);
785 	z80->mem[DE + FCB_EX] = SEQ_EX(ofst);
786 	z80->mem[DE + FCB_S2] = (0x80 | SEQ_S2(ofst));
787 	goto readseq;
788 	}
789     case 34:	/* write random record */
790         {
791         long ofst;
792 	fp = getfp(z80, DE);
793 	/* printf("data is %02x %02x %02x\n", z80->mem[z80->regde+33],
794 	       z80->mem[z80->regde+34], z80->mem[z80->regde+35]); */
795 	ofst = ADDRESS;
796         z80->mem[DE + FCB_CR] = SEQ_CR(ofst);
797 	z80->mem[DE + FCB_EX] = SEQ_EX(ofst);
798 	z80->mem[DE + FCB_S2] = (0x80 | SEQ_S2(ofst));
799 	goto writeseq;
800 	}
801     case 35:	/* compute file size */
802 	fp = getfp(z80, DE);
803 	fseek(fp, 0L, SEEK_END);
804 	/* fall through */
805     case 36:	/* set random record */
806 	fp = getfp(z80, DE);
807 	{
808 	    long ofst = ftell(fp) + 127;
809 	    long pos = (ofst >> 7);
810 	    HL = 0x00;	/* dunno, if necessary */
811             B = H; A = L;
812 	    z80->mem[DE + FCB_R0] = pos & 0xff;
813 	    z80->mem[DE + FCB_R1] = pos >> 8;
814 	    z80->mem[DE + FCB_R2] = pos >> 16;
815             z80->mem[DE + FCB_CR] = SEQ_CR(ofst);
816 	    z80->mem[DE + FCB_EX] = SEQ_EX(ofst);
817 	    z80->mem[DE + FCB_S2] = (0x80 | SEQ_S2(ofst));
818 	    fixrc(z80, fp);
819 	}
820 	break;
821     case 41:
822 	for (s = (char *)(z80->mem + DE); *s; ++s)
823 	    *s = tolower(*(unsigned char *)s);
824 	HL = (restricted_mode || chdir((char  *)(z80->mem + DE))) ? 0xff : 0x00;
825         B = H; A = L;
826 	break;
827     default:
828 	printf("\n\nUnrecognized BDOS-Function:\n");
829 	printf("AF=%04x  BC=%04x  DE=%04x  HL=%04x  SP=%04x\nStack =",
830 	       AF, BC, DE, HL, SP);
831 	for (i = 0; i < 8; ++i)
832 	    printf(" %4x", z80->mem[SP + 2*i]
833 		   + 256 * z80->mem[SP + 2*i + 1]);
834 	printf("\r\n");
835 	resetterm();
836 	exit(1);
837     }
838     z80->mem[PC = DIRBUF-1] = 0xc9; /* Return instruction */
839     return;
840 }
841