***************************************************************** * * * CP/M-68K BIOS * * for CompuPro CPU-68K and Disk 1 floppy disk controller * * by Michael A. Perry * * Double Density, Double Sided * * with track buffering * * with M-Drive/H support * * with DISK 2 support * * with DISK 3 support * * * * Last modified: 10/24/83 by J. R. Stoner * * * ***************************************************************** .globl _init * bios initialization entry point .globl _ccp * ccp entry point model = 2 * CP/M-68K version make = 1 * CP/M-68K correction number patch = 8 * CompuPro CP/M-68k CBIOS revision level syssup = 0 * zero for system support, 1 for I/O 3 or 4 lomem = $400 * bottom of tpa himem = $35000 * top of TPA xd8 = 0 * zero to select the second floppy disk drives fdbase = 0 * set to the first floppy major device number hmdrive = 0 * zero to select the M-DRIVE/H memory disk d2m10 = 1 * zero to select 10 Mb hard disk (8") d2m20 = 1 * zero to select 20 Mb hard disk (8") d2f20b = 1 * zero to select BE 20 Mb hard disk d2f40b = 1 * zero to select BE 40 Mb hard disk d2base = 4 * set to the first DISK2 major device number d3m5 = 1 * zero to select 5 Mb hard disk (ST506) d3m40 = 1 * zero to select 40 Mb hard disk (Q540) d3base = 4 * set to the first DISK3 major device number stepr8 = 3 * seek step rate (ms) uload8 = 240 * head unload time (ms) hdlt8 = 35 * head settle time (ms) srt8 = 16-stepr8 hut8 = uload8/16 disk2 = d2m10*d2m20*d2f20b*d2f40b disk3 = d3m5*d3m40 hard = disk2*disk3 d3scnt = 9 * 9 1Kb sectors/track d2scnt = 11 * 11 1Kb sectors/track d2_rhd = $e0 * DISK2 read header d2_wrt = $d0 * DISK2 write data d2_read = $c8 * DISK2 read data d2_sin = $a0 * DISK2 step in d2_sec = $98 * DISK2 set sector d2_head = $90 * DISK2 set head d2_cyl = $88 * DISK2 set cylinder d2_strb = $80 * DISK2 drive strobe d2_sou = $80 * DISK2 step out d2_rst = $04 * DISK2 reset drive fault clear bit d2_attn = $80 * DISK2 interrupt acknowledge status bit d2_tout = $40 * timeout error bit d2_crc = $20 * CRC error bit d2_ovr = $10 * data transfer overrun bit d2_nrdy = $08 * drive not ready status bit d2_sekd = $04 * seek complete flag d2_wrtf = $02 * write fault (0 = true) d2_cyl0 = $01 * cylinder 00 (0 = true) selpri = 10 * the DMA priority of the selector channel selbyte = $2f-selpri * selector channel command byte d3_attn = 0 * the command byte for waking up the DISK3 d3_rst = 1 * the command byte for resetting the DISK3 d3_nop = 0 * iopb command for a NOP d3_glob = 2 * iopb command for writing global variables d3_spec = 3 * iopb command for a specify d3_map = 4 * iopb command for a set_map d3_home = 5 * iopb command for a recalibrate d3_rdwr = 8 * iopb command for read or write data d3_stat = 12 * iopb command for reading the drive status d3_cmpl = $ff * iopb return value for success d3bmtrk = 2 * relocated data head d3bmcyl = 0 * relocation cylinder d3bmlen = d3scnt-1 d3bmsec = d3bmlen*8 * logical sector of the sector map defiopb = $50 * the address of the first iopb at reset k_cr = 13 * ASCII newline k_lf = 10 * ASCII line feed k_cls = 26 * clear screen for VT52 emulations (Lear,TVI) dpblen = 16 * length of a DPB * here be the I/O address standard assignments: iobase = $ff0000 * base of 68000's memory mapped i/o sysdata = iobase+$5c * System Support console port sysstat = sysdata+1 sio = iobase+$10 * serial i/o data port siostat = sio+1 * serial i/o status port select = sio+7 * Interfacer III or IV user number mddata = iobase+$c6 * memory disk data port mdadr = mddata+1 * memory disk address port dstat = iobase+$c0 * disk status port ddata = dstat+1 * disk data port ddma = dstat+2 * disk dma port d2ctl = iobase+$c8 * DISK2 control port d2stat = d2ctl * DISK2 status port d2data = d2ctl+1 * DISK2 data port selchan = iobase+$f0 * SELECTOR CHANNEL port d3port = iobase+$90 * DISK3 port * relative user numbers on an INTERFACER 3/INTERFACER 4 board console = 7 printer = 4 prnter1 = 5 * disk types in the software DPB extension: * 8" floppy disks fp8s1d0 = $00 * 1 sided, density 0: 128b fp8s2d0 = $01 * 2 sided, density 0: 128b fp8s1d1 = $02 * 1 sided, density 1: 256b fp8s2d1 = $03 * 2 sided, density 1: 256b fp8s1d2 = $04 * 1 sided, density 2: 512b fp8s2d2 = $05 * 2 sided, density 2: 512b fp8s1d3 = $06 * 1 sided, density 3: 1024b fp8s2d3 = $07 * 2 sided, density 3: 1024b maxftyp = fp8s2d3+1 * number of 8" floppy disk DPB's dsk2typ = $40 * DISK2 DPB type dsk3typ = $50 * DISK3 DPB type memtype = $80 * M-DRIVE/H DPB type dphlen = 26 * length of disk parameter header retries = 10 * number of retries for floppy I/O _init: move.l #signon,a0 * say hello bsr type clr.b dirty .ifeq disk2 bsr d2init .endc .ifeq disk3 clr.b d3iopb+2 * right now we initialize only drive zero bsr d3spec * go specify and set up the mapping tst.b d0 bne inita * will be zero here if no problems lea d3iopb+3,a2 * point at some of the iopb clr.b (a2)+ * set up in track/sector mode move.b #retries,(a2)+ * set up for a retry count of 10 move.b #4,(a2)+ * set up for 4 drives in the system move.l #d3_glob,d0 * set up the global parameter command bsr d3exec * and go do it inita: nop * yuck! .endc .ifeq hmdrive bsr setsize * auto-size M-Drive/H .endc move.l #trapentry,$8c * set up trap #3 handler clr.l d0 * log on disk A, user 0 rts trapentry: cmpi #functs,d0 * function number out of bounds bge badtrap * yes lsl #2,d0 * multiply bios function by 4 movea.l 6(pc,d0),a0 * get handler address jsr (a0) * call handler badtrap: rte table: .dc.l _init * cold boot .dc.l wboot * warm boot .dc.l constat * console status .dc.l conin * console input .dc.l conout * console output .dc.l lstout * printer output .dc.l pun * user device output .dc.l rdr * user device input .dc.l home * home the logical disk .dc.l seldsk * select a logical disk .dc.l settrk * set a logical track .dc.l setsec * set a logical sector .dc.l setdma * set the DMA pointer .dc.l read * read a logical sector .dc.l write * write a logical sector .dc.l listst * printer status .dc.l sectran * translate logical to physical sectors .dc.l setdma * for historical reasons .dc.l getseg * get the memory segment .dc.l getiob * get the I/O map byte .dc.l setiob * set the I/O map byte .dc.l flush * flush the disk buffers .dc.l setexc * set a trap vector functs=(*-table)/4 * discard buffer abort: clr.b cpmflg * default to the 8" SSSD disk clr.b dirty * do not flush the floppy disks wboot: clr.l news * mark all drives as new bsr flush * write buffer to disk moveq #$ff,d3 * mark buffer as empty move.b d3,bufdrv * no buffered drive move d3,buftrk * no buffered track move.b d3,bufsid * no buffered head jmp _ccp * go to the CP/M system now .ifeq syssup * system support console being used here constat: move.b sysstat,d0 * get status byte andi.w #2,d0 * data available bit on? bne yes * branch if true moveq #0,d0 * set result to false rts yes: moveq.l #$ff,d0 * set result to true rts conin: move.b sysstat,d0 * get status byte andi.w #2,d0 * data available bit on? beq conin * wait until key pressed move.b sysdata,d0 * get key and.l #$7f,d0 * clear all but low 7 bits rts conout: move d1,-(a7) * save character bsr flush * write data to disk move (a7)+,d1 * restore character emit: move.b sysstat,d0 * get status and.b #$1,d0 * wait for transmitter buffer empty beq emit move.b d1,sysdata * and output it rts .endc .ifne syssup * interfacer console being used here constat: move.b #console,select move.b siostat,d0 * get status byte andi.w #2,d0 * data available bit on? bne yes * branch if true moveq #0,d0 * set result to false rts yes: moveq.l #$ff,d0 * set result to true rts conin: move.b #console,select move.b siostat,d0 * get status byte andi.w #2,d0 * data available bit on? beq conin * wait until key pressed move.b sio,d0 * get key and.l #$7f,d0 * clear all but low 7 bits rts conout: move d1,-(a7) * save character bsr flush * write data to disk move (a7)+,d1 * restore character emit: move.b #console,select move.b siostat,d0 * get status and.b #$1,d0 * wait for transmitter buffer empty beq emit move.b d1,sio * and output it rts .endc listst: move.b cpmiob,d0 and.l #$c0,d0 beq lst2 move.b #prnter1,select * select the relative user for the UL1 device bra lst1 lst2: move.b #printer,select * select the relative user for the LPT device lst1: move.b siostat,d0 * get the status and.b #1,d0 * mask the bit bne yes * if a character is ready moveq #0,d0 * set for not ready rts pun: lstout: move.b #printer,select * select the printer or user devce move.b siostat,d0 * get status and.b #$1,d0 * wait for transmitter buffer empty beq lstout move.b d1,sio * and output it rts rdr: move.b #printer,select * select the user device move.b siostat,d0 * get status byte andi.w #2,d0 * data available bit on? beq rdr * wait until character available move.b sio,d0 * get character rts cr: move.b #k_cr,d1 * emit a newline bsr emit move.b #k_lf,d1 * emit a linefeed too bsr emit rts type: move.b (a0)+,d1 * get the next character beq typex * if zero we end the string bsr emit * else we emit the character bra type * go back for more typex: rts derror: bsr type * type the selected string move.l #drverr,a0 * print the prompt bsr type move.b drive,d1 * get the logical drive (major device) add #'A',d1 * bias for ASCII bsr emit * type it bsr cr * newline move.l #qmess,a0 * ask for something to do bsr type bsr conin * get a character or.b #' ',d0 * bias to make it lower case cmp.b #'r',d0 * do we have to retry it bne abort * no so we abort the I/O bsr cr * newline rts * select disk given by register d1.b * return its dph in d0.l seldsk: moveq #0,d0 * default to a bad type cmp.b #16,d1 * major device out of bounds? bcc selx * if yes move.b d1,cpmdrv * set the current major device mulu #4,d1 * set up for a pointer lea drvtab,a0 * point a0 at the DPH address table add.l d1,a0 * index it move.l (a0),d0 * get the DPH address into d0 beq selx * if zero it is a bad drive move.l d0,a0 * point a0 at the DPH move.l 14(a0),d1 * point d1 at the DPB sub.l #dpb0,d1 * we are going to get the offset from dpb0 divu #dpblen,d1 * d1 contains the DPB number lea typbase,a1 * point a1 at the DPB extension table move.b 0(a1,d1),cpmflg * set the major device type current move.b cpmflg,d1 * get the major device type cmp.b #maxftyp,d1 * is it a floppy disk? bcs selfloppy * if yes .ifeq hmdrive cmp.b #memtype,d1 * is it a memory drive? bne selo * if not, we already have a DPH in d0 move.l #dphm,d0 * point d0 at the memory drive DPH bra selx * and leave selo: nop .endc .ifeq disk2 cmp.b #dsk2typ,d1 * is it a DISK2 hard disk? beq selx * if yes, the DPH is already in d0 .endc .ifeq disk3 cmp.b #dsk3typ,d1 * is it a DISK3 hard disk? beq selx * if yes, the DPH is already in d0 .endc clr d0 * if not, return a bad major device bra selx selfloppy: lea news,a5 * point a5 at the disk mount table move.b cpmdrv,d1 * get the major device number (0..3) tst.b 0(a5,d1) * use it to check the current mount status bne selx * if the drive is already mounted bsr setdense * go get the density and mark it as mounted selx: rts * set track number to 0 home: clr d1 * we defer any seeking for later * set track number to value in d1 settrk: move.w d1,cpmtrk * make the logical track as current rts * defer the head seeking for later * set logical sector number to value in d1 setsec: move.b d1,cpmsec * make the logical sector as current rts * use table in d2 to translate logical sector in d1 to the physical sector sectran: tst.l d2 * is the table address zero beq notran * if yes move.l d2,a0 * point a0 at the table ext.l d1 * make d1 a longword move.b #0(a0,d1),d0 * index d1 into a0 for the skewed sector ext.l d0 * make d0 a longword bra sectranx * and leave notran: move d1,d0 * use the logical sector as the return value sectranx: rts * set data address to value in d1 setdma: move.l d1,cpmdma * make the new address current rts read: move.b cpmflg,d0 * get the major device type .ifeq hmdrive cmp.b #memtype,d0 * memory drive? beq vread * perform virtual disk read .endc bsr inbuffer * read sector into buffer tst d0 bne readx * exit on error move.l d2,a0 * a0 points to data move.l d1,a1 * a1 points to dma address bsr transfer * move data from buffer to dma address readx: rts write: move.b d1,cpmtyp * save the write type status for later .ifeq hmdrive move.b cpmflg,d0 * get the major device type cmp.b #memtype,d0 * memory drive? beq vwrite * perform virtual disk write .endc bsr inbuffer * read track into buffer tst d0 * if the pre-read was faulty bne writex * exit on error move.l d1,a0 * a0 points to new data move.l d2,a1 * a1 points into buffer bsr transfer * move data from dma address into buffer move.b #1,dirty * set the DISK1 dirty flag (default) move.b cpmtyp,d1 * get the write type back cmp.b #1,d1 * was the write to a directory sector? bne writex * if not bsr flush * if yes, go flush it now and not later writex: rts * write buffered data to disk, if it has been updated flush: nop .ifeq hard move.b bufdrv,d0 * get the currently buffered major device and.l #$f,d0 * mask it lea hdtab,a5 * get the drive select table base tst.b 0(a5,d0) * see if the currently buffered drive is hard bne d2flush * if non-zero we do a hard disk flush .endc clr.l d0 * default is return successful tst.b dirty * has data been written into buffer? beq flushx * exit if not move.b bufdrv,d7 * get the current major device cmp.b #4,d7 * is it out of bounds (0..3) bpl flushx * if yes, exit now move.b bufdrv,drive * make the buffered major device current move buftrk,track * and the cylinder move.b bufsid,side * and the head move.b #5,cmd * set up the FDC write data command bsr rdwr * go do the I/O flushx: clr.b dirty * always clear dirty flag rts getseg: move.l #region,d0 * return address of memory region table rts * return the current I/O byte in d0 getiob: move.b cpmiob,d0 and.l #$ff,d0 rts * set the current I/O byte to whatever is in d1 setiob: move.b d1,cpmiob clr.l d0 rts setexc: andi.l #$ff,d1 * do only for exceptions 0 - 255 lsl #2,d1 * multiply exception nmbr by 4 movea.l d1,a0 move.l (a0),d0 * return old vector value move.l d2,(a0) * insert new vector rts * send byte in d4 to disk controller send: btst #7,dstat * is the FDC command buffer busy? beq send * if yes move.b d4,ddata * if not go send the command byte rts * read all result bytes from disk controller results: lea stat,a0 * point a0 at the first byte of the table res1: btst #7,dstat * is the FDC command buffer busy? beq res1 * if yes btst #6,dstat * is the CPU expecting a status beq resx * if not move.b ddata,(a0)+ * get the status byte into the next entry bra res1 * go check it for any more resx: rts * set disk stepping rate and head load time specify: moveq #3,d4 * send the specify command bsr send moveq #srt8*16+15,d4 * send the step rate and the head unload time bsr send moveq #hdlt8,d4 * send the head load time bsr send rts * wait until Disk 1 interrupt flag becomes true waitint: btst #7,ddma * is the FDC still doing some dma? beq waitint * if yes rts * Sense Interrupt Status: must be used after seek or home sis: bsr waitint * see if the FDC is ready for something new moveq #8,d4 * send the sense interrupt status command bsr send bsr results * read all result bytes move.b stat,d4 * status for correct drive? move.b drive,d1 * get the FDC major device (0..3) eor d4,d1 * XOR with the device number from the FDC and #3,d1 * mask the lower two bits bne sis * should be zero - try again if not andi.b #$f8,d4 * ignore drive number cmpi.b #$20,d4 * successful seek? beq sisx * exit if ok move.b stat,d4 * get the FDC status byte s0 andi.b #$18,d4 * is drive not ready? beq sisx * exit if ok move.l #nrerr,a0 * else print message bsr derror * and ask to retry sisx: rts * home the disk restore: moveq #2,d2 * 3 retries rest1: moveq #$7,d4 * send the recalibrate disk command bsr send move.b drive,d4 * send the current major device (0..3) bsr send bsr sis * get status dbeq d2,rest1 * retry on error rts * position the disk head to the track in d6.b seek: moveq #2,d2 * 3 retries seek1: moveq #$0f,d4 * send the seek command bsr send move.b drive,d4 * send the current major device (0..3) bsr send move.b d6,d4 * send the new cylinder bsr send bsr sis * get status dbeq d2,seek1 * retry on error beq seekx * exit if ok bsr restore * home disk if the retries did not help seekx: rts * Sense Drive Status * tests for double sided sds: move.b #4,d4 * send the sense drive status command bsr send move.b drive,d4 * send the current major device (0..3) bsr send bsr results * get all of the status bytes move.b stat,d5 and.b #8,d5 * mask only the double sided bit rts * Read ID field: find density rid: move.b #$4a,d4 * send the read ID/MFM command bsr send move.b drive,d4 * send the current major device (0..3) bsr send bsr waitint * wait until done bsr results * get all of the status bytes rts * find the density of the disk in the selected drive, and set * its density flag, dpb and xlt to the new values setdense: move.l d0,a3 * save the DPH address move.b cpmdrv,drive * make the new major device current bsr restore * recalibrate the new disk bsr sds * check the number of sides move.b #1,d6 * set cylinder 1 (to see if the disk is MFM) bsr seek * move the head bsr rid * get density into the status table moveq #0,d7 * set the table pointers to the default values move.l d7,d3 move stat,d2 * get the FDC status byte s0 and #$fcff,d2 * ignore drive number bne setd0 * bad status means single density, else move.b stat+6,d3 * get density byte (0..3) setd0: lea dens,a0 * point to density table move.b drive,d7 * index by minor device move.b d3,0(a0,d7) * update density table entry lea sides,a0 * point to double sided flags move.b d5,0(a0,d7) * update side flag lea news,a0 * point to new drive flags move.b #$ff,0(a0,d7) * mark drive as currently mounted move.l d3,d4 * d3 and d4 have density (0..3) mulu #dpblen*2,d3 * index the pointer by DPB density type (even) tst.b d5 * d5 is 1 for two-sided disk beq setd1 * if one-sided add #dpblen,d3 * set the pointer to the next odd DPB number setd1: add.l #dpb0,d3 * point d3 at selected DPB (must be the lowest) mulu #4,d4 * index the XLT pointer by the density add.l #xlts,d4 * point d4 at the XLT table address list move.l d4,a1 * point a1 at it too move.l (a1),(a3) * move the XLT address to the DPH XLT location move.l d3,14(a3) * move the DPB address to the DPH DPB location move.l a3,d0 * return the DPH rts * set up the command buffer for the next read or write setcmd: moveq #0,d7 move.l d7,d2 move.b drive,d7 * index by drive number move track,d6 beq setcmd1 * force single density on track 0 lea dens,a1 * point a0 at density table move.b 0(a1,d7),d2 * get density byte beq setcmd1 * if not single density or.b #$40,cmd * then set mfm bit setcmd1: move.l d2,d3 * d2 and d3 have density byte = n mulu #3,d3 * index into gap table add.l #gaps,d3 move.l d3,a1 * point a1 at gpl/dtl entry lea cmd+1,a0 * set up the command buffer move.b side,d5 lsl.b #2,d5 * side is bit 2 of drive byte or.b d5,d7 move.b d7,(a0)+ move.b track+1,(a0)+ * low byte of track number move.b side,(a0)+ * redundant side byte move.b #1,(a0)+ * setor move.b d2,(a0)+ * density byte move.b (a1)+,(a0)+ * eot = spt move.b (a1)+,(a0)+ * gpl move.b (a1)+,(a0)+ * dtl rts * set dma address for data transfer command: lea dma+1,a0 * point a0 at the current dma address move.b (a0)+,ddma * send the high byte move.b (a0)+,ddma * send the middle byte move.b (a0)+,ddma * send the low byte lea cmd,a0 * point a0 at command buffer moveq #8,d3 * set up for 8 bytes and fall through... com: move.b (a0)+,d4 * get the next byte bsr send * and send it dbra d3,com * maybe send some more bsr waitint * wait for the command to finish bsr results * read all result bytes rts * Common routine to read or write a physical track rdwr: move.l #buffer,dma * dma transfers are to and from buffer move.w track,d6 * perform a seek to desired cylinder bsr seek bsr setcmd * set up the command buffer moveq #retries,d5 * 10 retries rdwr1: bsr command * issue command move.w stat,d4 * was disk i/o successful? and #$f8ff,d4 * ignore the minor device sub #$4080,d4 * 0x4080 is status for good read or write beq rdwrok * exit if ok move.w d4,d6 * otherwise: and.b #8,d4 * drive not ready? beq rdwr2 move.l #nrerr,a0 * display message bsr derror * and ask for a retry or abort bra rdwrtry * go do a retry rdwr2: and.b #2,d6 * write protected? beq rdwrtry * if not, this is a weird error, go do a retry move.l #wperr,a0 * display message for write protected bsr derror * and ask for a retry or abort rdwrtry: dbra d5,rdwr1 * retry rdwrerr: moveq #1,d0 * return the error condition rts rdwrok: moveq #0,d0 * return success rts * make certain that the desired data is in the buffer * if it was not, then read it from the disk after flushing old contents inbuffer: nop .ifeq hard move.b cpmflg,d0 * get the major device .ifeq disk2 cmp.b #dsk2typ,d0 * is it a DISK2? beq d2buf * if yes .endc .ifeq disk3 cmp.b #dsk3typ,d0 * is it a DISK3 beq d3buf .endc .endc clr.l d0 * clear the registers clr.l d7 clr.l d6 clr.l d5 clr.l d4 move.b cpmdrv,d7 * get the selected major device move.b d7,rdrive * save it move cpmtrk,d6 * get the selected logical track clr.b rside * default the saved side to zero lea sides,a1 * point at the sidedness table tst.b 0(a1,d7) * test the device for sidedness beq inbuf3 * if not move d6,d5 * copy the track and #1,d5 * get head number move.b d5,rside * and save it lsr #1,d6 * shift the physical cylinder by one (div2) inbuf3: move d6,rtrack * save the physical cylinder lea dens,a1 * point a1 at density table move.b 0(a1,d7),d2 * get density byte (0..3) cmp.b bufdrv,d7 * compare the saved and current major device bne inbuf1 * if not the same cmp buftrk,d6 * compare the saved and current cylinders bne inbuf1 * if not the same cmp.b bufsid,d5 * compare the save and current heads beq inbuf2 * if the same we do not do I/O yet inbuf1: bsr flush * write old buffer contents move.b rdrive,drive * make the saved major device new move rtrack,track * make the saved cylinder new move.b rside,side * make the saved head new move.b #6,cmd * set up the command table for an FDC read data bsr rdwr * go do the I/O move.b drive,bufdrv * make the new major device buffered move track,buftrk * make the new cylinder current move.b side,bufsid * make the new head current inbuf2: move.l #0,d2 * clear the register move.b cpmsec,d2 * get the selected logical sector sub #1,d2 * offset the sector from physical zero mulu #128,d2 * index by the length of a sector (128b) add.l #buffer,d2 * d2 now points at the new buffered data move.l cpmdma,d1 * point d1 at the selected dma address rts * move 128 bytes of data from address in a0 to address in a1. transfer: moveq #31,d2 * the byte count (128 moves) mov: move.l (a0)+,(a1)+ * move 4 bytes dbra d2,mov * maybe go move some more rts .ifeq hard d2flush: clr.l d0 * set a default of success tst.b dirty * check the dirty flag beq d2fx * if the buffer is already available move.b bufdrv,drive * make the buffered drive current move buftrk,track * make the buffered cylinder current move.b bufsid,side * make the buffered head current move.b bufsec,sec * make the buffered physical sector current move.b #1,hdtemp * set the command for a write bsr d2rw * go do the I/O clr.b dirty * make the buffer available d2fx: rts * and leave .endc .ifeq disk2 * go pre-read a physical sector if it is not already in the buffer d2buf: clr.l d0 * clear the registers clr.l d7 clr.l d6 clr.l d5 clr.l d4 move.b cpmdrv,d7 * get the selected logical major device and.b #$f,d7 * mask it move.b d7,rdrive * and save it move cpmtrk,d6 * get the selected logical cylinder clr.b rside * clear the saved head move d6,d5 * and copy it for later use .ifeq d2m20*d2f40b and.b #7,d5 * we have 8 heads in these drives lsr #3,d6 * so we divide by 8 heads for the cylinder .endc .ifeq d2m10*d2f20b and.b #3,d5 * we have 4 heads in these drives lsr #2,d6 * se we divide by 4 heads for the cylinder .endc move d6,rtrack * save the physical cylinder move.b d5,rside * and the physical R/W head move.b cpmsec,d4 * get the selected logical sector lsr.b #3,d4 * divide by 8 sectors per physical sector .ifeq d2f20b*d2f40b and.l #$1f,d4 * we allow up to 32 physical sectors here .endc .ifeq d2m10*d2m20 and.l #$f,d4 * we allow up to 16 physical sectors here .endc move.b d4,rsec * save the physical sector cmp.b bufdrv,d7 bne d2ba * if the selected major device is resident cmp.b buftrk+1,d6 * if the selected cylinder (one byte) is not bne d2ba * already resident cmp.b bufsid,d5 bne d2ba * if the selected head is not already resident cmp.b bufsec,d4 * if the selected physical sector is resident beq d2bb * we bypass the pre-read and continue d2ba: bsr flush * flush the dirty buffer, if any move.b rdrive,drive * make the saved major device new move rtrack,track * make the save cylinder new move.b rside,side * make the saved head new move.b rsec,sec * make the saved physical sector new clr.b hdtemp * set the command for a read bsr d2rw * go do the I/O move.b drive,bufdrv * buffer the new major device move track,buftrk * buffer the new cylinder move.b side,bufsid * buffer the new head move.b sec,bufsec * buffer the new physical sector tst.b d0 * see if the I/O was successful bne d2bc * if not, we return non-zero to the syscall d2bb: move.b cpmsec,d2 * get the selected logical sector and.l #7,d2 * mask the least significant 8 sectors/block mulu #128,d2 * multiply by the length of a logical sector add.l #buffer,d2 * index into the sector buffer move.l cpmdma,d1 * set the other pointer to the selected address d2bc: rts .endc .ifeq hard d2rw: move.l #buffer,dma * point the location at the (only) buffer .ifeq disk3 move.b drive,d5 * get the new major device and.l #$f,d5 * mask it mulu #4,d5 * multiply by the length of a pointer lea drvtab,a5 * index into the DPH pointer table move.l 0(a5,d5),a5 * get a DPH pointer move.l 14(a5),d5 * get a DPB pointer from the DPH sub.l #dpb0,d5 * subtract the first DPB address divu #dpblen,d5 * divide by the length of a DPB and.l #$ff,d5 * mask it just in case lea typbase,a5 * index into the major device type table move.b 0(a5,d5),d5 * get the major device type cmp.b #dsk3typ,d5 * is it a DISK3? beq d3rw * if yes .endc .endc .ifeq disk2 move.b drive,d5 * get the new major device and.l #$f,d5 * mask it add.l #hdtab,d5 * index into the minor device table move.l d5,a5 * point a5 at the minor device bsr d2select * go see if the drive is available bne d2rwc * if non-zero we wanted to abort move.b (a5),d2 * get the minor device and.l #3,d2 * mask the unit number lea d2cyl,a0 * point a0 at the current head position table move.b 0(a0,d2),d3 * get the current position and.l #$ff,d3 * mask the one byte cylinder cmp.b #$ff,d3 * if all ones we are in uncharted territory bne d2rwa * if not we continue bsr d2home * else we move the head to cylinder zero d2rwa: move track,d4 * get the new cylinder cmp.b d4,d3 * compare it with the old one beq d2rwb * if equal we continue without seeking bsr d2seek * else we move the head to a new position d2rwb: bsr d2xfer * go do the physical I/O command tst.b d0 * if non-zero we have some error beq d2rwc * else we quit gracefully d2rwm: clr.l d0 * clear a register lea d2bmap,a1 * point at the map move #d2scnt-1,d2 * set the map length move.b track+1,d0 * get the cylinder lsl.l #8,d0 * shift it move.b side,d0 * get the head lsl.l #8,d0 * shift it move.b sec,d0 * get the sector d2bmlp: clr.l d1 * clear another register move.b (a1)+,d1 * get the map cylinder lsl.l #8,d1 * shift it move.b (a1)+,d1 * get the map head lsl.l #8,d1 * shift it move.b (a1)+,d1 * get the map sector cmp.l d0,d1 * compare with the old stuff beq d2rwn * if a match dbra d2,d2bmlp * else we try the next map entry move.l #$ff,d0 * we have exhausted the map here bra d2rwp * so return a failure d2rwn: move.l a1,d0 * get the map pointer sub.l #d2bmap+3,d0 * decrement by one entry divu #3,d0 * divide by the length of an entry move.b d0,sec * set it up as the sector move track,d3 clr track * set up for cylinder 0 clr d4 bsr d2seek * go move the head move.b #2,side * select head 2 bsr d2xfer * try to move the data from the new track tst.b d0 * test the transfer for a failure beq d2rwc * if we have succeeded d2rwp: bsr d2home * move the head to cylinder zero move track,d4 * get the new cylinder bsr d2seek * move the head to it bsr d2xfer * go try the I/O again tst.b d0 * test for error (non-zero) beq d2rwc * if no error we are fine move.b #$ff,0(a0,d2) * else make the head position indeterminate d2rwc: rts d2select: bsr d2ready * check the device for ready status beq d2sx * if ready we return bsr d2ready * if not we ignore and try again beq d2sx * return now if ready lea d2msg,a0 * print the state of the device bsr type d2sel1: bsr constat * check for a key stroke beq d2sel2 * if none we try to get the device ready again bsr conin * read the character out of the USART bra d2sx * and quit d2sel2: bsr d2ready * see if the device is ready bne d2sel1 * if not we loop some more d2sx: rts d2ready: move.b (a5),d0 * get the minor device move.b d0,d1 * copy it for later and.b #$f,d0 * mask the disk unit or.b #d2_strb+d2_rst,d0 * combine with a device select move.b d0,d2ctl * send it nop * wait 1.5 ms. for the select lines to settle nop nop add.b #'0'-d2_strb-d2_rst,d0 * add an ASCII bias to the unit number move.b d0,d2msgx * and put it into the not ready prompt and.b #$f0,d1 * mask the drive select line in the copy move.b d1,d2data * send it (selecting head zero always) move.b d2stat,d0 * get some status and.b #d2_attn+d2_nrdy,d0 * mask for unit ready eor.b #d2_attn,d0 * flip the attention bit rts * move the selected drive/head to cylinder 0 * entry: a0 points at the cylinder table * d2 = the minor device number * exit: d0:0..7 = zero for success * d3 = the new cylinder (always zero) d2home: move.b d2stat,d1 * get drive status and.b #d2_cyl0,d1 * return now if we are already at cylinder zero beq d2home3 move.b d2,d0 * get the minor device or.b #d2_sou+d2_rst,d0 * combine with a step out command move.b d0,d2ctl * send it nop * wait for 1.5 ms. for the status to settle nop nop clr.b d4 * set the counter for 256 step pulses d2home1: move.b d2data,d0 * step the head out by one cylinder sub.b #1,d4 * decrement the counter bne d2home1 * and loop if not done d2home2: move.b d2stat,d0 * get some status and.b #d2_sekd,d0 * mask the seek complete line bne d2home2 * keep checking until the line goes true d2home3: move.b #0,0(a0,d2) * set the current cylinder in the table to zero clr.w d3 * and return zero rts * move the selected drive/head to a new cylinder * entry: a0 points at the cylinder table * d2 = the minor device number * d3 = the previous cylinder * d4 = the new cylinder d2seek: move.b #d2_sou,d0 * default to a step out command sub.b d4,d3 * get the difference of the old and the new bcc d2seek0 * if the new was lower than the old cylinder move.b #d2_sin,d0 * else we set up for a step in command neg.b d3 * and take the absolute value of the difference d2seek0: and.l #$ff,d3 * mask to one byte or.b d2,d0 * or the unit number with the command move.b d0,d2ctl * and send it nop * wait 1.5 ms. for the lines to settle nop nop d2step1: move.b d2data,d0 * step the head by one cylinder sub.b #1,d3 * bump the counter (for buffered seeks only) bne d2step1 * and keep doing this until we are done d2step2: move.b d2stat,d0 * get some status and.b d2_sekd,d0 * check for a seek complete bne d2step2 * and keep doing this until is it complete d2skx: move.b d4,0(a0,d2) * move the new cylinder into the position table rts * move some data * entry: a5 points at the physical minor device d2xfer: moveq #retries-1,d7 * set the retry counter d2xlp: move.b (a5),d1 * get the minor device and.b #3,d1 * mask the disk unit or.b #d2_strb+d2_rst,d1 * combine with a device select command move.b d1,d2ctl * and send it nop * wait for 1.5 ms. to allow the select nop * lines to settle nop move.b (a5),d1 * get the minor device and.b #$f0,d1 * mask the select lines or.b side,d1 * combine with the new head move.b d1,d2data * and send it move.b #d2_cyl,d2ctl * sent the select cylinder command move.b track+1,d2data * and send the new cylinder move.b #d2_head,d2ctl * send the select head command move.b side,d2data * and send the new head move.b #d2_sec,d2ctl * send the select sector command move.b sec,d2data * and send the new physical sector move.b selchan,d1 * stop the SELECTOR CHANNEL move.b dma+1,selchan * send the high address byte move.b dma+2,selchan * send the middle address byte move.b dma+3,selchan * send the low address byte move.b (a5),d1 * get the minor device and.b #3,d1 * mask the unit number tst.b hdtemp * check if we read or write beq d2xc * if we are reading move.b #selbyte,selchan * move the write to device command or.b #d2_wrt,d1 * combine the unit with the write data command bra d2xd * go send it d2xc: move.b #selbyte+$80,selchan * move the read from device command or.b #d2_read,d1 * combine the unit with the read data command d2xd: move.b d1,d2ctl * and send the command d2xe: move.b d2stat,d0 * go get some status blt d2xe * wait for the command complete line to go true move.b d2stat,d0 * get the whole status and.b #d2_tout+d2_crc+d2_ovr+d2_nrdy+d2_sekd+d2_wrtf,d0 * mask some eor.b #d2_wrtf,d0 * make the write fault logically positive tst.b d0 * check for problems beq d2xf * of zero we are okay and can leave nicely dbra d7,d2xlp * if not loop for a retry d2xf: move.b #d2_strb,d2ctl * select device zero to restart the FSM, move.b #$10,d2data * when we use more than one hard disk rts * here d0 is non-zero when retries have failed d2init: move.b #d2base,d1 bsr seldsk move.l #d2bmap,cpmdma move.w #0,cpmtrk move.b #d2scnt*8-8,cpmsec bsr read rts .endc .ifeq disk3 d3buf: clr.l d0 * clear some registers clr.l d7 clr.l d6 clr.l d5 clr.l d4 move.b cpmdrv,d7 * get the selected major device and.l #$f,d7 * mask it move.b d7,rdrive * and save it move cpmtrk,d6 * get the selected logical track move d6,rtrack * and save it move.b cpmsec,d4 * get the selected logical sector lsr.b #3,d4 * divide by the length of a phsical sector move.b d4,rsec * and save it cmp.b bufdrv,d7 bne d3ba * if the selected major device is not resident cmp buftrk,d6 bne d3ba * if the selected logical track is not resident cmp.b bufsec,d4 beq d3bb * if the selected physical sector is resident d3ba: bsr flush * flush out the old stuff first move.b rdrive,drive * make the saved major device new move rtrack,track * make the saved logical track new clr.b side * make the saved head zero move.b rsec,sec * make the saved physical sector new clr.b hdtemp * set up for a read bsr d3rw * go do the physical pre-read move.b drive,bufdrv * make the new major device resident move track,buftrk * make the new track resident move.b side,bufsid * make the new head (zero) resident move.b sec,bufsec * make the new sector resident tst.b d0 * check the return status of the read bne d3bc * if not successful d3bb: move.b cpmsec,d2 * get the old logical sector and.l #7,d2 * mask it mulu #128,d2 * multiply by the length of a physical sector add.l #buffer,d2 * index into the pre-read buffer move.l cpmdma,d1 * get the user buffer address d3bc: rts d3rw: move.b drive,d5 * get the major device and.l #$f,d5 * mask it lea hdtab,a5 * point into the minor devce table move.b 0(a5,d5),d3 * get a minor device and.l #3,d3 * mask the drive unit number lea d3iopb+2,a2 move.b d3,(a2)+ * and save it move.b hdtemp,d0 * go get the direction flag eor.b #1,d0 * invert bit 0 move.b d0,(a2)+ * set into the iopb move.b sec,(a2)+ * move the physical sector into the iopb move.b #0,(a2)+ * set the MSB of the iopb sector move.b track+1,(a2)+ * move the MSB of the track into the iopb move.b track+0,(a2)+ * and the LSB of the track move #$100,(a2)+ * set up for a one sector transfer move.l #buffer,d0 * set up the DMA pointer move.b d0,(a2)+ lsr.l #8,d0 move.b d0,(a2)+ lsr.l #8,d0 move.b d0,(a2)+ move.l #d3_rdwr,d0 * set up the physical read/write command bsr d3exec * go do a command rts * return whatever we get back from d3exec d3spec: move.l #defiopb,a0 * point at the initial iopb (0x50) lea d3sav,a1 * point at the save buffer move #15,d0 * the length of an iopb d3spa: move.b (a0)+,(a1)+ * move a byte dbra d0,d3spa * maybe move another move.l #d3iopb,d1 * move the next iopb address into the iopb move.b d1,defiopb+13 * and set up the iopb links move.b d1,d3iopb+13 lsr.l #8,d1 move.b d1,defiopb+14 move.b d1,d3iopb+14 lsr.l #8,d1 move.b d1,defiopb+15 move.b d1,d3iopb+15 move.b #d3_rst,d3port * send a reset into the controller move.b #d3_attn,d3port * now wake it up move #$3ff,d0 * set up to delay about 512 ms d3spq: nop * NOP uses 500 ns of time in an 8 MHz CPU dbra d0,d3spq * we wait here for the DISK3 to catch up move.b #d3_nop,d3iopb+0 * do a NOOP to check for a controller move.l #$ffff,d0 * set the timeout counter clr.b d3iopb+1 * set the iopb busy move.b #d3_attn,d3port d3spx: tst.b d3iopb+1 * get some iopb status bne d3spd * if yes sub.l #1,d0 * bump the timeout counter bne d3spx * maybe try again lea d3msg2,a0 * point at the message bsr type * and print it move.l #$ff,d0 bra d3spz * restore the saved iopb and quit early d3spd: move.l #d3_home,d0 bsr d3exec * do a recalibrate now move.b #1,d3iopb+3 * we are going to read something now clr.l d3iopb+4 * we are reading from cylinder 0, head 0 move #$200,d3iopb+8 * and we are reading 2 sectors move.l #buffer,d0 * point at the pre-read buffer lea d3iopb+10,a0 * and point at the data field of the iopb move.b d0,(a0)+ * place the buffer address in the data field lsr.l #8,d0 move.b d0,(a0)+ lsr.l #8,d0 move.b d0,(a0)+ move.l #d3_rdwr,d0 bsr d3exec * go read the sectors into the pre-read buffer move.l buffer,d0 * get the first four bytes from the buffer cmp.l #'Comp',d0 * check for the marker beq d3spc * if found we continue move.b d3iopb+2,d0 * else we set the message add.b #'0',d0 * ASCII bias move.b d0,d3msgx * set the number in the message lea d3msg,a0 * point at the message bsr type * print it move.l #$ff,d0 * set up for a failure bra d3spz * and quit now d3spc: move.l #buffer+16,d1 * point at the specify block lea d3iopb+10,a0 * and point at the data field of the iopb move.b d1,(a0)+ * place the specify block in the data field lsr.l #8,d1 move.b d1,(a0)+ lsr.l #8,d1 move.b d1,(a0)+ move.l #d3_spec,d0 * go do a specify command bsr d3exec move.l #buffer+1024,d0 * point at the map block lea d3iopb+10,a0 * and point at the data field of the iopb move.b d0,(a0)+ * place the buffer address in the data field lsr.l #8,d0 move.b d0,(a0)+ lsr.l #8,d0 move.b d0,(a0)+ move.l #d3_map,d0 * go do a set map command bsr d3exec move.b d3iopb+1,d0 * get the status cmp.b #d3_cmpl,d0 * check for success bne d3spz * if not we have non-zero in d0 clr.l d0 * else we return success d3spz: move.l #defiopb,a0 * point at the initial iopb lea d3sav,a1 * and the save buffer move #15,d1 * the length of an iopb d3spb: move.b (a1)+,(a0)+ * restore a byte from the save buffer dbra d1,d3spb * maybe go do another rts d3exec: move.b d0,d3iopb * put the command into the iopb clr.b d3iopb+1 * clear the status field of the iopb move.b #d3_attn,d3port * get the drive's attention d3xa: move.b d3iopb+1,d0 * get the status field tst.b d0 * if it is still zero we wait some more beq d3xa cmp.b #d3_cmpl,d0 * check the status for success (0xff) bne d3xf * if not clr.l d0 * here we return success (zero) d3xf: rts * here any non-zero in d0 marks a failure .endc .ifeq hmdrive * M-Drive/H Support * vread and vwrite perform reads and writes on the ram virtual disk vread: bsr setadr * set the DMA address move.l cpmdma,a1 * get the destination pointer move #127,d0 * move 128 bytes move.b #$5a,d1 * the checksum bias vrl: move.b mddata,d2 * get a data byte add.b d2,d1 * add a copy of it to the checksum move.b d2,(a1)+ * and put it into the user buffer dbra d0,vrl * maybe go back for more move.b cpmsec,mdadr * the logcal sector forms the MSB move.b cpmtrk+0,d0 * track bit 8 forms the middle and.b #1,d0 * send it (for a track address of 0..511) move.b d0,mdadr move.b cpmtrk+1,mdadr * track bis 0..7 forms the LSB of the checkadr move.b mddata,d0 * get the expected checksum sub.b d1,d0 * clear d0 if the expected and computed bne vre * checksums are the same clr.l d0 vre: rts vwrite: bsr setadr * set the DMA address move.l cpmdma,a1 * point at the user buffer move #127,d0 * move 128 bytes move #$5a,d1 * get the checksum bias vwl: move.b (a1)+,d2 * get the user data byte add.b d2,d1 * add a copy to the checksum move.b d2,mddata * send it dbra d0,vwl * maybe go back for more move.b cpmsec,mdadr * the logical sector forms the MSB move.b cpmtrk+0,d0 * track bit 8 forms the middle and.b #1,d0 * of the checkadr in a range of 0..511 move.b d0,mdadr * send it move.b cpmtrk+1,mdadr * send track bits 0..7 as the LSB move.b d1,mddata * send the computed checksum clr.l d0 * always return success rts setadr: move.b cpmsec,mdadr * send the secto as the MSB move.w cpmtrk,d6 * get the track move.w d6,d7 * copy it lsr.w #1,d6 * send the middle and LSB of the address move.b d6,mdadr * by shifting left the track by 7 bits lsl.w #7,d7 * the remaining 7 bits of the address move.b d7,mdadr * form a 128 byte counter in the M-DRIVE rts setsize: lea dpbm,a0 * point at the memory disk DPB move #0,cpmtrk * set the track to 0 move.b #7,cpmsec * we offset into the track by 7 sectors each set1: bsr checksize * go see if some memory is there bne set2 * if it is not move.b cpmsec,d0 * get the current sector add.b #8,d0 * add 8 sectors to point at the next board move.b d0,cpmsec * (adds 1K to the board pointer) cmp.b #64,d0 * if we have already tested 8 boards blo set1 * else we go try another set2: moveq #0,d0 * clear a register move.b cpmsec,d0 * get the board counter sub #7,d0 * point it at the last possible sector move d0,(a0) * place it in the SPT field of the DPB lsr #2,d0 * divide by 4 mulu #127,d0 * multiply by the length of a directory sector sub #1,d0 * (a weird number) move d0,6(a0) * place it in the DSM field of the DPB cmp #1024,d0 * if less than 1024 directory entries bcs setx * then we are done with DRM of 0c000h move #255,8(a0) * else set the directory count to 256 move #$f000,10(a0) * and set the DRM field to 0f000h setx: rts checksize: bsr setadr * set the address in the memory disk move.b mddata,d0 * get a data byte move d0,d1 * copy it not.b d0 * one's complement bsr setadr * point at the same byte as before move.b d0,mddata * send the complemented byte bsr setadr * point at the same byte again move.b mddata,d0 * get the data eor.b d1,d0 * XOR the normal with the complemented data cmp.b #$ff,d0 * should be zero if memory was there bne checkx * if not we return failure bsr setadr * point at the same old byte move.b d1,mddata * restore old data moveq #0,d0 * setup to return success checkx: rts .endc .data .even cpmiob: .dc.b 0 * the I/O byte cpmflg: .dc.b 0 * the selected major device DPB type cpmtyp: .dc.b 0 * the saved write command type cpmdma: .dc.l 0 * the selected dma address dma: .dc.l 0 * the current dma address cpmdrv: .dc.b 0 * the selected major device cpmsec: .dc.b 0 * the selected logical sector track: .dc.w 0 * the current cylinder cpmtrk: .dc.w 0 * the selected cylinder buftrk: .dc.w $99 * the buffered cylinder rtrack: .dc.w 0 * the new cylinder bufdrv: .dc.b $99 * the buffered major device bufsid: .dc.b 0 * the buffered head bufsec: .dc.b 0 * the buffered logical sector rdrive: .dc.b 0 * the new major device rside: .dc.b 0 * the new head rsec: .dc.b 0 * the new logical sector drive: .dc.b 0 * the current major device side: .dc.b 0 * the current head sec: .dc.b 0 * the current logical sector dirty: .dc.b 0 * the dirty buffer flag byte .even news: .dc.b 0,0,0,0 * new drive flag for each drive dens: .dc.b 0,0,0,0 * current density for each drive sides: .dc.b 0,0,0,0 * sidedness flag for each drive region: .dc.w 1 * one region .dc.l lomem * start of TPA .dc.l himem-lomem * length signon: .dc.b k_cls,'CompuPro CP/M-68K version ' .dc.b model+'0','.',make+'0',patch+'@',k_cr,k_lf .dc.b 'Copyright (c) 1983 CompuPro',k_cr,k_lf,0 nrerr: .dc.b ' Not ready',0 wperr: .dc.b ' Write protected',0 drverr: .dc.b ' on drive ',0 qmess: .dc.b ' Type R to retry, A to abort ',0 .ifeq hard hdtemp: .dc.b 0 * saved r/w status for hard disk I/O .endc .ifeq disk2 d2cyl: .dc.l $ffffffff * the current cylinders for each minor device d2msg: .dc.b 'DISK2 unit ' d2msgx: .dc.b 'x is not ready...',0 d2rhbf: .dc.b 0,0,0 * for the disk header info .endc .ifeq disk3 d3iopb: .dc.l 0,0,0,0 * the working IOPB d3sav: .dc.l 0,0,0,0 * the save block for initialization d3msg: .dc.b 'DISK3 unit ' d3msgx: .dc.b 'x is not formatted',k_cr,k_lf,0 d3msg2: .dc.b 'DISK3 controller does not respond',k_cr,k_lf,0 .endc * disk parameter headers .even dph0: .dc.l xlt0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpb0 * DPB address .dc.l ckv0 * CKV address .dc.l alv0 * ALV address dph1: .dc.l xlt0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpb0 * DPB address .dc.l ckv1 * CKV address .dc.l alv1 * ALV address .ifeq xd8 dph2: .dc.l xlt0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpb0 * DPB pointer .dc.l ckv2 * CKV pointer .dc.l alv2 * ALV pointer dph3: .dc.l xlt0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpb0 * DPB address .dc.l ckv3 * CKV address .dc.l alv3 * ALV address .endc .ifeq disk2 dphd2a: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd2a * DPB address .dc.l 0 * CKV address .dc.l alvd2a * ALV address dphd2b: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd2b * DPB address .dc.l 0 * CKV address .dc.l alvd2b * ALV address .endc .ifeq d2m20*d2f20b*d2f40b dphd2c: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer address .dc.l dpbd2c * DPB address .dc.l 0 * CKV address .dc.l alvd2c * ALV addres .endc .ifeq d2f40b dphd2d: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd2d * DPB address .dc.l 0 * CKV address .dc.l alvd2d * ALV address dphd2e: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd2e * DPB address .dc.l 0 * CKV address .dc.l alvd2e * ALV address .endc .ifeq disk3 dphd3a: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd3a * DPB address .dc.l 0 * CKV address .dc.l alvd3a * ALV address .endc .ifeq d3m40 dphd3b: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd3b * DPB address .dc.l 0 * CKV address .dc.l alvd3b * ALV address dphd3c: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd3c * DPB address .dc.l 0 * CKV address .dc.l alvd3c * ALV address dphd3d: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd3d * DPB address .dc.l 0 * CKV address .dc.l alvd3d * ALV address dphd3e: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbd3e * DPB address .dc.l 0 * CKV address .dc.l alvd3e * ALV address .endc .ifeq hmdrive dphm: .dc.l 0 * XLT table address .dc.w 0,0,0 * dummy .dc.l dirbuf * directory buffer pointer .dc.l dpbm * DPB address .dc.l 0 * CKV address .dc.l alvm * ALV address .endc .even * disk parameter blocks: * the CompuPro CBIOS requires the first 8 DPB's be for the floppy disk only dpb0: .dc.w 26 * sectors/track .dc.b 3 * block shift .dc.b 7 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 242 * disk size .dc.w 63 * directory entries .dc.w $c000 * directory mask .dc.w 16 * directory check size .dc.w 2 * track offset dpb0d: .dc.w 26 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 242 * disk size .dc.w 127 * directory entries .dc.w $c000 * directory mask .dc.w 32 * directory check size .dc.w 4 * track offset dpb1: .dc.w 52 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 242 * disk size .dc.w 127 * directory entries .dc.w $c000 * directory mask .dc.w 32 * directory check size .dc.w 2 * track offset dpb1d: .dc.w 52 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 485 * disk size .dc.w 255 * directory entries .dc.w $f000 * directory mask .dc.w 64 * directory check size .dc.w 4 * track offset dpb2: .dc.w 60 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 261 * disk size .dc.w 127 * directory entries .dc.w $c000 * directory mask .dc.w 32 * directory check size .dc.w 2 * track offset dpb2d: .dc.w 60 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 523 * disk size .dc.w 255 * directory entries .dc.w $f000 * directory mask .dc.w 64 * directory check size .dc.w 4 * track offset dpb3: .dc.w 64 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 299 * disk size .dc.w 127 * directory entries .dc.w $c000 * directory mask .dc.w 32 * directory check size .dc.w 2 * track offset dpb3d: .dc.w 64 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 599 * disk size .dc.w 255 * directory entries .dc.w $f000 * directory mask .dc.w 64 * directory check size .dc.w 4 * track offset .ifeq d2m10 dpbd2a: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 1342 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 1*4 * track offset dpbd2b: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 1331 * disk size .dc.w 1023 * directory entries .dc.w 0 * directory check size .dc.w 123*4 * track offset .endc .ifeq d2m20*d2f20b dpbd2a: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2045 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*1 * track offset dpbd2b: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2045 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*94 * track offset dpbd2c: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 1231 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*187 * track offset .endc .ifeq d2f40b dpbd2a: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2023 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*1 * track offset dpbd2b: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2023 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*47 * track offset dpbd2c: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2023 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*93 * track offset dpbd2d: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2023 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*139 * track offset dpbd2e: .dc.w d2scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2463 * disk size .dc.w 1023 * directory entres .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*185 * track offset .endc .ifeq disk3 dpbd3a: .dc.w d3scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 1331 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 2 * track offset .endc .ifeq d3m40 dpbd3b: .dc.w d3scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2519 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * drectory check size .dc.w 8*74 * track offset dpbd3c: .dc.w d3scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2519 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*211 * track offset dpbd3d: .dc.w d3scnt*8 * sectors/track .dc.b 5 * block shift .dc.b 31 * block mask .dc.b 1 * extent mask .dc.b 0 * dummy .dc.w 2519 * disk size .dc.w 1023 * directory entries .dc.w $ffff * directory mask .dc.w 0 * directory check size .dc.w 8*351 * track offset dpbd3e: .dc.w d3scnt*8 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 611 * disk size .dc.w 255 * directoy entres .dc.w $f000 * directory mask .dc.w 0 * directory check size .dc.w 8*491 * track offset .endc .ifeq hmdrive dpbm: .dc.w 8 * sectors/track .dc.b 4 * block shift .dc.b 15 * block mask .dc.b 0 * extent mask .dc.b 0 * dummy .dc.w 253 * disk size .dc.w 127 * directory entries .dc.w $c000 * directory mask .dc.w 0 * directory check size .dc.w 4 * track offset .endc .even typbase: .dc.b fp8s1d0,fp8s2d0,fp8s1d1,fp8s2d1 * for the floppies .dc.b fp8s1d2,fp8s2d2,fp8s1d3,fp8s2d3 .ifeq disk2 .dc.b dsk2typ,dsk2typ * for any DISK2 .endc .ifeq d2m20*d2f20b*d2f40b .dc.b dsk2typ * if not d2m10 .endc .ifeq d2f40b .dc.b dsk2typ,dsk2typ * if d2f40b .endc .ifeq disk3 .dc.b dsk3typ * for any DISK3 .endc .ifeq d3m40 .dc.b dsk3typ,dsk3typ,dsk3typ,dsk3typ * if Q540 .endc .ifeq hmdrive .dc.b memtype * for the memory disk .endc .even .ifeq hard hdtab: .dc.b 0,0,0,0 * drives A-D: no hard disk .dc.b $10 * drive E: hard disk unit 0 .ifeq disk2*d3m40 .dc.b $10 * drive F: hard disk unit 0 .endc .ifeq d2m20*d2f20b*d2f40b*d3m40 .dc.b $10 * drive G: hard disk unit 0 .endc .ifeq d2f40b*d3m40 .dc.b $10,$10 * drives H-I: hard disk unit 0 .endc .ifne disk2*d3m40 .dc.b 0 * drive F: no hard disk .endc .ifne d2m20*d2f20b*d2f40b*d3m40 .dc.b 0 * drive G: no hard disk .endc .ifne d2f40b*d3m40 .dc.b 0,0 * drives H-I: no hard disk .endc .dc.b 0,0,0,0,0,0,0 * drives J-P: no hard disk .endc * active drive table drvtab: .dc.l dph0,dph1 * drive A: 8" floppy disk unit 0 .ifeq xd8 .dc.l dph2,dph3 * drive C: 8" floppy disk unit 2 .endc .ifne xd8 .dc.l 0,0 * drive C: .endc .ifeq disk2 .dc.l dphd2a,dphd2b * drive E: hard disk unit 0 .ifeq d2m20*d2f20b*d2f40b .dc.l dphd2c * drive G: hard disk unit 2 .endc .ifeq d2f40b .dc.l dphd2d,dphd2e * drives H-I: hard disk unit 3 .endc .ifne d2m20*d2f20b*d2f40b .dc.l 0 * drive G: .endc .ifne d2f40b .dc.l 0,0 * drives H-I: .endc .endc .ifeq disk3 .dc.l dphd3a * drive E: hard disk unit 0 .ifeq d3m40 .dc.l dphd3b,dphd3c * drive F: hard disk unit 1 .dc.l dphd3d,dphd3e * drive H: hard disk unit 3 .endc .ifne disk3 .dc.l 0 * drive E: .endc .ifne d3m40 .dc.l 0,0,0,0 * drive F: .endc .endc .ifne hard .dc.l 0,0,0,0,0 * drive E: .endc .dc.l 0,0,0 * drive J: .ifeq hmdrive .dc.l dphm * drive M: M-DRIVE/H memory drive .endc .ifne hmdrive .dc.l 0 * drive M: .endc .dc.l 0,0,0 * drive N: .even * sector translation tables xlt0: .dc.b 1, 7,13,19,25, 5,11,17,23, 3, 9,15,21 .dc.b 2, 8,14,20,26, 6,12,18,24, 4,10,16,22 xlt1: .dc.b 1, 2, 19,20, 37,38, 3, 4, 21,22, 39,40 .dc.b 5, 6, 23,24, 41,42, 7, 8, 25,26, 43,44 .dc.b 9,10, 27,28, 45,46, 11,12, 29,30, 47,48 .dc.b 13,14, 31,32, 49,50, 15,16, 33,34, 51,52 .dc.b 17,18, 35,36 xlt2: .dc.b 1, 2, 3, 4, 17,18,19,20, 33,34,35,36, 49,50,51,52 .dc.b 5, 6, 7, 8, 21,22,23,24, 37,38,39,40, 53,54,55,56 .dc.b 9,10,11,12, 25,26,27,28, 41,42,43,44, 57,58,59,60 .dc.b 13,14,15,16, 29,30,31,32, 45,46,47,48 xlt3: .dc.b 1, 2, 3, 4, 5, 6, 7, 8, 25,26,27,28,29,30,31,32 .dc.b 49,50,51,52,53,54,55,56, 9,10,11,12,13,14,15,16 .dc.b 33,34,35,36,37,38,39,40, 57,58,59,60,61,62,63,64 .dc.b 17,18,19,20,21,22,23,24, 41,42,43,44,45,46,47,48 xlts: .dc.l xlt0, xlt1, xlt2, xlt3 gaps: .dc.b 26,$07,$80 * density 0 .dc.b 26,$0e,$ff * density 1 .dc.b 15,$1b,$ff * density 2 .dc.b 08,$35,$ff * density 3 .bss .ifeq disk2 d2bmap: .ds.b 128 * the sector relocation map .endc cmd: .ds.b 10 * FDC command table stat: .ds.b 8 * FDC result buffer ckv0: .ds.b 64 * FDC device 0 CKV ckv1: .ds.b 64 * FDC device 1 CKV .ifeq xd8 ckv2: .ds.b 64 * FDC device 2 CKV ckv3: .ds.b 64 * FDC device 3 CKV .endc alv0: .ds.b 80 * FDC device 0 ALV alv1: .ds.b 80 * FDC device 1 ALV .ifeq xd8 alv2: .ds.b 80 * FDC device 2 ALV alv3: .ds.b 80 * FDC device 3 ALV .endc .ifeq disk2 alvd2a: .ds.b 260 * DISK2 device 0 ALV alvd2b: .ds.b 260 * DISK2 device 1 ALV .endc .ifeq d2m20*d2f20b*d2f40b alvd2c: .ds.b 256 * DISK2 device 2 ALV .endc .ifeq d2f40b alvd2d: .ds.b 256 * DISK2 device 3 ALV alvd2e: .ds.b 310 * DISK2 device 4 ALV .endc .ifeq disk3 alvd3a: .ds.b 170 * DISK3 device 0 ALV .endc .ifeq d3m40 alvd3b: .ds.b 320 * DISK3 device 1 ALV alvd3c: .ds.b 320 * DISK3 device 2 ALV alvd3d: .ds.b 320 * DISK3 device 3 ALV alvd3e: .ds.b 80 * DISK3 device 4 ALV .endc .ifeq hmdrive alvm: .ds.b 64 * M-DRIVE/H ALV .endc dirbuf: .ds.b 128 * file system directory update buffer buffer: .ds.b $2000 * FDC blocking/deblocking buffer .end