You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

561 lines
10 KiB

#include "zx.h"
#define BDOS_DEF
#include "zxbdos.h"
#include "zxcbdos.h"
#include "zxdbdos.h"
#ifdef __MSDOS__
#include <conio.h>
#endif
#define BCD(x) (((x % 10)+16*(x/10)) & 0xFF)
/* Convert time_t to CP/M day count/hours/minutes */
dword cpmtime(time_t t)
{
long d = (t / 86400) - 2921; /* CP/M day 0 is unix day 2921 */
long h = (t % 86400) / 3600; /* Hour, 0-23 */
long m = (t % 3600) / 60; /* Minute, 0-59 */
return (d | (BCD(h) << 16) | (BCD(m) << 24));
}
byte get_time(cpm_word b)
{
time_t t;
time(&t);
wr32(b, cpmtime(t));
return (BCD(t % 60));
}
/* Functions to access 24-bit & 32-bit words in memory. These are always
little-endian. */
void wr24(word addr, dword v)
{
RAM[addr ] = v & 0xFF;
RAM[addr + 1] = (v >> 8) & 0xFF;
RAM[addr + 2] = (v >> 16) & 0xFF;
}
void wr32(word addr, dword v)
{
RAM[addr ] = v & 0xFF;
RAM[addr + 1] = (v >> 8) & 0xFF;
RAM[addr + 2] = (v >> 16) & 0xFF;
RAM[addr + 3] = (v >> 24) & 0xFF;
}
dword rd24(word addr)
{
register dword rv = RAM[addr + 2];
rv = (rv << 8) | RAM[addr + 1];
rv = (rv << 8) | RAM[addr];
return rv;
}
dword rd32(word addr)
{
register dword rv = RAM[addr + 3];
rv = (rv << 8) | RAM[addr + 2];
rv = (rv << 8) | RAM[addr + 1];
rv = (rv << 8) | RAM[addr];
return rv;
}
#define peekw(addr) ( (((word)(RAM[addr + 1])) << 8) | RAM[addr])
/* Get / set the program return code. We store this in 'C' form: 0 for
success, 1-255 for failure. Translate to/from the CP/M form of:
0x0000-0xFEFF for success
0xFF00-0xFFFE for failure
We also store the actual value so it can be returned
*/
word cpm_errcde(word DE)
{
static word real_err = 0;
if (DE == 0xFFFF) return real_err;
real_err = DE;
if (DE == 0xFF00) cpm_error = 1;
else if (DE > 0xFF00) cpm_error = (DE & 0xFF);
else cpm_error = 0;
return 0;
}
#ifdef USE_CPMGSX
gsx_byte gsxrd(gsx_word addr)
{
return RdZ80(addr);
}
void gsxwr(gsx_word addr, gsx_byte value)
{
WrZ80(addr, value);
}
#endif
#undef bc
#undef de
#undef hl
void setw(byte *l, byte *h, word w)
{
*l = (w & 0xFF);
*h = (w >> 8) & 0xFF;
}
void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
byte *h, byte *l, word *pc, word *ix, word *iy)
{
word de = ((*d) << 8) | *e;
word hl = ((*h) << 8) | *l;
byte *pde = &RAM[de];
byte *pdma = &RAM[cpm_dma];
word temp;
int retv;
Msg("BDOS service invoked: C=%02x DE=%04x\n", *c, de);
switch(*c)
{
case 0:
*pc = 0;
break;
case 1: /* Get a character */
#ifdef USE_CPMIO
retv = cpm_bdos_1();
#else
retv = cin();
#endif
if (retv < 0) *pc = 0;
setw(l, h, retv);
break;
case 2: /* Print a character */
#ifdef USE_CPMIO
if (cpm_bdos_2(*e)) *pc = 0;
#else
cout(*e);
#endif
break;
case 3: /* No auxin */
setw(l, h, 0x1A);
break;
case 4: /* No auxout */
break;
case 5: /* No printer */
break;
case 6: /* Direct console I/O */
retv = cpm_bdos_6(*e);
if (retv < 0) *pc = 0;
setw(l, h, retv);
break;
case 7: /* No auxist */
case 8: /* No auxost */
break;
case 9: /* Print a $-terminated string */
#ifdef USE_CPMIO
if (cpm_bdos_9((char *)pde)) *pc = 0;
# else
for (temp = 0; RAM[de + temp] != '$'; ++temp)
{
cout(RAM[de + temp]);
}
#endif
break;
case 0x0A:
bdos_rdline(de, &(*pc));
break;
case 0x0B: /* Console status */
//*l = *h = 0; /* No keys pressed */
*l = cstat();
*h = 0;
break;
case 0x0C: /* Get CP/M version */
/* For GENCOM's benefit, claim to be v3.1 */
*l = 0x31; /* v3.1 */
//*l = 0x22; /* v2.2 */
*h = 0; /* CP/M, no network */
break;
case 0x0D: /* Re-log discs */
fcb_reset();
break;
case 0x0E: /* Set default drive */
setw(l, h, fcb_drive(*e));
break;
case 0x0F: /* Open using FCB */
setw(l, h, x_fcb_open(pde, pdma));
break;
case 0x10: /* Close using FCB */
setw(l, h, fcb_close(pde));
break;
case 0x11: /* Find first */
setw(l, h, fcb_find1(pde, pdma));
break;
case 0x12:
setw(l, h, fcb_find2(pde, pdma));
break;
case 0x13: /* Delete using FCB */
setw(l, h, fcb_unlink(pde, pdma));
break;
case 0x14: /* Sequential read using FCB */
setw(l, h, fcb_read(pde, pdma));
//Msg("fcb_read L=%02x H=%02x\n", *l, *h);
break;
case 0x15: /* Sequential write using FCB */
setw(l, h, fcb_write(pde, pdma));
break;
case 0x16: /* Create using FCB */
setw(l, h, fcb_creat(pde, pdma));
break;
case 0x17: /* Rename using FCB */
setw(l, h, fcb_rename(pde, pdma));
break;
case 0x18: /* Get login vector */
setw(l, h, fcb_logvec());
break;
case 0x19: /* Get default drive */
setw(l, h, cpm_drive);
break;
case 0x1A: /* Set DMA */
Msg("Set DMA to %04x\n", de);
cpm_dma = de;
break;
case 0x1B: /* Get alloc vector */
fcb_getalv(RAM + 0xFF80, 0x40);
setw(l, h, 0xFF80);
break;
case 0x1C: /* Make disc R/O */
setw(l, h, fcb_rodisk());
break;
case 0x1D: /* Get R/O vector */
setw(l, h, fcb_rovec());
break;
case 0x1E: /* Set attributes */
setw(l, h, fcb_chmod(pde, pdma));
break;
case 0x1F: /* Get DPB */
fcb_getdpb(RAM + 0xFFC0);
setw(l, h, 0xFFC0);
break; /* Whoops. Missed that 'break'. */
case 0x20: /* Get/set uid */
setw(l, h, fcb_user(*e));
break;
case 0x21: /* Read a record */
setw(l, h, fcb_randrd(pde, pdma));
break;
case 0x22: /* Write a record */
setw(l, h, fcb_randwr(pde, pdma));
break;
case 0x23: /* Get file size */
setw(l, h, x_fcb_stat(pde));
break;
case 0x24: /* Get file pointer */
setw(l, h, fcb_tell(pde));
break;
case 0x25:
setw(l, h, fcb_resro(de));
break;
/* MP/M drive access functions, not implemented */
case 0x28: /* Write with 0 fill */
setw(l, h, fcb_randwz(pde, pdma));
break;
/* MP/M record locking functions, not implemented */
case 0x2C: /* Set no. of records to read/write */
setw(l, h, fcb_multirec(*e));
break;
case 0x2D: /* Set error mode */
err_mode = *e;
break;
case 0x2E:
setw(l, h, fcb_dfree(*e, pdma));
break; /* Whoops. Missed that 'break'. */
/* 0x2F: Chain */
case 0x30:
setw(l, h, fcb_sync(*e));
break;
case 0x31:
if (pde[1] == 0xFE)
{
RAM[0xFE9C + *pde] = pde[2];
RAM[0xFE9D + *pde] = pde[3];
}
else if (RAM[hl + 1] == 0xFF)
{
RAM[0xFE9C + *pde] = pde[2];
}
else
{
*l = RAM[0xFE9C + *pde];
*h = RAM[0xFE9D + *pde];
}
break;
case 0x32:
temp = *ix;
*ix = 3 * (pde[0] + 1);
*a = pde[1];
*c = pde[2];
*b = pde[3];
*e = pde[4];
*d = pde[5];
*l = pde[6];
*h = pde[7];
cpmbios(a,b,c,d,e,f,h,l,pc,ix,iy);
*ix = temp;
break;
case 0x3C: /* Communicate with RSX */
*h = *l = 0;
break;
case 0x62: /* Purge */
setw(l, h, fcb_purge());
break;
case 0x63: /* Truncate file */
setw(l, h, fcb_trunc(pde, pdma));
break;
case 0x64: /* Set label */
setw(l, h, fcb_setlbl(pde, pdma));
break;
case 0x65: /* Get label byte */
setw(l, h, fcb_getlbl(*e));
break;
case 0x66: /* Get file date */
setw(l, h, fcb_date(pde));
break;
case 0x67: /* Set password */
setw(l, h, fcb_setpwd(pde, pdma));
break;
case 0x68: /* Set time of day */
/* Not advisable to let an emulator play with the clock */
break;
case 0x69: /* Get time of day */
setw(l, h, get_time(de));
break;
case 0x6A: /* Set default password */
setw(l, h, fcb_defpwd(pde));
break;
case 0x6B: /* Get serial number */
memcpy(pde, SERIAL, 6);
break;
case 0x6C: /* 0.03 set error code */
setw(l, h, cpm_errcde(de));
break;
#ifdef USE_CPMIO
case 0x6D: /* Set/get console mode */
setw(l, h, cpm_bdos_109(de));
break;
case 0x6E: /* Set/get string delimiter */
setw(l, h, cpm_bdos_110(*e));
break;
case 0x6F: /* Send fixed length string to screen */
if (cpm_bdos_111((char *)RAM + peekw(de),
peekw(de + 2)))
*pc = 0;
break;
case 0x70: /* Send fixed length string to printer */
break;
/* 0x71: Strange PCP/M function */
#else
case 0x6D: /* Set/get console mode */
setw(l, h, 0);
break;
#endif
#ifdef USE_CPMGSX
case 0x73: /* GSX */
setw(l, h, gsx80(gsxrd, gsxwr, de));
break;
#endif
case 0x74: /* Set date stamp */
setw(l, h, fcb_sdate(pde, pdma));
break;
case 0x98: /* Parse filename */
setw(l, h, fcb_parse((char *)RAM + peekw(de),
(byte *)RAM + peekw(de + 2)));
break;
default:
#ifdef USE_CPMIO
cpm_scr_unit();
#endif
#ifdef USE_CPMGSX
gsx_deinit();
#endif
fprintf(stderr,"%s: Unsupported BDOS call %d\n", progname,
(int)(*c));
dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy);
zx_exit(1);
break;
}
*a = *l;
*b = *h;
Msg("BDOS service completion.\n");
}
void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f,
byte *h, byte *l, word *pc, word *ix, word *iy)
{
int func = (((*ix) & 0xFF) / 3) - 1;
Msg("BIOS service invoked: func=%02x\n", func);
switch(func) /* BIOS function */
{
case 1:
zx_exit(zx_term()); /* Program termination */
break;
case 2: /* CONST */
#ifdef USE_CPMIO
*a = cpm_const();
#else
*a = cpm_bdos_6(0xFE);
#endif
break;
case 3: /* CONIN */
#ifdef USE_CPMIO
*a = cpm_conin();
#else
*a = cpm_bdos_6(0xFD);
#endif
break;
case 4: /* CONOUT */
#ifdef USE_CPMIO
cpm_conout(*c);
#else
cpm_bdos_6(*c);
#endif
break;
case 20: /* DEVTBL */
setw(l, h, 0xFFFF);
break;
case 22: /* DRVTBL */
setw(l, h, 0xFFFF);
break;
case 26: /* TIME */
RAM[0xFEF8] = get_time(0xFEF4);
break;
case 30: /* USERF!!! */
#ifdef USE_CPMIO
cpm_bdos_110('$');
cpm_bdos_9("This program has attempted to call USERF, "
"which is not implemented\r\n$");
#else
printf("This program has attempted to call USERF, which "
"is not implemented.\n");
#endif
zx_term();
zx_exit(1);
break;
default:
#ifdef USE_CPMIO
cpm_scr_unit();
#endif
#ifdef USE_CPMGSX
gsx_deinit();
#endif
fprintf(stderr,"%s: Unsupported BIOS call %d\n", progname, func);
dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy);
zx_exit(1);
}
Msg("BIOS service completion.\n");
}