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