#ifdef loader #define LOADER 1 #endif /*=======================================================================*/ /*+---------------------------------------------------------------------+*/ /*| |*/ /*| CP/M-8000(tm) BIOS for the OLIVETTI M20 (Z8000) |*/ /*| |*/ /*| Copyright 1984, Digital Research Inc. |*/ /*| |*/ /*+---------------------------------------------------------------------+*/ /*=======================================================================*/ /*-------------------------*/ /* Compilation information */ /*-------------------------*/ /*----------------------------------------------------------------------------*/ /*To compile bios.c for cpmldr.sys the command is: zcc -c -M1 -dLOADER bios.c */ /*This conditionally compiles bios.c leaving unrequired code out of the object*/ /*file. */ /*----------------------------------------------------------------------------*/ /* The normal bios compile command for cpm.sys is: zcc -c -M1 bios.c */ /* This will provide the full functionallity of the bios in the object file */ /*---------------------------------------------------------------------------*/ /* By compiling bios.c with the command : zcc -c -M1 -dTRANSFER bios.c */ /* You are provided with a bios object that allows the two floppy drives to */ /* have two different formats. This is left purely as an example for the */ /* the benefit of porting to a different format and can be modified. */ /*----------------------------------------------------------------------------*/ /* By compiling bios.c with the command: zcc -c -M1 -dsect26 bios.c */ /* 8" floppy disk support is provided by conditional compilation. */ /*----------------------------------------------------------------------------*/ #define BAUD 0 /* Setting this define to 1 will conditionally compile*/ /* code setting the tty port to 1200 baud listening */ /* XOFF on that port. */ /* #define DEBUG 1 */ /* By decommenting this define hard disk debugging */ /* is enabled. This provides drive, block, and */ /* track information to be printed on the console. */ char copyright[] = "Copyright 1984 Digital Research Inc."; /* HISTORY ** ** 820803 S. Savitzky (Zilog) -- derived from 68000 EXORMACS bios ** ** 830614 F. Zlotnick (Zilog) -- removed initialization of iobyte ** upon each warmboot. Changed seldisk to test for ** overflow of the dphtab (to fix the "dir d:" bug). ** ** 830804 F. Zlotnick (Zilog) -- Added conditional compilation for ** loader BIOS, which only needs a few of the BIOS ** functions. The loader DOES require the definition ** of a context structure, for transfer of control to ** the system proper. ** ** 830804 F. Zlotnick (Zilog) -- Changed Disk Parameter Blocks to ** reflect new bootstrap method. ** ** 830809 F. Zlotnick (Zilog) -- Added escape character to keyboard ** map, as ctrl'['. ** ** 831205 K. Greenberg (Zilog) -- Fixed disk parameter table for hard ** drive C to point to dpb3, not dpb2 (80 trk floppy). ** ** 831212 K. Greenberg (Zilog) -- Modified disk parameter tables for ** hard disk to look more like floppies (fewer sectors ** but more tracks). This will fix the sector deblocking ** part of the bios to be compatible with both. Also ** switched to 4K allocation blocks and 512 direntries. */ /************************************************************************/ /************************************************************************/ /* */ /* I/O Device Definitions */ /* */ /************************************************************************/ /************************************************************************/ /************************************************************************/ /* Define Interrupt Controller constants */ /************************************************************************/ /* The interrupt controller is an Intel 8259 left-shifted one bit */ /* to allow for the word-alligned interrupt vectors of the Z8000. */ /* === I am going to assume that this is set up in the PROM === */ /************************************************************************/ /* Define the two USART ports */ /************************************************************************/ /* The USARTs are Intel 8251's */ #define KBD 0xA1 /* Keyboard USART base */ #define RS232 0xC1 /* RS-232 terminal */ #define SERDATA 0 /* data port offset */ #define SERCTRL 2 /* control port offset */ #define SERSTAT 2 /* status port offset */ #define SERINIT 0x37 /* init (3 times) */ #define SERRES 0x40 /* reset */ #define SERMODE 0xEE /* mode (2 stop, even parity */ /* parity disable, 8 bits */ /* divide by 16 */ /* DUBIOUS. 15?? */ #define TTYON 0x37 /* cmd (no hunt, no reset, */ /* RTS=0, error reset, */ /* no break, rcv enable, */ /* DTR=0, xmt enable */ #define SERRRDY 0x02 /* RCV ready bit mask */ #define SERXRDY 0x01 /* XMT ready bit mask */ #define XON 0x11 /* Control- Q */ #define XOFF 0x13 /* Control- S */ /************************************************************************/ /* Define the counter/timer ports */ /************************************************************************/ /* The counter-timer is an Intel 8253 */ #define CT_232 0x121 /* counter/timer 0 -- RS232 baud rate */ #define CT_KBD 0x123 /* counter/timer 1 -- kbd baud rate */ #define CT_RTC 0x125 /* counter/timer 2 -- NVI (rt clock) */ #define CT_CTRL 0x127 /* counter/timer control port */ #define CT0CTL 0x36 /* c/t 0 control byte */ #define CT1CTL 0x76 /* c/t 1 control byte */ #define CT2CTL 0xB4 /* c/t 2 control byte */ /* control byte is followed by LSB, then MSB of count to data register */ /* baud rate table follows: */ #ifndef LOADER /* NOT needed by the Loader Bios */ int baudRates[10] = { 1538, /* 50 */ 699, /* 110 */ 256, /* 300 */ 128, /* 600 */ 64, /* 1200 */ 32, /* 2400 */ 16, /* 4800 */ 8, /* 9600 */ 4, /* 19200 */ 2 /* 38400 */ }; #endif /* End Conditional */ /************************************************************************/ /* Define Parallel Port constants */ /************************************************************************/ /* The parallel (printer) port is an Intel 8255 */ #define PAR_A 0x81 /* port A data */ #define PAR_B 0x83 /* port B data */ #define PAR_C 0x85 /* port C data */ #define PARCTRL 0x87 /* control port */ #define PARBSY 0x02 /* bit one (busy bit) needs to be low */ #define PARFLT 0x10 /* bit five (fault bit) needs to be high */ /************************************************************************/ /************************************************************************/ /* */ /* PROM AND HARDWARE INTERFACE */ /* */ /************************************************************************/ /************************************************************************/ /************************************************************************/ /* Define PROM I/O Addresses and Related Constants */ /************************************************************************/ /* SEE BIOSIO.8KN FOR THESE EXTERNALS */ extern int disk_io(); /* (char drive, cmd -- disk I/O */ /* int blk_count, */ /* int blk_num, */ /* char *dest) -> int error? */ extern crt_put(); /* (char character) -- put byte to CRT */ extern cold_boot(); /* boot operating system */ #define DSKREAD 0 /* disk read command */ #define DSKWRITE 1 /* disk write command */ #define DSKFMT 2 /* disk format command */ #define DSKVFY 3 /* disk verify command */ #define DSKINIT 4 /* disk init. command */ /************************************************************************/ /* Define external I/O routines and addresses */ /************************************************************************/ /* SEE BIOSIF.8KN FOR THESE EXTERNALS */ extern output(); /* (port, data: int) -- output */ extern int input(); /* (port: int) -- input */ /************************************************************************/ /* Define external memory management routines */ /************************************************************************/ /* SEE SYSCALL.8KN FOR THESE EXTERNALS */ extern mem_cpy(); /* (src, dest, len: long)-- copy data */ extern long map_adr(); /* paddr = (laddr: long; space: int) */ #define CDATA 0 /* caller data space */ #define CCODE 1 /* caller code space */ #define SDATA 2 /* system data space */ #define SCODE 3 /* system code space */ #define NDATA 4 /* normal data space */ #define NCODE 5 /* normal code space */ /************************************************************************/ /* System Entry and Stack Pointer */ /************************************************************************/ #define SYSENTRY 0x0b000006L /* entry point */ #define SYSSTKPTR 0x0b00bffeL /* system's stack pointer start */ /************************************************************************/ /* Memory Region Table */ /************************************************************************/ #ifndef LOADER /* NOT needed for the Loader Bios */ struct mrt { int count; struct {long tpalow; long tpalen; } regions[5]; } memtab = {5, 0x0A000000L, 0x10000L, /* merged I and D */ 0x08000000L, 0x10000L, /* separated I */ 0x08000000L, 0x10000L, /* and D */ 0x0B000000L, 0x10000L, /* accessing I as D */ 0x09000000L, 0x10000L /* ddt debugger sits here */ }; #endif /* End conditional */ #ifdef LOADER /* NEEDED fr the Loader Bios */ struct mrt { int count; struct {long tpalow; long tpalen; } regions[1]; } memtab = {1, 0x0B000000L, 0x0C000L, /* system space: merged I and D */ }; struct context /* Startup context for user's program */ { short regs[14]; long segstkptr; short ignore; short FCW; long PC; }; struct context context = { /* Regs 0-13 cleared, sp set up below */ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, SYSSTKPTR, /* Loaded system's stack pointer */ 0, /* Ignore: value is zero */ 0xD800, /* FCW: segmented system, VI, NVI set */ SYSENTRY /* Entry point to system */ }; #endif /* End conditional */ /************************************************************************/ /* Set Exception Vector entry */ /************************************************************************/ extern long trapvec[]; /* trap vector */ long setxvect(vnum, vval) int vnum; long vval; { register long oldval; oldval = trapvec[vnum]; trapvec[vnum] = vval; return(oldval); } /************************************************************************/ /************************************************************************/ /* */ /* CHARACTER I/O */ /* */ /************************************************************************/ /************************************************************************/ /************************************************************************/ /* Generic Serial Port I/O Procedures */ /************************************************************************/ /* define as extern the dirty flag, which is actually defined later */ /* on in this file. Used to flush the buffer at an opportune moment. */ extern int tbdirty; serinit(port) int port; { output(port+SERCTRL, SERINIT); output(port+SERCTRL, SERINIT); output(port+SERCTRL, SERINIT); output(port+SERCTRL, SERRES); output(port+SERCTRL, SERMODE); output(port+SERCTRL, TTYON); #if BAUD /* Conditional for 1200 baud */ output(CT_CTRL,CT0CTL); /* Set baud rate genrator */ output(CT_232,baudRates[4]); /* Modify for different speeds */ output(CT_232,0); /* Set for 1200 baud */ #else /* === assume the PROM sets it up. === */ #endif /* End conditional */ } int serirdy(port) int port; { return(((input(port+SERSTAT) & SERRRDY) == SERRRDY) ? 0xFF : 0); } char serin(port) int port; { while (serirdy(port) == 0) ; return input(port+SERDATA); } int serordy(port) int port; { return(((input(port+SERSTAT) & SERXRDY) == SERXRDY) ? 0xFF : 0); } serout(port, ch) int port; char ch; { #if BAUD /* Conditional for 1200 baud and XOFF */ while ( ((input(port + SERSTAT) & SERXRDY) != SERXRDY) | ((((input(port + SERDATA)) & 0x7F) ^ XOFF) == 0)); output(port + SERDATA, ch); #else while ( (input(port + SERSTAT) & SERXRDY) != SERXRDY) ; output(port+SERDATA, ch); #endif /* End conditional */ } parordy(port) int port; { int status; status = (input(port)); return (((status & PARBSY) != PARBSY) && ((status & PARFLT) == PARFLT) ? 0xFF : 0); } parout(port, ch) int port; char ch; { register int i, status; i = 0; do { if (--i == 0) /* only check for */ {printstr ("\n\rPrinter Timeout.\n\r"); /* printer ready a */ return; /* finite number of */ } /* times */ } while (!parordy(PAR_B)); /* if printer ready */ output (port, ch); /* print character */ output (PARCTRL, 0x0A); /* set strobe low */ output (PARCTRL, 0x0B); /* set strobe high */ } /************************************************************************/ /* Olivetti keyboard translation table. */ /************************************************************************/ #ifndef LOADER /* NOT needed for the Loader Bios */ char kbtran[256] = { /* Raw key codes for main keypad: RE \ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 - ^ @ [ ; : ] , . / */ /* main keyboard UNSHIFTED. */ 0xDD,'\\', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '^', '@', '[', ';', ':', ']', ',', '.', '/', /* main keyboard SHIFTED */ 0xDE, '|', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '!', '"', '#', '$', '%', '&','\'', '(', ')', '=', '~', '`', '{', '+', '*', '}', '<', '>', '?', /* main keyboard CONTROL -- CTL B and C differ from Olivetti. */ 0xA0,0x7F,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0xE0,0xE1,0xE2,0xE3, 0xE4,0xE5,0xD6,0xE7,0xE8,0xE9,0xEA,0xEB,0x00,0x1B,0x1E,0x1F,0x1D,0xFE,0xFF,0xA4, /* main keyboard COMMAND */ 0xDF,0xF8,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D, 0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0x13,0x1C,0xFC,0xFD,0x9F,0xF9,0xFA,0xA5, /* other keys SP CR S1 S2 KEYPAD . 0 00 1 2 3 4 5 6 7 8 9 + - * / */ /* other keys UNSHIFTED -- CR differs from Olivetti */ ' ','\r',0x7f,0x08, '.', '0',0xA6, '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '*', '/', /* other keys SHIFTED -- CR differs from Olivetti */ ' ','\r',0xA8,0xA9, '.', '0',0xA6,0x1C, 0x9A,0x1D,0x9B,0x9C, 0x9D,0x1E,0x9E,0x1F, 0x2B,0x2D,0x2A,0x2F, /* other keys CONTROL */ ' ','\r',0xA8,0xA9, 0xB0,0xB1,0xB2,0xB3, 0xB4,0xB5,0xB6,0x1B, 0xB8,0xB9,0xBA,0xBB, 0xBC,0xBD,0xBE,0xBF, /* special -- substitute \r for Olivetti's 0xAF. */ '\r','\r','\r','\r' }; #endif /* End conditional */ /************************************************************************/ /* specific I/O procedures for use with iobyte */ /************************************************************************/ /* CRT status, read, write routines */ int crtrs() { return( serirdy(KBD)); } #ifndef LOADER /* NOT needed for the Loader Bios */ char crtrd() { return( kbtran[serin(KBD) & 0xff]); } #endif /* End conditional */ #ifdef LOADER /* Conditional for Loader Bios disable KBD */ #define crtrd nulrd #endif /* End conditional */ int crtws() { return(0xFF); } #define crtwr crt_put /* output routine in PROM */ /* TTY status, read, write routines */ int ttyrs() { return(serirdy(RS232)); } char ttyrd() { return(serin(RS232)); } int ttyws() { return(serordy(RS232)); } ttywr(ch) char ch; { serout(RS232, ch); } /* LPT status, output routines */ int lptws() { return (parordy (PAR_B)); } lptwr(ch) /* ARGSUSED */ char ch; { parout (PAR_A, ch); } /************************************************************************/ /* generic device names, batch, and null devices */ /************************************************************************/ /* the device names are the offset of the proper field in iobyte */ #define CON 0 #define READER 2 #define PUNCH 4 #define LIST 6 /* BATCH status, read, write routines */ #ifndef LOADER /* NOT needed by the Loader Bios */ int batrs() { int genstat(); return genstat(READER); } char batrd() { int genread(); return genread(READER); } batwr(ch) char ch; { genwrite(LIST, ch); } #endif /* End Conditional */ #ifdef LOADER /* NEEDED for the Loader Bios */ #define batrd nulrd #define batrs nulst #define batwr nulwr #endif /* End conditional */ /* NULL status, read, write routines */ int nulst() { return 0xFF; } char nulrd() { return 0xFF; } nulwr(ch) /* ARGSUSED */ char ch; { } /************************************************************************/ /* Generic I/O routines using iobyte */ /************************************************************************/ /* ** IObyte itself. */ char iobyte = 0x41;; /* ** Device operation tables. DEVINDEX is the index into the ** table appropriate to a device (row) and its iobyte index (column) ** ** nonexistent devices are mapped into NUL. */ #define DEVINDEX (((iobyte>>dev) & 3) + (dev * 2) ) int (*sttbl[16])() = { ttyrs, crtrs, batrs, nulst, /* con */ ttyrs, nulst, nulst, nulst, /* reader */ ttyws, nulst, nulst, nulst, /* punch */ ttyws, crtws, lptws, nulst /* list */ }; char (*rdtbl[16])() = { ttyrd, crtrd, batrd, nulrd, ttyrd, nulrd, nulrd, nulrd, nulrd, nulrd, nulrd, nulrd, nulrd, nulrd, nulrd, nulrd }; int (*wrtbl[16])() = { ttywr, crtwr, batwr, nulwr, nulwr, nulwr, nulwr, nulwr, ttywr, nulwr, nulwr, nulwr, ttywr, crtwr, lptwr, nulwr }; /* ** the generic service routines themselves */ int genstat(dev) int dev; { return( (*sttbl[DEVINDEX])() ); } int genread(dev) int dev; { return( (*rdtbl[DEVINDEX])() ); } genwrite(dev, ch) int dev; char ch; { (*wrtbl[DEVINDEX])(ch); } /************************************************************************/ /* Error procedure for BIOS */ /************************************************************************/ bioserr(errmsg) register char *errmsg; { printstr("\n\rBIOS ERROR -- "); printstr(errmsg); printstr(".\n\r"); while(1); } printstr(s) /* used by bioserr */ register char *s; { while (*s) {crtwr(*s); s += 1; }; } #ifdef DEBUG /* Conditional for Disk Debugging Hex output */ puthexd(i) /* put a hex digit to crt */ int i; { i &= 0xf; if (i < 10) crtwr(i + '0'); else crtwr(i + 'a' - 10); } puthexv(i) /* put an int in hex */ int i; { puthexd(i >> 12); puthexd(i >> 8); puthexd(i >> 4); puthexd(i); } #endif /* End conditional */ /************************************************************************/ /************************************************************************/ /* */ /* DISK I/O */ /* */ /************************************************************************/ /************************************************************************/ /************************************************************************/ /* BIOS Table Definitions */ /************************************************************************/ struct dpb { int spt; /* sectors per track */ char bsh; /* block shift = log2(blocksize/128) */ char blm; /* block mask = 2**bsh - 1 */ char exm; /* extent mask */ char dpbjunk; /* dummy field to allign words */ int dsm; /* size of disk less offset, in blocks */ int drm; /* size of directory - 1 */ char al0; /* reservation bits for directory */ char al1; /* ... */ int cks; /* size of checksum vector = (drm+1)/4 */ int off; /* track offset for OS boot */ char psh; /* log2(sectorsize/128) */ char psm; /* physical size mask = 2**psh - 1 */ }; struct dph { char *xltp; /* -> sector translation table */ int dphscr[3]; /* scratchpad for BDOS */ char *dirbufp; /* -> directory buffer (128 bytes) */ struct dpb *dpbp; /* -> disk parameter block */ char *csvp; /* -> software check vector (cks bytes) */ char *alvp; /* -> alloc vector ((dsm/8)+1 bytes) */ }; /************************************************************************/ /* Disk Parameter Blocks */ /************************************************************************/ /* ** CP/M assumes that disks are made of 128-byte logical sectors. ** ** The Olivetti uses 256-byte sectors on its disks. This BIOS buffers ** a track at a time, so sector address translation is not needed. ** ** Sample tables are included for several different disk sizes. */ /* === Olivetti has 3 floppy formats & a hard disk === */ #define SECSZ 128 /* CP/M logical sector size */ #define TRKSZ 32 /* track size for floppies, 1/2 track sz for hd */ #define PSECSZ 256 /* Olivetti physical sector size */ #define PTRKSZ 16 /* physical track size */ #ifndef TRANSFER /* Conditional for Normal bios */ #define MAXDSK 3 /* max. number of disks */ #endif /* End conditional */ #ifdef TRANSFER /* Tranfer Conditional needs an extra dpb define*/ #define MAXDSK 4 /* Disk 4 is a pseudonym for disk 2, with */ #endif /* an old-style dpb to rescue those files. */ /* End conditional */ /***** spt, bsh, blm, exm, jnk, dsm, drm, al0, al1, cks, off, psh, psm */ struct dpb dpb0= /* --- 1 side, 16*256 sector, 35 track. 140kb --- */ { 32, 4, 15, 1, 0, 64, 63, 0xC0, 0, 16, 3}; struct dpb dpb1= /* --- 2 side, 16*256 sector, 35 track. 280kb --- */ { 32, 4, 15, 1, 0, 134, 63, 0xC0, 0, 16, 3}; struct dpb dpb2= /* --- 2 side, 16*256 sector, 80 track. 640kb --- */ { 32, 4, 15, 0, 0, 314, 63, 0xC0, 0, 16, 3}; struct dpb dpb3= /* --- 6 side, 32*256 sector, 180 trk. 8640kb --- */ { 32, 5, 31, 1, 0, 2154, 511, 0xf0, 0, 0, 3}; #ifdef TRANSFER /* Conditional Tranfer dpb defined here */ struct dpb dpb4= /* --- 2 side, 16*256 sector, 35 track. 280kb --- */ { 32, 4, 15, 1, 0, 120, 63, 0xC0, 0, 16, 10}; #endif /* End conditional */ /* bls = 2K dsm = (disk size - 3 reserved tracks) / bls */ /* bls = 4K for hard disk (8640 - 24) / 4 */ #ifdef SECT26 /* Conditional for 8" floppy drives */ /* === The Olivetti does not have 26-sector disks, but many people do. ** The following parameter blocks are provided for their use. */ struct dpb dpbS= /* --- 1 side, 26*128 sector, 77 trk --- */ { 26, 3, 7, 0, 0, 242, 63, 0xC0, 0, 16, 2}; struct dpb dpbD= /* --- 1 side, 26*256 sector, 77 trk --- */ { 52, 4, 15, 0, 0, 242, 63, 0xC0, 0, 16, 2}; #endif /* End conditional */ /************************************************************************/ /* BDOS Scratchpad Areas */ /************************************************************************/ char dirbuf[SECSZ]; char csv0[16]; char csv1[16]; char csv2[32]; #ifdef TRANSFER /* For Transfer conditional */ char csv3[16]; #endif /* End conditional */ char alv0[32]; /* (dsm0 / 8) + 1 */ char alv1[32]; /* (dsm1 / 8) + 1 */ char alv2[2002]; /* (dsm2 / 8) + 1 */ #ifdef TRANSFER /* For Transfer conditional */ char alv3[32]; #endif /* End conditional */ /************************************************************************/ /* Sector Translate Table */ /************************************************************************/ #ifdef SECT26 /* Conditional for 8" floppy drives */ /* === The Olivetti does not have 26-sector disks, but many people do. ** The following translate table is provided for their use. */ char xlt26[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 }; #endif /* End conditional */ char xlt16[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32}; /************************************************************************/ /* Disk Parameter Headers */ /* */ /* Three disks are defined: dsk a: diskno=0, drive 0 */ /* dsk b: diskno=1, drive 1 */ /* dsk c: diskno=2, drive 10 */ /************************************************************************/ #ifndef TRANSFER /* Normal bios dph conditional */ struct dph dphtab[3] = { {xlt16, {0, 0, 0}, dirbuf, &dpb1, csv0, alv0}, /*dsk a*/ {xlt16, {0, 0, 0}, dirbuf, &dpb1, csv1, alv1}, /*dsk b*/ {xlt16, {0, 0, 0}, dirbuf, &dpb3, csv2, alv2}, /*dsk c*/ }; #endif /* End conditional */ #ifdef TRANSFER /* Trnafer conditional with extra dph */ struct dph dphtab[4] = { {xlt16, {0, 0, 0}, dirbuf, &dpb1, csv0, alv0}, /*dsk a*/ {xlt16, {0, 0, 0}, dirbuf, &dpb1, csv1, alv1}, /*dsk b*/ {xlt16, {0, 0, 0}, dirbuf, &dpb3, csv2, alv2}, /*dsk c*/ {xlt16, {0, 0, 0}, dirbuf, &dpb4, csv3, alv3}, /*dsk d*/ }; #endif /* End conditional */ /************************************************************************/ /* Currently Selected Disk Stuff */ /************************************************************************/ int settrk, setsec, setdsk; /* track, sector, disk # */ long setdma; /* dma address with segment info: long */ char trkbuf[TRKSZ * SECSZ]; /* track buffer */ int tbvalid = 0; /* track buffer valid */ int tbdirty = 0; /* track buffer dirty */ int tbtrk; /* track buffer track # */ int tbdsk; /* track buffer disk # */ int dskerr; /* disk error */ /************************************************************************/ /* Disk I/O Procedures */ /************************************************************************/ dskxfer(dsk, trk, bufp, cmd) /* transfer a disk track */ register int dsk, trk, cmd; register char *bufp; /* This is a handy place to keep notes on Olivetti block numbering. For a floppy, bits 3-0 are sector, bit 4 is side, and high-order bits are track. We define a floppy to have twice as many sectors as there are on a track; thus, the sector number overflows to the side bit and all is well. On the hard disk, bits 4-0 are sector (there are 32 per track), and the high-order bits are (track*6)+surface, where surface is in the range 0..5. To make the indexing of trkbuf consistent, we define a hard disk to have only 32 logical (16 physical) sectors per track, like a floppy. Thus we will transfer only half a track to/from the buffer at a time, and the logical sector number will overflow into the real high-order bit of the sector number. This works because we will always move half a track at a time. The tracks and surfaces simply take care of themselves, incrementing through the surfaces and effectively minimizing seeks. */ { int blknum; if (dsk==2) dsk = 10; /* convert hard disk drive # */ #ifdef TRANSFER /* Conditional reasignment for Transfer */ if(dsk==3) dsk = 1; /* for transfer disks */ #endif /* End conditional */ dskerr=0; /* assume no error */ /* do transfer */ #ifdef DEBUG /* Conditional DEBUG output */ blknum = trk*PTRKSZ; printstr("\nxfer block "); puthexv(blknum); printstr(" unit "); puthexd(dsk); printstr(" track "); puthexv(trk); if (cmd == DSKREAD) printstr(" read"); else printstr(" write"); crtwr(10); crtwr(13); #endif /* End conditional */ if (0 != disk_io(dsk, cmd, PTRKSZ, trk*PTRKSZ, map_adr((long)bufp,0))) dskerr=1; } #define wrongtk ((! tbvalid) || (tbtrk != settrk) || (tbdsk != setdsk)) #define gettrk if (wrongtk) filltb() #ifndef LOADER /* NOT needed for Loader Bios */ flush() { if ( tbdirty && tbvalid ) dskxfer(tbdsk, tbtrk, trkbuf, DSKWRITE); tbdirty = 0; } #endif /* End conditional */ filltb() { #ifndef LOADER /* NOT needed by Loader Bios */ if ( tbvalid && tbdirty ) flush(); #endif /* End conditional */ dskxfer(setdsk, settrk, trkbuf, DSKREAD); tbvalid = 1; tbdirty = 0; tbtrk = settrk; tbdsk = setdsk; } dskread() { register char *p; gettrk; p = &trkbuf[SECSZ * (setsec-1)]; /* transfer between memory spaces. setdma is physical address */ mem_cpy(map_adr((long)p, CDATA), setdma, (long)SECSZ); return(dskerr); } #ifndef LOADER /* NOT needed by Loader Bios it doesn't write */ dskwrite(mode) char mode; { register char *p; gettrk; p = &trkbuf[SECSZ * (setsec-1)]; /* transfer between memory spaces. setdma is physical address */ mem_cpy(setdma, map_adr((long) p, CDATA), (long)SECSZ); tbdirty = 1; if ( mode == 1 ) flush(); return(dskerr); } #endif /* End conditional */ char sectran(s, xp) int s; char *xp; { if (xp != 0) return xp[s]; else return s; } struct dph *seldisk(dsk, logged) register char dsk; char logged; { register struct dph *dphp; if (dsk > MAXDSK) return(0L); setdsk = dsk; dphp = &dphtab[dsk]; if (dphp >= dphtab + (sizeof(dphtab)/sizeof(struct dph)) ) return(0L); if ( ! logged ) { /* === disk not logged in. select density, etc. === */ } return(dphp); } /************************************************************************/ /************************************************************************/ /* */ /* BIOS PROPER */ /* */ /************************************************************************/ /************************************************************************/ biosinit() { #ifdef DEBUG /* Conditional banner for DEBUG */ printstr("\r\nCP/M-8000: Olivetti M20 BIOS DEBUG"); #endif /* End conditional */ /* serinit(KBD);*/ /* DON'T init keyboard serial port */ serinit(RS232); /* init rs232 serial port */ tbvalid = 0; /* init disk flags */ tbdirty = 0; /* Following reset of iobyte on each warm boot has been */ /* removed, so that STAT can reassign devices. iobyte */ /* is now initialized on cold boot only. */ /* iobyte = 0x41; */ /* con, list = CRT; rdr, punch = TTY */ } /* In the LOADER bios, the main routine is called "bios", not "_bios" */ #ifdef LOADER /* Loader Bios conditional */ #define _bios bios #endif /* End conditional */ long _bios(d0, d1, d2) int d0; long d1, d2; { switch(d0) { case 0: /* INIT */ biosinit(); break; #ifndef LOADER /* Normal Bios use */ case 1: /* WBOOT */ wboot(); break; #endif /* End conditional */ case 2: /* CONST */ return(genstat(CON)); break; case 3: /* CONIN */ return(genread(CON)); break; case 4: /* CONOUT */ genwrite(CON, (char)d1); break; #ifndef LOADER /* Normal Bios use */ case 5: /* LIST */ genwrite(LIST, (char)d1); break; case 6: /* PUNCH */ genwrite(PUNCH, (char)d1); break; case 7: /* READER */ return(genread(READER)); break; case 8: /* HOME */ settrk = 0; break; #endif /* End conditional */ case 9: /* SELDSK */ return((long)seldisk((char)d1, (char)d2)); break; case 10: /* SETTRK */ settrk = (int)d1; break; case 11: /* SETSEC */ setsec = (int)d1; break; case 12: /* SETDMA */ setdma = d1; break; case 13: /* READ */ return(dskread()); break; #ifndef LOADER /* Normal Bios use */ case 14: /* WRITE */ return(dskwrite((char)d1)); break; case 15: /* LISTST */ return(genstat(LIST)); break; #endif /* End conditional */ case 16: /* SECTRAN */ return(sectran((int)d1, (char*)d2)); break; case 18: /* GMRTA */ return((long)&memtab); break; #ifndef LOADER /* Normal Bios use */ case 19: /* GETIOB */ return((long)iobyte); break; case 20: /* SETIOB */ iobyte = (char)d1; break; case 21: /* FLUSH */ flush(); return((long)dskerr); break; #endif /* End conditional */ case 22: /* SETXVECT */ return(setxvect((int)d1, d2)); break; } /* end switch */ return(0); } /* end bios procedure */ /* End of C Bios */ */