/*****************************************************************************/
/*									     */
/*									     */
/*	CP/M emulator version 0.1					     */
/*									     */
/*	written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de)		     */
/*	June-1994							     */
/*									     */
/*	This file is distributed under the GNU COPYRIGHT		     */
/*	see COPYRIGHT.GNU for Copyright details				     */
/*									     */
/*									     */
/*****************************************************************************/
#include "cpmemu.h"
#include <ctype.h>
#include <unistd.h>

#define CPMLIBDIR "/usr/local/lib/cpm"

struct z80regs z80regs,
    z80regs0 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
unsigned short dmaaddr = 0x80;
unsigned short usercode = 0x00;
unsigned char z80mem[65536L+6];
static int SPT = 64;

union zeropage {
    struct c {
        uchar jpbyte0;
        ushrt wbootvec;
        uchar iobyte;
        uchar usrdrv;
        uchar jpbyte5;
        ushrt bdosvec;
        uchar fill1[0x54];
        uchar fcb[36];
        uchar dma[128];
    };
    uchar z[256];
} zeropage;

static char zeropage0[8] = {
    0xc3, (BIOS+3) & 0xff, (BIOS+3) >> 8, 0x80, 0x00, 0xc3, BDOS & 0xff, BDOS >> 8 };
static char cpmsys[0x1600];
unsigned bios_dma = 0x80, bios_track = 0, bios_sector = 0;
static FILE *bios_fp = NULL;
static const char *cpmimage;

/* BIOS data locations: */
#define DPH0		(BIOS+0x36U)
#define DPB0		(DPH0+16U)
#define DATA_START	(DPB0+15U)
#define DIRBUF		0xff80U		/* fixed! */


static void make_jumpers(void) {
    int i;
    for (i = 0; i < 18; ++i) {
	/* make jump table at BIOS start. This HAS to be a true jump table, */
	/* since some programs rely on this */
	z80mem[BIOS+3*i] = 0xc3; /* Z80-jump instruction */
	z80mem[BIOS+3*i+1] = DIRBUF - 20 + i;
	z80mem[BIOS+3*i+2] = DIRBUF >> 8;
    }
    z80mem[0x38] = 0xc9;	/* no interrupt */
}
/* just for information: */
static struct dph {
    ushrt xlat, scr1, scr2, scr3;
    ushrt dirbuf;
    ushrt dpb;
    ushrt csv;
    ushrt alv;
} dph0 = { 0, 0, 0, 0, DIRBUF, DPB0, 0, 0 };

void cpm_init(const char *imagefile, const char *systemfile, struct dpb *dpb0) {
    register unsigned i;
    FILE *fp;

    cpmimage = imagefile;
    memset(z80mem+0x100, 0x76, sizeof(z80mem)-0x100);	/* HALT insn */

    if (!(fp = fopen(systemfile, "rb"))) {
	fprintf(stderr, "cpm: cannot open system file \"%s\"\n", systemfile);
	exit(1);
    }
    fread(cpmsys, 0x1600, 1, fp);
    if (!cpmsys[0] && !cpmsys[1] && !cpmsys[2]) {
	memmove(cpmsys, cpmsys+5, 0x806);
	memmove(cpmsys+0x806, cpmsys+0x806+7, 0xe00-13);
    }
    fclose(fp);

    for (i = 0; i <= NBREAKS; ++i)
	breakpoint[i].action = 0;

    for (i = 0; i < 8; ++i)
        zeropage.z[i] = zeropage0[i];
    for (i = 8; i < 256; ++i)
        zeropage.z[i] = 0x76;
    memcpy(z80mem, zeropage.z, 0x100);
    memcpy(z80mem+BIOS-0x1600, cpmsys, 0x1600);

    dpb0->blm = (1 << dpb0->bsh) - 1;
    dpb0->exm = dpb0->blm >> 3;
    if (dpb0->dsm >= 256)
	dpb0->exm >>= 1;
    /* build alloc0, alloc1 bitmasks */
    {   int bits = ((int)dpb0->drm + 1) / ((int)dpb0->blm + 1) / 4;
	if (bits <= 8) {
 	    dpb0->alloc0 = 256 - (256 >> bits);
	    dpb0->alloc1 = 0;
	} else {
 	    dpb0->alloc0 = 255;
	    dpb0->alloc1 = 256 - (256 >> (bits-8));
	}
    }

    SPT = dpb0->spt;
    /* Transfer dpb */
    z80mem[DPB0+ 0] = dpb0->spt & 0xff;
    z80mem[DPB0+ 1] = dpb0->spt >> 8;
    z80mem[DPB0+ 2] = dpb0->bsh;
    z80mem[DPB0+ 3] = dpb0->blm;
    z80mem[DPB0+ 4] = dpb0->exm;
    z80mem[DPB0+ 5] = dpb0->dsm & 0xff;
    z80mem[DPB0+ 6] = dpb0->dsm >> 8;
    z80mem[DPB0+ 7] = dpb0->drm & 0xff;
    z80mem[DPB0+ 8] = dpb0->drm >> 8;
    z80mem[DPB0+ 9] = dpb0->alloc0;
    z80mem[DPB0+10] = dpb0->alloc1;
    z80mem[DPB0+11] = dpb0->cks & 0xff;
    z80mem[DPB0+12] = dpb0->cks >> 8;
    z80mem[DPB0+13] = dpb0->off & 0xff;
    z80mem[DPB0+14] = dpb0->off >> 8;

    i = DATA_START;	/* begin allocating csv & alv */
    dph0.csv = i;
    i += dpb0->cks;
    dph0.alv = i;
    i += (dpb0->dsm+1 + 7) >> 3;
    if (i > DIRBUF-16) {
	fprintf(stderr, "Error configuring BIOS tables: out of memory!\n");
	exit(1);
    }
    /* Transfer dph */
    memcpy(z80mem+DPH0, &dph0, 16);

    make_jumpers();
    z80regs.sp = 0x80;
}

