mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-23 00:14:25 +00:00
1125 lines
23 KiB
C
1125 lines
23 KiB
C
/*=======================================================================*/
|
|
/*/---------------------------------------------------------------------\*/
|
|
/*| |*/
|
|
/*| CP/M-68K(tm) BIOS for the EXORMACS |*/
|
|
/*| |*/
|
|
/*| Copyright 1982, Digital Research. |*/
|
|
/*| |*/
|
|
/*| Modified 9/7/82 wbt |*/
|
|
/*| 10/5/82 wbt |*/
|
|
/*| |*/
|
|
/*\---------------------------------------------------------------------/*/
|
|
/*=======================================================================*/
|
|
|
|
#define LOADER 0 /* 1 for Loader BIOS, 0 for normal BIOS */
|
|
|
|
|
|
#include "[bill.cpm68k.csupport]stdtypes.h"
|
|
|
|
char copyright[] = "Copyright 1982, 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
|
|
#define PORT2 0xFFEE015
|
|
|
|
/* 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];
|
|
BYTE csv2[256];
|
|
BYTE csv3[256];
|
|
|
|
|
|
/************************************************************************/
|
|
/* ALV's */
|
|
/************************************************************************/
|
|
|
|
BYTE alv0[32]; /* (dsm0 / 8) + 1 */
|
|
BYTE alv1[32]; /* (dsm1 / 8) + 1 */
|
|
BYTE alv2[412]; /* (dsm2 / 8) + 1 */
|
|
BYTE alv3[412]; /* (dsm2 / 8) + 1 */
|
|
|
|
#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
|
|
|
|
struct dpb dpb0={ 26, 3, 7, 0, 0, 242, 63, 0xC0, 0, 16, 2};
|
|
struct dpb dpb2={ 32, 5, 31, 1, 0, 3288, 1023, 0xFF, 0, 256, 4};
|
|
|
|
********** 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 };
|
|
struct dpb dpb2 = { 32, 1311, 256, 3288, 1023, 0xFF00, 256, 4 };
|
|
|
|
/*************** End of kludge *****************/
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* Sector Translate Table */
|
|
/************************************************************************/
|
|
|
|
BYTE xlt[26] = { 0, 6, 12, 18, 24, 4, 10, 16, 22, 2, 8, 14, 20,
|
|
1, 7, 13, 19, 25, 5, 11, 17, 23, 3, 9, 15, 21 };
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* Disk Parameter Headers */
|
|
/* */
|
|
/* Four disks are defined : dsk a: diskno=0, (Motorola's #fd04) */
|
|
/* dsk b: diskno=1, (Motorola's #fd05) */
|
|
/* dsk c: diskno=2, (Motorola's #hd00) */
|
|
/* dsk d: diskno=3, (Motorola's #hd01) */
|
|
/************************************************************************/
|
|
|
|
#if ! LOADER
|
|
|
|
/* Disk Parameter Headers */
|
|
|
|
struct dph dphtab[4] =
|
|
{ {&xlt, 0, 0, 0, &dirbuf, &dpb0, &csv0, &alv0}, /*dsk a*/
|
|
{&xlt, 0, 0, 0, &dirbuf, &dpb0, &csv1, &alv1}, /*dsk b*/
|
|
{ 0L, 0, 0, 0, &dirbuf, &dpb2, &csv2, &alv2}, /*dsk c*/
|
|
{ 0L, 0, 0, 0, &dirbuf, &dpb2, &csv3, &alv3}, /*dsk d*/
|
|
};
|
|
#else
|
|
|
|
struct dph dphtab[4] =
|
|
{ {&xlt, 0, 0, 0, &dirbuf, &dpb0, 0L, 0L}, /*dsk a*/
|
|
{&xlt, 0, 0, 0, &dirbuf, &dpb0, 0L, 0L}, /*dsk b*/
|
|
{ 0L, 0, 0, 0, &dirbuf, &dpb2, 0L, 0L}, /*dsk c*/
|
|
{ 0L, 0, 0, 0, &dirbuf, &dpb2, 0L, 0L}, /*dsk d*/
|
|
};
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Memory Region Table */
|
|
/************************************************************************/
|
|
|
|
struct mrt { WORD count;
|
|
LONG tpalow;
|
|
LONG tpalen;
|
|
}
|
|
memtab = { 1, 0x0000,0x400, 0x0007,0x0c00 };/* kludge init by int */
|
|
|
|
|
|
#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 8 /* Number of track buffers -- must be at least 3 */
|
|
/* for some algorithms to work properly */
|
|
|
|
/* Define a 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;
|
|
BYTE buftrk;
|
|
|
|
#endif
|
|
|
|
|
|
/************************************************************************/
|
|
/* Disk I/O Packets for the UDC and other Disk I/O Variables */
|
|
/************************************************************************/
|
|
|
|
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 */
|
|
|
|
|
|
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
|
|
|
|
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 NUMDSKS 4 /* number of disks defined */
|
|
#define MAXDSK (NUMDSKS-1) /* maximum disk number */
|
|
|
|
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 */
|
|
|
|
/* 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 */
|
|
/************************************************************************/
|
|
|
|
portinit(port)
|
|
REG BYTE *port;
|
|
{
|
|
*(port + PORTCTRL) = PORTRSET; /* reset the port */
|
|
*(port + PORTCTRL) = PORTINIT;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* Generic serial port 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 ( 0x7f & *(port + PORTRDR)); /* got some, return it */
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* Generic serial port output */
|
|
/************************************************************************/
|
|
|
|
portout(port, ch)
|
|
REG BYTE *port;
|
|
REG BYTE ch;
|
|
{
|
|
ch = ch & 0x7f;
|
|
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()
|
|
{
|
|
l : goto l;
|
|
}
|
|
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* Disk I/O Procedures */
|
|
/************************************************************************/
|
|
|
|
|
|
EXTERN dskia();
|
|
EXTERN setimask();
|
|
|
|
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 */
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
|
|
#if ! LOADER
|
|
|
|
setinvld(dsk)
|
|
REG WORD dsk;
|
|
{
|
|
REG struct tbstr *tbp;
|
|
|
|
tbp = firstbuf;
|
|
while ( tbp )
|
|
{
|
|
if ( tbp->dsk == dsk ) tbp->valid = 0;
|
|
tbp = tbp->nextbuf;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
sendack()
|
|
{
|
|
/* acknowledge a message from the IPC */
|
|
|
|
(DSKIPC + MSGFMIPC)->byte = 0; /* clear message flag */
|
|
(DSKIPC + ACKTOIPC)->byte = ACK; /* send ACK */
|
|
(DSKIPC + INTTOIPC)->byte = 0; /* interrupt 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();
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
dskxfer(dsk, trk, bufp, cmd)
|
|
REG WORD dsk, trk, cmd;
|
|
REG BYTE *bufp;
|
|
{
|
|
/* build packet */
|
|
|
|
REG WORD sectcnt;
|
|
REG WORD result;
|
|
|
|
rwpack.dskno = cnvdsk[dsk];
|
|
rwpack.iobf = bufp;
|
|
sectcnt = (dphtab[dsk].dpbp)->spt;
|
|
rwpack.lsect = trk * (sectcnt >> 1);
|
|
rwpack.chcmd = cmd;
|
|
rwpack.numblks = (sectcnt >> 1);
|
|
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
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
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(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);
|
|
}
|
|
|
|
|
|
struct tbstr *gettrk()
|
|
{
|
|
REG struct tbstr *tbp;
|
|
REG struct tbstr *ltbp;
|
|
REG struct tbstr *mtbp;
|
|
REG WORD imsave;
|
|
|
|
imsave = setimask(7);
|
|
if ( ! dskstate[setdsk].ready )
|
|
{
|
|
setimask(imsave);
|
|
tbp = 0L;
|
|
return (tbp);
|
|
}
|
|
tbp = firstbuf;
|
|
ltbp = 0;
|
|
mtbp = 0;
|
|
|
|
while (tbp)
|
|
{
|
|
if ( (tbp->valid) && (tbp->dsk == setdsk)
|
|
&& (tbp->trk == settrk) )
|
|
{
|
|
if (ltbp)
|
|
{
|
|
ltbp->nextbuf = tbp->nextbuf;
|
|
tbp->nextbuf = firstbuf;
|
|
firstbuf = tbp;
|
|
}
|
|
setimask(imsave);
|
|
return tbp;
|
|
}
|
|
else
|
|
{
|
|
mtbp = ltbp;
|
|
ltbp = tbp;
|
|
tbp = tbp->nextbuf;
|
|
}
|
|
}
|
|
|
|
if (mtbp) mtbp->nextbuf = 0; /* detach lru buffer */
|
|
if ( (! firstbuf->valid) ||
|
|
(firstbuf->trk > dphtab[firstbuf->dsk].dpbp->off) ) /* dir trk? */
|
|
{
|
|
ltbp->nextbuf = firstbuf;
|
|
firstbuf = ltbp;
|
|
}
|
|
else
|
|
{
|
|
ltbp->nextbuf = firstbuf->nextbuf;
|
|
firstbuf->nextbuf = ltbp;
|
|
}
|
|
setimask(imsave);
|
|
if (flush1(ltbp) && fill(ltbp)) mtbp = ltbp;
|
|
else mtbp = 0L ;
|
|
return (mtbp);
|
|
}
|
|
|
|
|
|
|
|
read()
|
|
{
|
|
REG BYTE *p;
|
|
REG BYTE *q;
|
|
REG WORD i;
|
|
REG struct tbstr *tbp;
|
|
|
|
tbp = gettrk();
|
|
|
|
if ( ! tbp ) return(1); /* failure */
|
|
|
|
p = (tbp->buf) + (setsec << 7); /* multiply by shifting */
|
|
q = setdma;
|
|
i = 128;
|
|
do {*q++ = *p++; i -= 1;} while (i);
|
|
return(0);
|
|
}
|
|
|
|
|
|
write(mode)
|
|
BYTE mode;
|
|
{
|
|
REG BYTE *p;
|
|
REG BYTE *q;
|
|
REG WORD i;
|
|
REG struct tbstr *tbp;
|
|
|
|
tbp = gettrk();
|
|
if ( ! tbp ) return (1); /* failure */
|
|
|
|
p = (tbp->buf) + (setsec << 7); /* multiply by shifting */
|
|
q = setdma;
|
|
i = 128;
|
|
do {*p++ = *q++; i -= 1;} while (i);
|
|
tbp->dirty = 1;
|
|
if ( mode == 1 ) { if ( flush() ) return(0); else return(1); }
|
|
else return(0);
|
|
}
|
|
|
|
#else
|
|
|
|
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
|
|
|
|
|
|
WORD sectran(s, xp)
|
|
REG WORD s;
|
|
REG BYTE *xp;
|
|
{
|
|
if (xp) return (WORD)xp[s];
|
|
else return s;
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
LONG slctdsk(dsk, logged)
|
|
REG BYTE dsk;
|
|
BYTE logged;
|
|
{
|
|
REG struct dph *dphp;
|
|
REG BYTE st1, st2;
|
|
BYTE stpkt[STPKTSZ];
|
|
|
|
#if ! LOADER
|
|
if ( (dsk > MAXDSK) || ( dsk == 2 ) )
|
|
{
|
|
printstr("\n\rBIOS ERROR -- DISK ");
|
|
portout(PORT1, 'A'+dsk);
|
|
printstr(" NOT SUPPORTED\n\r");
|
|
return(0L);
|
|
}
|
|
#endif
|
|
|
|
setdsk = dsk;
|
|
dphp = &dphtab[dsk];
|
|
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;
|
|
|
|
case 2 : /* hard disk */
|
|
|
|
dphp->dpbp = &dpb2;
|
|
break;
|
|
|
|
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;
|
|
|
|
slctdsk( (BYTE)dsk, (BYTE) 1 );
|
|
|
|
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)
|
|
WORD d0;
|
|
LONG d1, d2;
|
|
{
|
|
|
|
switch(d0)
|
|
{
|
|
#if ! LOADER
|
|
case 0: init(); /* INIT */
|
|
break;
|
|
|
|
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: return(slctdsk((char)d1, (char)d2)); /* SELDSK */
|
|
break;
|
|
|
|
case 10: settrk = (int)d1; /* SETTRK */
|
|
break;
|
|
|
|
case 11: setsec = (int)d1; /* SETSEC */
|
|
break;
|
|
|
|
case 12: setdma = d1; /* SETDMA */
|
|
break;
|
|
|
|
case 13: return(read()); /* READ */
|
|
break;
|
|
#if ! LOADER
|
|
case 14: return(write((char)d1)); /* WRITE */
|
|
break;
|
|
#endif
|
|
case 15: return(portstat(PORT2)); /* LISTST */
|
|
break;
|
|
|
|
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
|
|
} /* end switch */
|
|
|
|
|
|
} /* END OF BIOS */
|
|
|
|
|
|
|
|
/* End of C Bios */
|