Files
Digital-Research-Source-Code/CPM OPERATING SYSTEMS/CPM 68K/1.0X SOURCES/v101/bios/bios.c
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

1343 lines
30 KiB
C

/*=======================================================================*/
/*/---------------------------------------------------------------------\*/
/*| |*/
/*| CP/M-68K(tm) BIOS for the EXORMACS |*/
/*| |*/
/*| Copyright 1983, Digital Research. |*/
/*| |*/
/*| Modified 9/ 7/82 wbt |*/
/*| 10/ 5/82 wbt |*/
/*| 12/15/82 wbt |*/
/*| 12/22/82 wbt |*/
/*| 1/28/83 wbt |*/
/*| |*/
/*\---------------------------------------------------------------------/*/
/*=======================================================================*/
#include "biostype.h" /* defines LOADER : 0-> normal bios, 1->loader bios */
/* also defines CTLTYPE 0 -> Universal Disk Cntrlr */
/* 1 -> Floppy Disk Controller */
/* MEMDSK: 0 -> no memory disk */
/* 4 -> 384K memory disk */
#include "biostyps.h" /* defines portable variable types */
char copyright[] = "Copyright 1983, Digital Research";
struct memb { BYTE byte; }; /* use for peeking and poking memory */
struct memw { WORD word; };
struct meml { LONG lword;};
/************************************************************************/
/* I/O Device Definitions */
/************************************************************************/
/************************************************************************/
/* Define the two serial ports on the DEBUG board */
/************************************************************************/
/* Port Addresses */
#define PORT1 0xFFEE011 /* console port */
#define PORT2 0xFFEE015 /* debug port */
/* Port Offsets */
#define PORTCTRL 0 /* Control Register */
#define PORTSTAT 0 /* Status Register */
#define PORTRDR 2 /* Read Data Register */
#define PORTTDR 2 /* Write Data Register */
/* Port Control Functions */
#define PORTRSET 3 /* Port Reset */
#define PORTINIT 0x11 /* Port Initialize */
/* Port Status Values */
#define PORTRDRF 1 /* Read Data Register Full */
#define PORTTDRE 2 /* Write Data Register Empty */
/************************************************************************/
/* Define Disk I/O Addresses and Related Constants */
/************************************************************************/
#define DSKIPC 0xFF0000 /* IPC Base Address */
#define DSKINTV 0x3FC /* Address of Disk Interrupt Vector */
#define INTTOIPC 0xD /* offsets in mem mapped io area */
#define RSTTOIPC 0xF
#define MSGTOIPC 0x101
#define ACKTOIPC 0x103
#define PKTTOIPC 0x105
#define MSGFMIPC 0x181
#define ACKFMIPC 0x183
#define PKTFMIPC 0x185
#define DSKREAD 0x10 /* disk commands */
#define DSKWRITE 0x20
/* Some characters used in disk controller packets */
#define STX 0x02
#define ETX 0x03
#define ACK 0x06
#define NAK 0x15
#define PKTSTX 0x0 /* offsets within a disk packet */
#define PKTID 0x1
#define PKTSZ 0x2
#define PKTDEV 0x3
#define PKTCHCOM 0x4
#define PKTSTCOM 0x5
#define PKTSTVAL 0x6
#define PKTSTPRM 0x8
#define STPKTSZ 0xf
/************************************************************************/
/* BIOS Table Definitions */
/************************************************************************/
/* Disk Parameter Block Structure */
struct dpb
{
WORD spt;
BYTE bsh;
BYTE blm;
BYTE exm;
BYTE dpbjunk;
WORD dsm;
WORD drm;
BYTE al0;
BYTE al1;
WORD cks;
WORD off;
};
/* Disk Parameter Header Structure */
struct dph
{
BYTE *xltp;
WORD dphscr[3];
BYTE *dirbufp;
struct dpb *dpbp;
BYTE *csvp;
BYTE *alvp;
};
/************************************************************************/
/* Directory Buffer for use by the BDOS */
/************************************************************************/
BYTE dirbuf[128];
#if ! LOADER
/************************************************************************/
/* CSV's */
/************************************************************************/
BYTE csv0[16];
BYTE csv1[16];
#if ! CTLTYPE
BYTE csv2[256];
BYTE csv3[256];
#endif
#if MEMDSK
BYTE csv4[16];
#endif
/************************************************************************/
/* ALV's */
/************************************************************************/
BYTE alv0[32]; /* (dsm0 / 8) + 1 */
BYTE alv1[32]; /* (dsm1 / 8) + 1 */
#if ! CTLTYPE
BYTE alv2[412]; /* (dsm2 / 8) + 1 */
BYTE alv3[412]; /* (dsm2 / 8) + 1 */
#endif
#if MEMDSK
BYTE alv4[48]; /* (dsm4 / 8) + 1 */
#endif
#endif
/************************************************************************/
/* Disk Parameter Blocks */
/************************************************************************/
/* The following dpb definitions express the intent of the writer, */
/* unfortunately, due to a compiler bug, these lines cannot be used. */
/* Therefore, the obscure code following them has been inserted. */
/************* spt, bsh, blm, exm, jnk, dsm, drm, al0, al1, cks, off */
#ifdef BULLSHIT
struct dpb dpb0 = { 26, 3, 7, 0, 0, 242, 63, 0, 0, 16, 2};
#if ! CTLTYPE
struct dpb dpb2 = { 32, 5, 31, 1, 0, 3288, 1023, 0, 0, 256, 4};
#endif
#if MEMDSK
struct dpb dpb3 = { 32, 4, 15, 0, 0, 191, 63, 0, 0, 16, 0};
#endif
#endif
/*********** end of readable definitions *************/
/* The Alcyon C compiler assumes all structures are arrays of int, so */
/* in the following definitions, adjacent pairs of chars have been */
/* combined into int constants --- what a kludge! **********************/
struct dpb dpb0 = { 26, 775, 0, 242, 63, -16384, 16, 2 };
#if ! CTLTYPE
struct dpb dpb2 = { 32, 1311, 256, 3288, 1023, 0xFF00, 256, 4 };
#endif
#if MEMDSK
struct dpb dpb3 = { 32, 1039, 0, 191, 63, 0, 16, 0 };
/*************** End of kludge *****************/
#endif
/************************************************************************/
/* Sector Translate Table for Floppy Disks */
/************************************************************************/
BYTE xlt[26] = { 1, 7, 13, 19, 25, 5, 11, 17, 23, 3, 9, 15, 21,
2, 8, 14, 20, 26, 6, 12, 18, 24, 4, 10, 16, 22 };
/************************************************************************/
/* Disk Parameter Headers */
/* */
/* Four disks are defined : dsk a: diskno=0, (Motorola's #fd04) */
/* if CTLTYPE = 0 : dsk b: diskno=1, (Motorola's #fd05) */
/* : dsk c: diskno=2, (Motorola's #hd00) */
/* : dsk d: diskno=3, (Motorola's #hd01) */
/* */
/* Two disks are defined : dsk a: diskno=0, (Motorola's #fd00) */
/* if CTLTYPE = 1 : dsk b: diskno=1, (Motorola's #fd01) */
/* */
/************************************************************************/
#if ! LOADER
/* Disk Parameter Headers */
struct dph dphtab[] =
{ {&xlt, 0, 0, 0, &dirbuf, &dpb0, &csv0, &alv0}, /*dsk a*/
{&xlt, 0, 0, 0, &dirbuf, &dpb0, &csv1, &alv1}, /*dsk b*/
#if ! CTLTYPE
{ 0L, 0, 0, 0, &dirbuf, &dpb2, &csv2, &alv2}, /*dsk c*/
{ 0L, 0, 0, 0, &dirbuf, &dpb2, &csv3, &alv3}, /*dsk d*/
#endif
#if MEMDSK
{ 0L, 0, 0, 0, &dirbuf, &dpb3, &csv4, &alv4} /*dsk e*/
};
#endif
#else
#if ! CTLTYPE
struct dph dphtab[4] =
#else
struct dph dphtab[2] =
#endif
{ {&xlt, 0, 0, 0, &dirbuf, &dpb0, 0L, 0L}, /*dsk a*/
{&xlt, 0, 0, 0, &dirbuf, &dpb0, 0L, 0L}, /*dsk b*/
#if ! CTLTYPE
{ 0L, 0, 0, 0, &dirbuf, &dpb2, 0L, 0L}, /*dsk c*/
{ 0L, 0, 0, 0, &dirbuf, &dpb2, 0L, 0L}, /*dsk d*/
#endif
};
#endif
/************************************************************************/
/* Memory Region Table */
/************************************************************************/
struct mrt { WORD count;
LONG tpalow;
LONG tpalen;
}
memtab = { 1, 0x0400L, 0x14800L };
#if MEMDSK
BYTE *memdsk = 0x20000L;
#endif
#if ! LOADER
/************************************************************************/
/* IOBYTE */
/************************************************************************/
WORD iobyte; /* The I/O Byte is defined, but not used */
#endif
/************************************************************************/
/* Currently Selected Disk Stuff */
/************************************************************************/
WORD settrk, setsec, setdsk; /* Currently set track, sector, disk */
BYTE *setdma; /* Currently set dma address */
/************************************************************************/
/* Track Buffering Definitions and Variables */
/************************************************************************/
#if ! LOADER
#define NUMTB 4 /* Number of track buffers -- must be at least 3 */
/* for the algorithms in this BIOS to work properly */
/* Define the track buffer structure */
struct tbstr {
struct tbstr *nextbuf; /* form linked list for LRU */
BYTE buf[32*128]; /* big enough for 1/4 hd trk */
WORD dsk; /* disk for this buffer */
WORD trk; /* track for this buffer */
BYTE valid; /* buffer valid flag */
BYTE dirty; /* true if a BIOS write has */
/* put data in this buffer, */
/* but the buffer hasn't been */
/* flushed yet. */
};
struct tbstr *firstbuf; /* head of linked list of track buffers */
struct tbstr *lastbuf; /* tail of ditto */
struct tbstr tbuf[NUMTB]; /* array of track buffers */
#else
/* the loader bios uses only 1 track buffer */
BYTE buf1trk[32*128]; /* big enough for 1/4 hd trk */
BYTE bufvalid;
WORD buftrk;
#endif
/************************************************************************/
/* Disk I/O Packets for the UDC and other Disk I/O Variables */
/************************************************************************/
/* Home disk packet */
struct hmpkst {
BYTE a1;
BYTE a2;
BYTE a3;
BYTE dskno;
BYTE com1;
BYTE com2;
BYTE a6;
BYTE a7;
}
hmpack = { 512, 1792, 0, 768 }; /* kludge init by words */
/* Read/write disk packet */
struct rwpkst {
BYTE stxchr;
BYTE pktid;
BYTE pktsize;
BYTE dskno;
BYTE chcmd;
BYTE devcmd;
WORD numblks;
WORD blksize;
LONG iobf;
WORD cksum;
LONG lsect;
BYTE etxchr;
BYTE rwpad;
};
struct rwpkst rwpack = { 512, 5376, 4097, 13, 256, 0, 0, 0, 0, 0, 768 };
#if ! LOADER
/* format disk packet */
struct fmtpkst {
BYTE fmtstx;
BYTE fmtid;
BYTE fmtsize;
BYTE fmtdskno;
BYTE fmtchcmd;
BYTE fmtdvcmd;
BYTE fmtetx;
BYTE fmtpad;
};
struct fmtpkst fmtpack = { 512, 1792, 0x4002, 0x0300 };
#endif
/************************************************************************/
/* Define the number of disks supported and other disk stuff */
/************************************************************************/
#if ! CTLTYPE
#define NUMDSKS 4 /* number of disks defined */
#else
#define NUMDSKS 2
#endif
#if MEMDSK
#define NUMDSKS 5
#endif
#define MAXDSK (NUMDSKS-1) /* maximum disk number */
#if ! CTLTYPE
BYTE cnvdsk[NUMDSKS] = { 4, 5, 0, 1 }; /* convert CP/M dsk# to EXORmacs */
BYTE rcnvdsk[6] = { 2, 3, 0, 0, 0, 1 }; /* and vice versa */
#else
BYTE cnvdsk[NUMDSKS] = { 0, 1 };
BYTE rcnvdsk[2] = { 0, 1 };
#endif
/* defines for IPC and disk states */
#define IDLE 0
#define ACTIVE 1
WORD ipcstate; /* current IPC state */
WORD actvdsk; /* disk number of currently active disk, if any */
LONG intcount; /* count of interrupts needing to be processed */
struct dskst {
WORD state; /* from defines above */
BYTE ready; /* 0 => not ready */
BYTE change; /* 0 => no change */
}
dskstate[NUMDSKS];
/************************************************************************/
/* Generic Serial Port I/O Procedures */
/************************************************************************/
/************************************************************************/
/* Port initialization */
/************************************************************************/
portinit(port)
REG BYTE *port;
{
*(port + PORTCTRL) = PORTRSET; /* reset the port */
*(port + PORTCTRL) = PORTINIT;
}
/************************************************************************/
/* Generic serial port status input status */
/************************************************************************/
portstat(port)
REG BYTE *port;
{
if ( *(port + PORTSTAT) & PORTRDRF) return(0xff); /* input ready */
else return(0x00); /* not ready */
}
/************************************************************************/
/* Generic serial port input */
/************************************************************************/
BYTE portin(port)
REG BYTE *port;
{
while ( ! portstat(port)) ; /* wait for input */
return ( *(port + PORTRDR)); /* got some, return it */
}
/************************************************************************/
/* Generic serial port output */
/************************************************************************/
portout(port, ch)
REG BYTE *port;
REG BYTE ch;
{
while ( ! (*(port + PORTSTAT) & PORTTDRE) ) ; /* wait for ok to send */
*(port + PORTTDR) = ch; /* then send character */
}
/************************************************************************/
/* Error procedure for BIOS */
/************************************************************************/
#if ! LOADER
bioserr(errmsg)
REG BYTE *errmsg;
{
printstr("\n\rBIOS ERROR -- ");
printstr(errmsg);
printstr(".\n\r");
}
printstr(s) /* used by bioserr */
REG BYTE *s;
{
while (*s) {portout(PORT1,*s); s += 1; };
}
#else
bioserr() /* minimal error procedure for loader BIOS */
{
l : goto l;
}
#endif
/************************************************************************/
/* Disk I/O Procedures */
/************************************************************************/
EXTERN dskia(); /* external interrupt handler -- calls dskic */
EXTERN setimask(); /* use to set interrupt mask -- returns old mask */
dskic()
{
/* Disk Interrupt Handler -- C Language Portion */
REG BYTE workbyte;
BYTE stpkt[STPKTSZ];
workbyte = (DSKIPC + ACKFMIPC)->byte;
if ( (workbyte == ACK) || (workbyte == NAK) )
{
if ( ipcstate == ACTIVE ) intcount += 1;
else (DSKIPC + ACKFMIPC)->byte = 0; /* ??? */
}
workbyte = (DSKIPC + MSGFMIPC)->byte;
if ( workbyte & 0x80 )
{
getstpkt(stpkt);
if ( stpkt[PKTID] == 0xFF )
{
/* unsolicited */
unsolst(stpkt);
sendack();
}
else
{
/* solicited */
if ( ipcstate == ACTIVE ) intcount += 1;
else sendack();
}
}
} /* end of dskic */
/************************************************************************/
/* Read status packet from IPC */
/************************************************************************/
getstpkt(stpktp)
REG BYTE *stpktp;
{
REG BYTE *p, *q;
REG WORD i;
p = stpktp;
q = (DSKIPC + PKTFMIPC);
for ( i = STPKTSZ; i; i -= 1 )
{
*p = *q;
p += 1;
q += 2;
}
}
/************************************************************************/
/* Handle Unsolicited Status from IPC */
/************************************************************************/
unsolst(stpktp)
REG BYTE *stpktp;
{
REG WORD dev;
REG WORD ready;
REG struct dskst *dsp;
dev = rcnvdsk[ (stpktp+PKTDEV)->byte ];
ready = ((stpktp+PKTSTPRM)->byte & 0x80) == 0x0;
dsp = & dskstate[dev];
if ( ( ready && !(dsp->ready) ) ||
(!ready) && (dsp->ready) ) dsp->change = 1;
dsp->ready = ready;
#if ! LOADER
if ( ! ready ) setinvld(dev); /* Disk is not ready, mark buffers */
#endif
}
#if ! LOADER
/************************************************************************/
/* Mark all buffers for a disk as not valid */
/************************************************************************/
setinvld(dsk)
REG WORD dsk;
{
REG struct tbstr *tbp;
tbp = firstbuf;
while ( tbp )
{
if ( tbp->dsk == dsk ) tbp->valid = 0;
tbp = tbp->nextbuf;
}
}
#endif
/************************************************************************/
/* Wait for an ACK from the IPC */
/************************************************************************/
waitack()
{
REG WORD imsave;
REG BYTE work;
while (1)
{
while ( ! intcount ) ; /* wait */
imsave = setimask(7);
intcount -= 1;
work = (DSKIPC + ACKFMIPC)->byte;
if ( (work == ACK) || (work == NAK) )
{
(DSKIPC + ACKFMIPC)->byte = 0;
setimask(imsave);
return(work == ACK);
}
setimask(imsave);
}
}
/************************************************************************/
/* Acknowledge a message from the IPC */
/************************************************************************/
sendack()
{
(DSKIPC + MSGFMIPC)->byte = 0; /* clear message flag */
(DSKIPC + ACKTOIPC)->byte = ACK; /* send ACK */
(DSKIPC + INTTOIPC)->byte = 0; /* interrupt IPC */
}
/************************************************************************/
/* Send a packet to the IPC */
/************************************************************************/
sendpkt(pktadr, pktsize)
REG BYTE *pktadr;
REG WORD pktsize;
{
REG BYTE *iopackp;
REG WORD imsave;
while ( (DSKIPC+MSGTOIPC)->byte ); /* wait til ready */
(DSKIPC+ACKFMIPC)->byte = 0;
(DSKIPC+MSGFMIPC)->byte = 0;
iopackp = (DSKIPC+PKTTOIPC);
do {*iopackp = *pktadr++; iopackp += 2; pktsize -= 1;} while(pktsize);
(DSKIPC+MSGTOIPC)->byte = 0x80;
imsave = setimask(7);
dskstate[actvdsk].state = ACTIVE;
ipcstate = ACTIVE;
intcount = 0L;
(DSKIPC+INTTOIPC)->byte = 0;
setimask(imsave);
waitack();
}
/************************************************************************/
/* Wait for a Disk Operation to Finish */
/************************************************************************/
WORD dskwait(dsk, stcom, stval)
REG WORD dsk;
BYTE stcom;
WORD stval;
{
REG WORD imsave;
BYTE stpkt[STPKTSZ];
imsave = setimask(7);
while ( (! intcount) &&
dskstate[dsk].ready && (! dskstate[dsk].change) )
{
setimask(imsave); imsave = setimask(7);
}
if ( intcount )
{
intcount -= 1;
if ( ( (DSKIPC + MSGFMIPC)->byte & 0x80 ) == 0x80 )
{
getstpkt(stpkt);
setimask(imsave);
if ( (stpkt[PKTSTCOM] == stcom) &&
( (stpkt+PKTSTVAL)->word == stval ) ) return (1);
else return (0);
}
}
setimask(imsave);
return(0);
}
/************************************************************************/
/* Do a Disk Read or Write */
/************************************************************************/
dskxfer(dsk, trk, bufp, cmd)
REG WORD dsk, trk, cmd;
REG BYTE *bufp;
{
/* build packet */
REG WORD sectcnt;
REG WORD result;
#if CTLTYPE
LONG bytecnt; /* only needed for FDC */
WORD cheksum;
#endif
rwpack.dskno = cnvdsk[dsk];
rwpack.iobf = bufp;
sectcnt = (dphtab[dsk].dpbp)->spt;
rwpack.lsect = trk * (sectcnt >> 1);
rwpack.chcmd = cmd;
rwpack.numblks = (sectcnt >> 1);
#if CTLTYPE
cheksum = 0; /* FDC needs checksum */
bytecnt = ((LONG)sectcnt) << 7;
while ( bytecnt-- ) cheksum += (~(*bufp++)) & 0xff;
rwpack.cksum = cheksum;
#endif
actvdsk = dsk;
dskstate[dsk].change = 0;
sendpkt(&rwpack, 21);
result = dskwait(dsk, 0x70, 0x0);
sendack();
dskstate[dsk].state = IDLE;
ipcstate = IDLE;
return(result);
}
#if ! LOADER
/************************************************************************/
/* Write one disk buffer */
/************************************************************************/
flush1(tbp)
struct tbstr *tbp;
{
REG WORD ok;
if ( tbp->valid && tbp->dirty )
ok = dskxfer(tbp->dsk, tbp->trk, tbp->buf, DSKWRITE);
else ok = 1;
tbp->dirty = 0; /* even if error, mark not dirty */
tbp->valid &= ok; /* otherwise system has trouble */
/* continuing. */
return(ok);
}
/************************************************************************/
/* Write all disk buffers */
/************************************************************************/
flush()
{
REG struct tbstr *tbp;
REG WORD ok;
ok = 1;
tbp = firstbuf;
while (tbp)
{
if ( ! flush1(tbp) ) ok = 0;
tbp = tbp->nextbuf;
}
return(ok);
}
/*************************************************************************/
/* Fill the indicated disk buffer with the current track and sector */
/*************************************************************************/
fill(tbp)
REG struct tbstr *tbp;
{
REG WORD ok;
if ( tbp->valid && tbp->dirty ) ok = flush1(tbp);
else ok = 1;
if (ok) ok = dskxfer(setdsk, settrk, tbp->buf, DSKREAD);
tbp->valid = ok;
tbp->dirty = 0;
tbp->trk = settrk;
tbp->dsk = setdsk;
return(ok);
}
/************************************************************************/
/* Return the address of a track buffer structure containing the */
/* currently set track of the currently set disk. */
/************************************************************************/
struct tbstr *gettrk()
{
REG struct tbstr *tbp;
REG struct tbstr *ltbp;
REG struct tbstr *mtbp;
REG WORD imsave;
/* Check for disk on-line -- if not, return error */
imsave = setimask(7);
if ( ! dskstate[setdsk].ready )
{
setimask(imsave);
tbp = 0L;
return (tbp);
}
/* Search through buffers to see if the required stuff */
/* is already in a buffer */
tbp = firstbuf;
ltbp = 0;
mtbp = 0;
while (tbp)
{
if ( (tbp->valid) && (tbp->dsk == setdsk)
&& (tbp->trk == settrk) )
{
if (ltbp) /* found it -- rearrange LRU links */
{
ltbp->nextbuf = tbp->nextbuf;
tbp->nextbuf = firstbuf;
firstbuf = tbp;
}
setimask(imsave);
return ( tbp );
}
else
{
mtbp = ltbp; /* move along to next buffer */
ltbp = tbp;
tbp = tbp->nextbuf;
}
}
/* The stuff we need is not in a buffer, we must make a buffer */
/* available, and fill it with the desired track */
if (mtbp) mtbp->nextbuf = 0; /* detach lru buffer */
ltbp->nextbuf = firstbuf;
firstbuf = ltbp;
setimask(imsave);
if (flush1(ltbp) && fill(ltbp)) mtbp = ltbp; /* success */
else mtbp = 0L ; /* failure */
return (mtbp);
}
/************************************************************************/
/* Bios READ Function -- read one sector */
/************************************************************************/
read()
{
REG BYTE *p;
REG BYTE *q;
REG WORD i;
REG struct tbstr *tbp;
#if MEMDSK
if(setdsk != MEMDSK)
{
#endif
tbp = gettrk(); /* locate track buffer with sector */
if ( ! tbp ) return(1); /* failure */
/* locate sector in buffer and copy contents to user area */
p = (tbp->buf) + (setsec << 7); /* multiply by shifting */
#if MEMDSK
}
else
p = memdsk + (((LONG)(settrk) << 12L) + ((LONG)setsec << 7L));
#endif
q = setdma;
i = 128;
do {*q++ = *p++; i -= 1;} while (i); /* this generates good code */
return(0);
}
/************************************************************************/
/* BIOS WRITE Function -- write one sector */
/************************************************************************/
write(mode)
BYTE mode;
{
REG BYTE *p;
REG BYTE *q;
REG WORD i;
REG struct tbstr *tbp;
/* locate track buffer containing sector to be written */
#if MEMDSK
if(setdsk != MEMDSK)
{
#endif
tbp = gettrk();
if ( ! tbp ) return (1); /* failure */
/* locate desired sector and do copy the data from the user area */
p = (tbp->buf) + (setsec << 7); /* multiply by shifting */
#if MEMDSK
} else
{
p = memdsk + (((LONG)(settrk) << 12L) + ((LONG)setsec << 7L));
q = setdma;
i = 128;
do {*p++ = *q++; i -= 1;} while (i); /* this generates good code */
return(0);
}
#endif
q = setdma;
i = 128;
do {*p++ = *q++; i -= 1;} while (i); /* this generates good code */
tbp->dirty = 1; /* the buffer is now "dirty" */
/* The track must be written if this is a directory write */
if ( mode == 1 ){if ( flush1(tbp) ) return(0); else return(1);}
else return(0);
}
#else
/************************************************************************/
/* Read and Write functions for the Loader BIOS */
/************************************************************************/
read()
{
REG BYTE *p;
REG BYTE *q;
REG WORD i;
if ( ( (! bufvalid) || (buftrk != settrk) ) &&
( ! dskxfer(setdsk, settrk, buf1trk, DSKREAD) ) ) {return(1);}
bufvalid = 1;
buftrk = settrk;
p = buf1trk + (setsec << 7);
q = setdma;
i = 128;
do { *q++ = *p++; i-=1; } while(i);
return(0);
}
#endif
/************************************************************************/
/* BIOS Sector Translate Function */
/************************************************************************/
WORD sectran(s, xp)
REG WORD s;
REG BYTE *xp;
{
if (xp) return (WORD)xp[s];
else return (s+1);
}
/************************************************************************/
/* BIOS Set Exception Vector Function */
/************************************************************************/
LONG setxvect(vnum, vval)
WORD vnum;
LONG vval;
{
REG LONG oldval;
REG BYTE *vloc;
vloc = ( (long)vnum ) << 2;
oldval = vloc->lword;
vloc->lword = vval;
return(oldval);
}
/************************************************************************/
/* BIOS Select Disk Function */
/************************************************************************/
LONG slctdsk(dsk, logged)
REG BYTE dsk;
BYTE logged;
{
REG struct dph *dphp;
REG BYTE st1, st2;
BYTE stpkt[STPKTSZ];
setdsk = dsk; /* Record the selected disk number */
#if ! LOADER
/* Special Code to disable drive C. On the EXORmacs, drive C */
/* is the non-removable hard disk. Including this code lets */
/* you save your non-removable disk for non-CP/M use. */
if ( (dsk > MAXDSK) || ( dsk == 2 ) )
{
printstr("\n\rBIOS ERROR -- DISK ");
portout(PORT1, 'A'+dsk);
printstr(" NOT SUPPORTED\n\r");
return(0L);
}
#endif
dphp = &dphtab[dsk];
#if MEMDSK
if (setdsk == MEMDSK)
return(dphp);
#endif
if ( ! (logged & 0x1) )
{
hmpack.dskno = cnvdsk[setdsk];
hmpack.com1 = 0x30;
hmpack.com2 = 0x02;
actvdsk = dsk;
dskstate[dsk].change = 0;
sendpkt(&hmpack, 7);
if ( ! dskwait(dsk, 0x72, 0x0) )
{
sendack();
ipcstate = IDLE;
return ( 0L );
}
getstpkt(stpkt); /* determine disk type and size */
sendack();
ipcstate = IDLE;
st1 = stpkt[PKTSTPRM];
st2 = stpkt[PKTSTPRM+1];
if ( st1 & 0x80 ) /* not ready / ready */
{
dskstate[dsk].ready = 0;
return(0L);
}
else
dskstate[dsk].ready = 1;
switch ( st1 & 7 )
{
case 1 : /* floppy disk */
dphp->dpbp = &dpb0;
break;
#if ! CTLTYPE
case 2 : /* hard disk */
dphp->dpbp = &dpb2;
break;
#endif
default : bioserr("Invalid Disk Status");
dphp = 0L;
break;
}
}
return(dphp);
}
#if ! LOADER
/****************************************************************/
/* */
/* This function is included as an undocumented, */
/* unsupported method for EXORmacs users to format */
/* disks. It is not a part of CP/M-68K proper, and */
/* is only included here for convenience, since the */
/* Motorola disk controller is somewhat complex to */
/* program, and the BIOS contains supporting routines. */
/* */
/****************************************************************/
format(dsk)
REG WORD dsk;
{
REG WORD retval;
if ( ! slctdsk( (BYTE)dsk, (BYTE) 1 ) ) return;
#if MEMDSK
if (setdsk == MEMDSK) return;
#endif
fmtpack.dskno = cnvdsk[setdsk];
actvdsk = setdsk;
dskstate[setdsk].change = 0;
sendpkt(&fmtpack, 7);
if ( ! dskwait(setdsk, 0x70, 0x0) ) retval = 0;
else retval = 1;
sendack();
ipcstate = IDLE;
return(retval);
}
#endif
/************************************************************************/
/* */
/* Bios initialization. Must be done before any regular BIOS */
/* calls are performed. */
/* */
/************************************************************************/
biosinit()
{
initprts();
initdsks();
}
initprts()
{
portinit(PORT1);
portinit(PORT2);
}
initdsks()
{
REG WORD i;
REG WORD imsave;
#if ! LOADER
for ( i = 0; i < NUMTB; ++i )
{
tbuf[i].valid = 0;
tbuf[i].dirty = 0;
if ( (i+1) < NUMTB ) tbuf[i].nextbuf = &tbuf[i+1];
else tbuf[i].nextbuf = 0;
}
firstbuf = &tbuf[0];
lastbuf = &tbuf[NUMTB-1];
#else
bufvalid = 0;
#endif
for ( i = 0; i <= MAXDSK; i += 1)
{
dskstate[i].state = IDLE;
dskstate[i].ready = 1;
dskstate[i].change = 0;
}
imsave = setimask(7); /* turn off interrupts */
intcount = 0;
ipcstate = IDLE;
setimask(imsave); /* turn on interrupts */
}
/************************************************************************/
/* */
/* BIOS MAIN ENTRY -- Branch out to the various functions. */
/* */
/************************************************************************/
LONG cbios(d0, d1, d2)
REG WORD d0;
REG LONG d1, d2;
{
switch(d0)
{
case 0: biosinit(); /* INIT */
break;
#if ! LOADER
case 1: flush(); /* WBOOT */
initdsks();
wboot();
/* break; */
#endif
case 2: return(portstat(PORT1)); /* CONST */
/* break; */
case 3: return(portin(PORT1)); /* CONIN */
/* break; */
case 4: portout(PORT1, (char)d1); /* CONOUT */
break;
case 5: ; /* LIST */
case 6: portout(PORT2, (char)d1); /* PUNCH */
break;
case 7: return(portin(PORT2)); /* READER */
/* break; */
case 8: settrk = 0; /* HOME */
break;
case 9:
#if LOADER
d1 = 3; /* Always HD */
#endif
return(slctdsk((char)d1, (char)d2)); /* SELDSK */
/* break; */
case 10: settrk = (int)d1; /* SETTRK */
break;
case 11: setsec = ((int)d1-1); /* SETSEC */
break;
case 12: setdma = d1; /* SETDMA */
break;
case 13: return(read()); /* READ */
/* break; */
#if ! LOADER
case 14: return(write((char)d1)); /* WRITE */
/* break; */
case 15: if ( *(BYTE *)(PORT2 + PORTSTAT) & PORTTDRE )
return ( 0x0ff );
else return ( 0x000 );
/* break; */
#endif
case 16: return(sectran((int)d1, d2)); /* SECTRAN */
/* break; */
#if ! LOADER
case 18: return(&memtab); /* GMRTA */
/* break; */
case 19: return(iobyte); /* GETIOB */
/* break; */
case 20: iobyte = (int)d1; /* SETIOB */
break;
case 21: if (flush()) return(0L); /* FLUSH */
else return(0xffffL);
/* break; */
#endif
case 22: return(setxvect((int)d1,d2)); /* SETXVECT */
/* break; */
#if ! LOADER
/**********************************************************/
/* This function is not part of a standard BIOS. */
/* It is included only for convenience, and will */
/* not be supported in any way, nor will it */
/* necessarily be included in future versions of */
/* CP/M-68K */
/**********************************************************/
case 63: return( ! format((int)d1) ); /* Disk Formatter */
/* break; */
#endif
default: return(0L);
break;
} /* end switch */
} /* END OF BIOS */
/* End of C Bios */