void loadfile(const char *name) {
    FILE *fp;
    unsigned p;

    if (!(fp = fopen(name, "rb"))) {
	fprintf(stderr, "cpm: cannot open file \"%s\"\n", name);
	exit(1);
    }
    p = 256;
    while (fread(z80mem+p, 1, 256, fp) == 256)
	p += 256;
    fclose(fp);
}

void cpminit2(int argc, char *argv[]) {
    z80mem[0x80] = 0;
    z80mem[0x81] = 0x0d;
    z80regs = z80regs0;
    z80regs.pc = BIOS-0x1600;
    z80regs.bc = 0;
    if (argc) {
	/* transfer command line into CCP */
	int i, p;
	p = BIOS - 0x1600 + 8;
	for (i = 0; i < argc; ++i) {
	    char *s;	/* transfer one arg */
	    if (i)
		z80mem[p++] = ' ';
	    for (s = argv[i]; *s; )
		z80mem[p++] = *s++;
	    if (p > BIOS - 0x1600 + 128)
		break;
	}
	z80mem[p] = 0;
	z80mem[BIOS - 0x1600 + 7] = p - (BIOS - 0x1600 + 8);
	{   unsigned char *s;
	    for (s = z80mem + BIOS - 0x1600 + 8; *s; ++s)
	    if (islower(*s))
		*s = toupper(*s);
	}
	silent_exit = 1;
	/* patch CCP return: */
	for (p = BDOS; p > BIOS-0x1600; --p)
	    if (z80mem[p] == 0xcd && z80mem[p+1] == 0x00 && z80mem[p+2] == 0x01) {
		z80mem[p+3] = 0xc3;
		z80mem[p+4] = (unsigned char)(BIOS & 0xffU);
		z80mem[p+5] = (unsigned char)(BIOS>>8);
		break;
	    }
	if (p == BIOS-0x1600) {
	    fprintf(stderr, "FATAL: cannot patch CCP return address\n");
	    exit(1);
	}
    }
}


/* return 0 = invalid (TRAP), 1 = BIOS hook */
extern jmp_buf mainloop;
static int storedfps = 0;

static void open_image(void) {
    if (!(bios_fp = fopen(cpmimage, "r+b"))) {
	fprintf(stderr, "cpm: cannot open disk image \"%s\"\n", cpmimage);
	exit(1);
    }
}


int check_BIOS_hook(void) {
    int i;
    static uchar buffbios[128];
    static int currdsk = 0;

    if (z80regs.pc >= BIOS) {
	switch (z80regs.pc) {
	case BIOS:      /* System Reset (coldboot) */
	case DIRBUF-20:
	    if (!silent_exit)
		printf("\nCp/M BIOS COLDBOOT takes you back to Linux\n");
	    exit(0);
	case BIOS+3:    /* System Reset (warmboot) */
	case DIRBUF-19:
	    if (silent_exit)
		exit(0);
	    storedfps = 0;	/* clean up! */
	    for (i = 0; i < 0x1600; ++i)
		z80mem[i+BIOS-0x1600] = cpmsys[i];
	    for (i = 0; i < 8; ++i)
		z80mem[i] = zeropage.z[i];
	    z80regs.bc = 0;
	    z80regs.pc = BIOS-0x1600+3;
	    z80regs.sp = 0x80;
	    make_jumpers();
	    if (bios_fp)
		fflush(bios_fp);	/* prepare for disk change */
	    return 1;
	case BIOS+6:    /* Console Status */
	case DIRBUF-18:
	    z80regs.af = kbhit() ? 0xff : 0x00;
	ret1:
	    z80mem[z80regs.pc=DIRBUF-1] = 0xc9;
	    return 1;
	case BIOS+9:    /* Console In */
	case DIRBUF-17:
	    z80regs.af = conin();
	    goto ret1;
	case BIOS+12:   /* Console Out */
	case DIRBUF-16:
	    vt52(z80regs.bc & 0xff);
	    goto ret1;
	case BIOS+15:   /* List Out */
	case DIRBUF-15:
	    goto ret1;
	case BIOS+18:   /* Punch Out */
	case DIRBUF-14:
	    goto ret1;
	case BIOS+21:   /* Reader In */
	case DIRBUF-13:
	    z80regs.af = 0;
	    goto ret1;
	case BIOS+24:   /* Home */
	case DIRBUF-12:
	    bios_track = 0;
	    bios_sector = 0;
	    goto ret1;
	case BIOS+27:   /* Seldsk */
	case DIRBUF-11:
	    currdsk = z80regs.bc & 0xff;
	    if (z80regs.bc & 0xff)
		z80regs.hl = 0xffff;
	    else
		z80regs.hl = DPH0;
	    goto ret1;
	case BIOS+30:   /* Settrack */
	case DIRBUF-10:
	    bios_track = z80regs.bc;
	    goto ret1;
	case BIOS+33:   /* Set sector */
	case DIRBUF- 9:
	    bios_sector = z80regs.bc & 0xff;
	    goto ret1;
	case BIOS+36:   /* Set dma */
	case DIRBUF- 8:
	    bios_dma = z80regs.bc;
	    goto ret1;
	case BIOS+39:   /* Read */
	case DIRBUF- 7:
	    if (currdsk) {
		fprintf(stderr, "BDOS Err on %c: Select\n", currdsk+'A');
		exit(1);
	    }
	    if (!bios_fp)
		open_image();
	    fseek(bios_fp, ((long)bios_sector + (long)SPT * (long)bios_track) * 128L, 0);
	    fread(buffbios, 1, 128, bios_fp);
	    for (i = 0; i < 128; ++i)
		z80mem[i + bios_dma] = buffbios[i];
	    z80regs.af = 0;
	    goto ret1;
	case BIOS+42:   /* Write */
	case DIRBUF- 6:
	    if (currdsk) {
		fprintf(stderr, "BDOS Err on %c: Select\n", currdsk+'A');
		exit(1);
	    }
	    if (!bios_fp)
		open_image();
	    fseek(bios_fp, ((long)bios_sector + (long)SPT * (long)bios_track) * 128L, 0);
	    for (i = 0; i < 128; ++i)
		buffbios[i] = z80mem[i + bios_dma];
	    fwrite(buffbios, 1, 128, bios_fp);
	    z80regs.af = 0;
	    goto ret1;
	case BIOS+45:   /* List Status */
	case DIRBUF- 5:
	    z80regs.af = 0;     /* not ready */
	    goto ret1;
	case BIOS+48:   /* Sectran */       /* no sectran */
	case DIRBUF- 4:
	    z80regs.hl = z80regs.bc & 0xff;
	    goto ret1;
	default:
	    printf("Program traps in BIOS:\n");
	    debug = 1;
	    longjmp(mainloop, 1);
	}
    }
    return 0;
}

/* #include "cpmemu.h" */

#if 0
static struct FCB {
    char drive;
    char name[11];
    char data[24];
} samplefcb;
#endif
static void FCB_to_filename(unsigned char *p, char *name) {
    int i;
    /* strcpy(name, "test/");
       name += 5; */
    for (i = 0; i < 8; ++i)
	if (p[i+1] != ' ')
	    *name++ = tolower(p[i+1]);
    if (p[9] != ' ') {
	*name++ = '.';
	for (i = 0; i < 3; ++i)
	    if (p[i+9] != ' ')
		*name++ = tolower(p[i+9]);
    }
    *name = '\0';
}

static struct stfps {
    FILE *fp;
    unsigned where;
    char name[12];
} stfps[100];

static void storefp(FILE *fp, unsigned where) {
    int i;
    int ind = -1;
    for (i = 0; i < storedfps; ++i)
	if (stfps[i].where == 0xffffU)
	    ind = i;
	else if (stfps[i].where == where) {
	    ind = i;
	    goto putfp;
	}
    if (ind < 0) {
	if (++storedfps > 100) {
	    fprintf(stderr, "out of fp stores!\n");
	    exit(1);
	}
	ind = storedfps - 1;
    }
    stfps[ind].where = where;
 putfp:
    stfps[ind].fp = fp;
    memcpy(stfps[ind].name, z80mem+z80regs.de+1, 11);
    stfps[ind].name[11] = '\0';
}
#if 1
static FILE *getfp(unsigned where) {
    int i;
    for (i = 0; i < storedfps; ++i)
	if (stfps[i].where == where) {
	    /* check name? */
	    return stfps[i].fp;
	}
    /* fcb not found. maybe it has been moved? */
    for (i = 0; i < storedfps; ++i)
	if (stfps[i].where != 0xffffU &&
	    !memcmp(z80mem+z80regs.de+1, stfps[i].name, 11)) {
	    stfps[i].where = where;	/* moved FCB */
	    return stfps[i].fp;
	}

    fprintf(stderr, "error: cannot find fp entry for FCB at %04x"
	    " fctn %d, FCB named %s\n", where, z80regs.bc & 0xff,
	    z80mem+where+1);
    for (i = 0; i < storedfps; ++i)
	if (stfps[i].where != 0xffffU)
	    printf("%s %04x\n", stfps[i].name, stfps[i].where);
    exit(1);
}
static void delfp(unsigned where) {
    int i;
    for (i = 0; i < storedfps; ++i)
	if (stfps[i].where == where) {
	    stfps[i].where = 0xffffU;
	    return;
	}
    fprintf(stderr, "error: cannot del fp entry for FCB at %04x\n", where);
    exit(1);
}
#endif



#include <dirent.h>
#include <sys/stat.h>

#define ADDRESS  (((long)z80mem[z80regs.de+33] + \
		    (long)z80mem[z80regs.de+34] * 256) * 128L)
/* (long)z80mem[z80regs.de+35] * 65536L; */

static DIR *dp = NULL;
static unsigned sfn = 0;

/* emulation of BDOS calls */

void check_BDOS_hook(void) {
    int i;
    char name[32];
    char name2[32];
    struct stat stbuf;
    FILE *fp;
    char *s, *t;
    const char *mode;
    switch (z80regs.bc & 0xff) {
    case  0:    /* System Reset */
	if (silent_exit) {
	    /* printf("\nProgram terminates normally (BDOS function 0)\n"); */
	    exit(0);
	}
	for (i = 0; i < 0x1600; ++i)
	    z80mem[i+BIOS-0x1600] = cpmsys[i];
	z80regs.bc = 0;
	z80regs.pc = BIOS-0x1600+3;
	z80regs.sp = 0x80;
	break;
    case 1:     /* Console Input */
	z80regs.af = z80regs.hl = conin();
	if ((z80regs.hl & 0xff) < ' ') {
	    switch(z80regs.hl) {
	    case '\r':
	    case '\n':
	    case '\t':
		putch(z80regs.hl);
		break;
	    default:
		putch('^');
		putch((z80regs.hl & 0xff)+'@');
		if (z80regs.hl == 3) {	/* ctrl-C pressed */
		    z80regs.pc = BIOS+3;
		    check_BIOS_hook();
		    return;
		}
	    }
	} else {
	    vt52(z80regs.hl);
	}
	break;
    case 2:     /* Console Output */
	vt52(z80regs.de & 0xff);
	break;
    case 6:     /* direct I/O */
	switch (z80regs.de & 0xff) {
	case 0xff:  if (!kbhit()) {
	    z80regs.af = z80regs.hl = 0;
	    break;
	}
	case 0xfd:  z80regs.af = z80regs.hl = conin();
	    break;
	case 0xfe:  z80regs.af = z80regs.hl = kbhit() ? 0xff : 0;
	    break;
	default:    vt52(z80regs.de & 0xff);
	}
	break;
    case 9:	/* Print String */
	s = z80mem +z80regs.de;
	while (*s != '$')
	    vt52(*s++);
	break;
    case 10:    /* Read Command Line */
	s = rdcmdline(*(t = z80mem+z80regs.de), 1);
	if (z80regs.pc == BIOS+3) { 	/* ctrl-C pressed */
	    check_BIOS_hook();		/* execute WBOOT */
	    return;
	}
	++t;
	for (i = 0; i <= *s; ++i)
	    t[i] = s[i];
	break;
    case 12:    /* Return Version Number */
	z80regs.af = z80regs.hl = 0x22;      /* emulate Cp/M 2.2 */
	break;
    case 26:    /* Set DMA Address */
	dmaaddr = z80regs.de;
	break;
    case 32:    /* Get/Set User Code */
	if (z80regs.de & 0xff == 0xff)  /* Get Code */
	    z80regs.af = z80regs.hl = usercode;
	else
	    usercode = z80regs.de & 0x0f;
	break;

	/* dunno if these are correct */

    case 11:	/* Console Status */
	z80regs.af = z80regs.hl = kbhit() ? 0xff : 0x00;
	break;

    case 13:	/* reset disk system */
	/* storedfps = 0; */	/* WS crashes then */
	if (dp)
	    closedir(dp);
	dp = NULL;
	dmaaddr = 0x80;
	/* select only A:, all r/w */
	break;
    case 14:	/* select disk */
	break;
    case 15:	/* open file */
	mode = "r+b";
    fileio:
	FCB_to_filename(z80mem+z80regs.de, name);
	if (!(fp = fopen(name, mode))) {
	    if (*mode == 'r') {
		char ss[50];
		sprintf(ss, "%s/%s", CPMLIBDIR, name);
		fp = fopen(ss, "rb");
	    }
	    if (!fp) {
		/* still no success */
		z80regs.af = z80regs.hl = 0xff;
		break;
	    }
	}
	/* success */
	memset(z80mem+z80regs.de+12, 0, 33-12);
	z80mem[z80regs.de+15] = 0;	/* rc field of FCB */
	if (fstat(fileno(fp), &stbuf) || !S_ISREG(stbuf.st_mode)) {
	    z80regs.af = z80regs.hl = 0xff;
	    fclose(fp);
	    break;
	}
	{   unsigned long pos;
	    pos = (stbuf.st_size + 127) >> 7;	/* number of records */
	    if (pos > 128)
		z80mem[z80regs.de+15] = 0x80;
	    else
		z80mem[z80regs.de+15] = pos;
	}
	z80regs.af = z80regs.hl = 0;
	/* where to store fp? */
	storefp(fp, z80regs.de);
	/* printf("opening file %s\n", name); */
	break;
    case 16:	/* close file */
	fp = getfp(z80regs.de);
	delfp(z80regs.de);
	fclose(fp);
	break;
    case 17:	/* search for first */
	if (dp)
	    closedir(dp);
	if (!(dp = opendir("."))) {
	    fprintf(stderr, "opendir fails\n");
	    exit(1);
	}
	sfn = z80regs.de;
	/* fall through */
    case 18:	/* search for next */
	if (!dp)
	    goto retbad;
	{   struct dirent *de;
	    unsigned char *p;
	    const char *sr;
	nocpmname:
	    if (!(de = readdir(dp))) {
		closedir(dp);
		dp = NULL;
	    retbad:
		z80regs.af = z80regs.hl = 0xff;
		break;
	    }
	    /* compare data */
	    memset(p = z80mem+dmaaddr, 0, 128);	/* dmaaddr instead of DIRBUF!! */
	    if (*de->d_name == '.')
		goto nocpmname;
	    if (strchr(sr = de->d_name, '.')) {
		if (de->d_reclen > 12)	/* POSIX: namlen */
		    goto nocpmname;
	    } else if (de->d_reclen > 8)
		    goto nocpmname;
	    /* seems OK */
	    for (i = 0; i < 8; ++i)
		if (*sr != '.' && *sr) {
		    *++p = toupper(*sr); sr++;
		} else
		    *++p = ' ';
	    /* skip dot */
	    while (*sr && *sr != '.')
		++sr;
	    while (*sr == '.')
		++sr;
	    for (i = 0; i < 3; ++i)
		if (*sr != '.' && *sr) {
		    *++p = toupper(*sr); sr++;
		} else
		    *++p = ' ';
	    /* OK, fcb block is filled */
	    /* match name */
	    p -= 11;
	    sr = z80mem+sfn;
	    for (i = 1; i <= 12; ++i)
		if (sr[i] != '?' && sr[i] != p[i])
		    goto nocpmname;
	    /* yup, it matches */
	    z80regs.af = z80regs.hl = 0x00;	/* always at pos 0 */
	    p[32] = p[64] = p[96] = 0xe5;
	}
	break;
    case 19:	/* delete file (no wildcards yet) */
	FCB_to_filename(z80mem+z80regs.de, name);
	unlink(name);
	break;
    case 20:	/* read sequential */
	fp = getfp(z80regs.de);
    readseq:
	if ((i = fread(z80mem+dmaaddr, 1, 128, fp)) > 0) {
	    if (i != 128)
		memset(z80mem+dmaaddr+i, 0x1a, 128-i);
	    z80regs.af = z80regs.hl = 0x00;
	} else
	    z80regs.af = z80regs.hl = 0x1;	/* ff => pip error */
	break;
    case 21:	/* write sequential */
	fp = getfp(z80regs.de);
    writeseq:
	if (fwrite(z80mem+dmaaddr, 1, 128, fp) == 128)
	    z80regs.af = z80regs.hl = 0x00;
	else
	    z80regs.af = z80regs.hl = 0xff;
	break;
    case 22:	/* make file */
	mode = "w+b";
	goto fileio;
    case 23:	/* rename file */
	FCB_to_filename(z80mem+z80regs.de, name);
	FCB_to_filename(z80mem+z80regs.de+16, name2);
	/* printf("rename %s %s called\n", name, name2); */
	rename(name, name2);
	break;
    case 24:	/* return login vector */
	z80regs.af = z80regs.hl = 1;	/* only A: online */
	break;
    case 25:	/* return current disk */
	z80regs.af = z80regs.hl = 0;	/* only A: */
	break;
    case 29:	/* return r/o vector */
	z80regs.af = z80regs.hl = 0;	/* none r/o */
	break;
    case 33:	/* read random record */
	fp = getfp(z80regs.de);
	/* printf("data is %02x %02x %02x\n", z80mem[z80regs.de+33],
	       z80mem[z80regs.de+34], z80mem[z80regs.de+35]); */
	fseek(fp, ADDRESS, SEEK_SET);
	goto readseq;
    case 34:	/* write random record */
	fp = getfp(z80regs.de);
	/* printf("data is %02x %02x %02x\n", z80mem[z80regs.de+33],
	       z80mem[z80regs.de+34], z80mem[z80regs.de+35]); */
	fseek(fp, ADDRESS, SEEK_SET);
	goto writeseq;
    case 35:	/* compute file size */
	fp = getfp(z80regs.de);
	fseek(fp, 0L, SEEK_END);
	/* fall through */
    case 36:	/* set random record */
	fp = getfp(z80regs.de);
	{   long pos;
	    pos = ftell(fp) >> 7;
	    z80regs.af = z80regs.hl = 0x00;	/* dunno, if necessary */
	    z80mem[z80regs.de+21] = pos & 0xff;
	    z80mem[z80regs.de+22] = pos >> 8;
	    z80mem[z80regs.de+23] = pos >> 16;
	}
	break;
    case 41:
	for (s = z80mem+z80regs.de; *s; ++s)
	    *s = tolower(*s);
	z80regs.af = z80regs.hl = 
	    restricted_mode || chdir(z80mem+z80regs.de) ? 0xff : 0x00;
	break;
    default:
	printf("\n\nUnrecognized BDOS-Function:\n");
	printf("AF=%04x  BC=%04x  DE=%04x  HL=%04x  SP=%04x\nStack =",
	       z80regs.af, z80regs.bc, z80regs.de, z80regs.hl, z80regs.sp);
	for (i = 0; i < 8; ++i)
	    printf(" %4x", z80mem[z80regs.sp+2*i]
		   + 256 * z80mem[z80regs.sp+2*i+1]);
	printf("\n");
	exit(1);
    }
    z80mem[z80regs.pc=DIRBUF-1] = 0xc9;
    return;
}
