; CBIOS.MAC - 3/9/86 The real BIOS for the Xerox. ; This loads itself using the SBIOS. ; It takes the place of the Xerox PROM code. ; Xerox CBIOS Version 10.6 (For MailBox/GateWay version 10.x). ; Thanks to k1bc, ke1g, ki4xo for help with the bios code. ; Bigboard I mods from n6fqr .z80 .xlist maclib TNC.LIB timdef asciictl .list false equ 0 true equ not false maclib BIOSCPM.INC ; CP/M addresses maclib BIOSDSKE.INC ; Disk equates ; Parallel keyboard port: ; Set keyreg to true to use a "normal" keyboard. ; Set to false and set keyxor if: ; the high order bit is set to one for control shift, ; then use ffh, if it is set to zero then use 7fh keyreg equ true ; I have funny keyboards. keyxor equ 0ffh ; 7fh for Sperry, 0ffh for Honeywell kbd ; What to do with the PRINTER port. ; Set ONE of these tags to true. pistnc equ true ; Second TNC, 4800 baud, 8 bit, no parity. piscon equ false ; Console, 9600 baud, 8 bit, no parity. pispr equ false ; Printer, 9600 baud, 7 bit, even parity. ; How to handshake the serial ports. hdwhnd equ false ; True if hdw handshake works xon equ dc1 ; dc1 or bs xoff equ dc3 ; dc3 or ' ' ; What to do with the parallel user port. parlst equ true ; True to use par port for list device. clk4 equ false ; Set true if 4 Mhz system clock ; Ram Allocation ivec equ 0ff00h ; Interrupt vector page. defbuf equ 80h ; Default CPM Disk buffer area nmivec equ 66h ; Where Z80 traps on non-maskable interrupt ; I/O ports: ; COMM port (SIO A) pu1st equ 6 pu1dat equ 4 pu1cts equ 20h pu1dcd equ 8 pu1tbe equ 4 pu1spd equ 0 ; Baud rate generator for channel A pu1buf equ bufbas ; Must be on page boundary pu1siz equ 256 ; Input buffer size ; PRINTER port (SIO B) pu2st equ 7 pu2dat equ 5 pu2cts equ 20h pu2dcd equ 8 pu2tbe equ 4 pu2spd equ 0ch ; Baud rate generator for channel B pu2buf equ pu1buf+pu1siz ; Must be on page boundary pu2siz equ 256 ; Input buffer size ; Keyboard port. pu3dat equ 1eh ; Keyboard data port of PIO pu3st equ 1fh ; Keyboard control port of PIO pu3buf equ pu2buf+pu2siz ; Must be on page boundary pu3siz equ 16 ; Must be pwr of 2 and <= 256 ; Misc system ports. pio.ad equ 8 ; PIO channel A, Data port pio.as equ 9 ; PIO channel A, Sts/Ctrl port pio.bd equ 0ah ; PIO channel B, Data port pio.bs equ 0bh ; PIO channel B, Sts/Ctrl port fdc.cs equ 10h ; 1771 Disc Controller: Ctrl/Status fdc.tk equ 11h ; 1771 Disc Controller: Cylinder/track fdc.se equ 12h ; 1771 Disc Controller: Sector fdc.da equ 13h ; 1771 Disc Controller: Data crt.sc equ 14h ; CRT Scroll control register ctc.c0 equ 18h ; Counter/Timer Chip Channel 0 ctc.c1 equ 19h ; Counter/Timer Chip Channel 1 ctc.c2 equ 1ah ; Counter/Timer Chip Channel 2 ctc.c3 equ 1bh ; Counter/Timer Chip Channel 3 pio.sd equ 1ch ; Data port of system PIO pio.sc equ 1dh ; Control port of system PIO ; Bit numbers for bit, res,and set instructions b0 equ 0 b1 equ 1 b2 equ 2 b3 equ 3 b4 equ 4 b5 equ 5 b6 equ 6 b7 equ 7 ; This section is the loader code. It runs as a normal .COM file. ; It sets up the interrupt vectors, then moves the BIOS ; code to upper memory where it belongs. aseg org 100h ; Initialize the I/O devices. di ; Start with interrupts off ld hl,inivio ; Set up initial I/O variables initio: ld b,(hl) ; Get count of I/O setup data bytes inc hl ld c,(hl) ; To be stuffed into this I/O port inc hl ; Point at data bytes otir ; Set up the I/O device bit b7,(hl) ; Check for end of setup blocks jr z,initio ; Loop over all I/O setups ; Set the processor to proper interrupt state. ld a,ivec shr 8 ; I/O vector page ld i,a ; Set processor for vectored interrupts im 2 ; Set up the interrupt vectors. ld hl,livec ; Point to local copy ld de,ivec ; Point to where they go ld bc,livecl ; How many bytes ldir ; Move them there ; Move the BIOS code to its execution area. ld hl,bsbgn ld de,bios ld bc,bslen ldir ei jp bios ; Go do it. ; I/O port initialization values. ; Organized for copying loop above, these are in the form: ; one byte count ; one byte port number ; n bytes of port initialization values ; followed by another block, or "FF" to mark the end of the list inivio: if parlst db 1,pio.as db 0fh ; Output mode on PIO port A db 2,pio.bs db 0cfh ; BIT mode on PIO port B db 0fh ; BITS 7..4 outputs, BITS 3..0 inputs db 1,pio.ad db 0d0h ; Strobe bit negated endif db 3,pio.sc ; PIO for system control port db 0cfh,38h,40h ; Change 38 to 18 for bell db 1,pio.sd db 0 ; Init system control port data db 3,pu3st ; PIO control for keyboard db 4fh db ivkbd and 0ffh ; Vector location db 83h db 1,ctc.c0 ; Chan 0 of CTC db ivctc and 0ffh db 2,ctc.c2 ; Chan 2 of CTC if clk4 db 27h,168 ; Each tick = 10.752 Ms. else db 27h,105 ; Each tick = 10.752 Ms. endif db 2,ctc.c3 ; Chan 3 of CTC db 0c7h,93 ; Counts of ch 2 = 1 sec. ; Baud rates for the SIO ports. db 1,pu1spd ; SIO port A baud rate db 12 ; 4800 baud for COMM port. db 1,pu2spd ; SIO port B baud rate if pistnc db 12 ; 4800 baud for PRINTER port. else db 14 ; 9600 baud endif ; SIO port A (COMM port). db pu1inl,pu1st ; Count, port pu1ini: db 18h ; Reset db 14h,44h ; A reg 4 - x16, 1 stop, no par db 13h,0c1h ; A reg 3 - 8 bit, rx enable db 15h,0eah ; A reg 5 - DTR, RTS, 8 bit, tx enable db 11h,19h ; A reg 1 - rx int all char, external pu1inl equ $-pu1ini ; SIO port B (PRINTER port). db pu2inl,pu2st ; Count, port pu2ini: if pispr db 18h ; Reset db 14h,43h ; B reg 4 - x16, 1 stop, even par db 13h,40h ; B reg 3 - 7 bit, no rx db 15h,0aah ; B reg 5 - DTR, RTS, 7 bit, tx enable db 12h,(ivsio and 0ffh) ; B reg 2 - Int addr db 11h,5h ; B reg 1 - rx off, ext, status affects vect else db 18h ; Reset db 14h,44h ; B reg 4 - x16, 1 stop, no par db 13h,0c1h ; B reg 3 - 8 bit, rx enable db 15h,0eah ; B reg 5 - DTR, RTS, 8 bit, tx enable db 12h,(ivsio and 0ffh) ; B reg 2 - Int addr db 11h,1dh ; B reg 1 - rx all, ext, status affects vect endif pu2inl equ $-pu2ini db 0ffh ; End of I/O initializer list ; Local copy of the interrupt vectors. ; Moved to the interrupt page at initialization time. livec: ; SIO vectors. ivsio equ ivec+($-livec) dw 0 dw pu2ext dw pu2ihd dw pu2err dw 0 dw pu1ext dw pu1ihd dw pu1err ; CTC interrupt vectors. ivctc equ ivec+($-livec) dw 0 ; CTC 0, not used. dw ctcint ; Handler for timer (1 Ms ticks) dw 0 ; CTC 2, prescale for clock. dw clkint ; Handler for clock (seconds tick) ; Keyboard PIO vectors. ivkbd equ ivec+($-livec) dw pu3ihd ; Handler for keyboard dw 0 dw 0 dw 0 livecl equ $-livec ; Here is the bios code. It is moved to its real location ; by the loader code above. bsbgn equ $ ; Start of BIOS code .phase bios ; Assemble correct addresses maclib BIOSJMP.INC ; Makes the BIOS jump table ; Console. ; pu0 is keyboard/video + PRINTER, controled by pistnc/piscon/pispr ; pu1 is COMM port (SIO A) ; pu2 is PRINTER port (SIO B) ; pu3 is keyboard/video ; pu4 is parallel port on PIO ; Console status const: ld a,(3) ; Iobyte or a jr z,pu0ist dec a jr z,pu1ist if pistnc pu2ist: ld a,(pu2ict) or a jr z,pu2isu ld a,0ffh ret pu2isu: ld a,(pu2off) or a ret z ; Not off if hdwhnd di ld a,5 out (pu2st),a ld a,0eah out (pu2st),a ; Turn TNC on else push bc ld c,xon call pu2ot ; Send the xon to the tnc pop bc di endif xor a ld (pu2off),a ei ret ; With zero set else pu2ist: ld a,(pu2ict) or a ret z ld a,0ffh ret endif pu1ist: ld a,(pu1ict) or a jr z,pu1isu ld a,0ffh ret pu1isu: ld a,(pu1off) or a ret z ; Not off if hdwhnd di ld a,5 out (pu1st),a ld a,0eah out (pu1st),a ; Turn TNC on else push bc ld c,xon call pu1ot pop bc di endif xor a ld (pu1off),a ei ret pu0ist: if piscon call pu2ist ret nz endif pu3ist: ld a,(pu3ict) ; Anything in ring buffer? or a ret z ; Return Z if not ld a,0ffh ; Yes, return non-Z and A/ FF ret ; Console input conin: ld a,(3) ; Iobyte or a jr z,pu0inp dec a jr z,pu1inp pu2inp: call pu2ist jr z,pu2inp ld hl,(pu2gpt) ld a,(hl) inc l ld (pu2gpt),hl ld hl,pu2ict dec (hl) ret pu1inp: call pu1ist jr z,pu1inp ld hl,(pu1gpt) ld a,(hl) inc l ld (pu1gpt),hl ld hl,pu1ict dec (hl) ret pu0inp: if piscon call pu2ist jr nz,pu2inp endif call pu3ist jr z,pu0inp ; Return character from keyboard. Wait if needed. pu3inp: call pu3ist ; Anything there yet? jr z,pu3inp ; Wait if not push hl ; Yes, save hl pair ld hl,pu3ict ; One less char in ring dec (hl) ld hl,pu3gpt ; Point at ring taker call pu3ibp ; Get the char pop hl ; Restore regs ret ; Return char in A ; Console output conout: ld a,(3) ; Iobyte or a jr z,pu0ot dec a jr z,pu1ot if pistnc pu2ot: in a,(pu2st) and pu2dcd ret z ; TNC is off pu2ota: in a,(pu2st) and pu2tbe jr z,pu2ota pu2otb: in a,(pu2st) and pu2cts jr z,pu2otb ld a,c out (pu2dat),a ret else pu2ot: call pu2ost jr z,pu2ot ld a,c out (pu2dat),a ret pu2ost: in a,(pu2st) and pu2tbe ret z ld a,0ffh ret endif pu1ot: in a,(pu1st) and pu1dcd ret z ; TNC is off pu1ota: in a,(pu1st) and pu1tbe jr z,pu1ota pu1otb: in a,(pu1st) and pu1cts jr z,pu1otb ld a,c out (pu1dat),a ret pu0ot: call pu3ot if piscon jr pu2ot else ret endif if parlst pu4ot: in a,(pio.bd) and 2 jr nz,pu4ot ld a,c out (pio.ad),a push af di in a,(pio.bd) res 4,a out (pio.bd),a set 4,a out (pio.bd),a ei pop af ret pu4ost: in a,(pio.bd) and 2 jr nz,pu4osa ld a,0ffh or a ret pu4osa: xor a ret endif ; CP/M list device. if pispr list equ pu2ot listst equ pu2ost endif if parlst list equ pu4ot listst equ pu4ost endif if (not pispr) and (not parlst) list equ pu0ot listst: ld a,0ffh ; Return always ready or a ret endif ; Punch and reader are not used. punch equ conout reader equ conin ; Increment pu3inp buffer pointer. pu3ibp: ld a,(hl) ; Pick up putter or taker low bits inc a ; Step the pointer and pu3siz-1 ; Wrap within ring ld (hl),a ; Update pointer ld hl,pu3buf ; Now find the char it points to add a,l ld l,a ld a,(hl) ; Pick up the character, for taker case ret ; The interrupt code. pu1no: ld a,(pu1off) or a ret nz ; Already off cpl ld (pu1off),a if hdwhnd ld a,5 out (pu1st),a ld a,0e8h out (pu1st),a else ld c,xoff call pu1ot endif ret pu2no: ld a,(pu2off) or a ret nz ; Already off cpl ld (pu2off),a if hdwhnd ld a,5 out (pu2st),a ld a,0e8h out (pu2st),a else ld c,xoff call pu2ot endif ret pu1err: ex af,af' in a,(pu1dat) ld a,30h jr pu1xx pu1ext: ex af,af' in a,(pu1st) ld a,10h pu1xx: out (pu1st),a jr iret2 pu1ihd: ex af,af' exx ld hl,(pu1ppt) in a,(pu1dat) ld (hl),a inc l ld (pu1ppt),hl ld hl,pu1ict inc (hl) ld a,(hl) cp pu1siz-8 call z,pu1no jr iret1 pu2err: ex af,af' in a,(pu2dat) ld a,30h jr pu2xx pu2ext: ex af,af' in a,(pu2st) ld a,10h pu2xx: out (pu2st),a jr iret2 pu2ihd: ex af,af' exx ld hl,(pu2ppt) in a,(pu2dat) ld (hl),a inc l ld (pu2ppt),hl ld hl,pu2ict inc (hl) ld a,(hl) cp pu2siz-8 call z,pu2no jr iret1 ; Keyboard interrupt handler. pu3ihd: exx ; Save registers ex af,af' in a,(pu3dat) ; Get the keyboard data if keyreg cpl ; Invert inverted data and 7fh ; Bit 7 = 0 else ; Fix for control chars from keyboard xor keyxor jp p,xx and 1fh endif xx: ld c,a ; Char to add to ring ld hl,pu3ict ; Ring fullness count ld a,(hl) ; Pick up count inc a ; Add one more character cp pu3siz ; Ring full? jr nc,iret1 ; Don't add another char, if full ld (hl),a ; OK, add this one ld hl,pu3ppt ; Point at ring putter call pu3ibp ; Compute address and wrap ld (hl),c ; Put char in ring iret1: exx iret2: ex af,af' ei reti pu1ppt: dw pu1buf pu1gpt: dw pu1buf pu1ict: db 0 pu1off: db 0 pu2ppt: dw pu2buf pu2gpt: dw pu2buf pu2ict: db 0 pu2off: db 0 pu3ppt: db 0 pu3gpt: db 0 pu3ict: db 0 ; Disk handler. maclib BIOSDPB.INC ; Sector translate sectrn: ex de,hl add hl,bc ld l,(hl) ld h,0 ret ; Select drive seldsk: ld hl,0 ; Assume bad staus ld a,c cp mxdrv ret nc ; No such drive ld (drive),a ld l,c add hl,hl add hl,hl add hl,hl add hl,hl ld de,dpe0 add hl,de ret ; Do the actual select. select: ld a,(drive) ; Get drive wanted ld hl,ldrive ; Point at last drive used cp (hl) ; Same one? ret z ; Yes, do nothing. cp 4 ; Is it 4 or more? ret nc ; Return if so, no good. ld e,(hl) ; Get the old one ld (hl),a ; Save current, committed now ld c,a ; Save a copy ld a,e ; The previous drive. cp 0ffh ; was there really one? jr z,selec1 ; No, no need to save the track. ld hl,trkvec ; Point at track slot if xrxsds ld a,e and 1 ; make the two sides of the same ld e,a ; point to the same track save endif ld d,0 add hl,de ; for the old drive in a,(fdc.tk) ; Read old track number ld (hl),a ; Put it in vector for next use of drive selec1: in a,(pio.sd) ; Turn off the drive/side select bits so that and 0f8h ; first call to diskup will set them. if bigbrd or 4 ; Deselct the drive endif out (pio.sd),a ld hl,trkvec ; Now get track of new drive if xrxsds ld a,c and 1 ; make the two sides of the same ld c,a ; point to the same track save endif ld b,0 add hl,bc ld a,(hl) cp 0ffh ; If unknown, home this drive jr z,homea out (fdc.tk),a ; Else tell 1771 where the head is xor a ; Return Z, success ret ; Home the drive home: call select homea: call diskup ; Get the drive up to speed first ret nz ; Return if can't xor a ; OK, set cylinder to zero ld (track),a homeb: ld b,0ch ; Set seek track 0 command call fdcsek ; Go do it xor 4 ; Set up error bits for return and 9ch ret ; Set track. settrk: ld a,c ld (track),a ret ; Set sector. setsec: ld a,c ld (sect),a ret ; Set dma address. setdma: ld (dmaadr),bc ret ; Read a sector. read: call setup call z,rdsec ret ; Write a sector. write: call setup call z,wtsec ret setup: if pistnc call pu2no endif call pu1no call select jp seek ; Reload CCP + BDOS. ; Set up stuff in low memory for CP/M. ; loadit is the entry point lderrm: db ff,'Disk error loading DOS, type any character to retry.',0 lderr: ld hl,lderrm call putstr call pu3inp ; May want to be pu0inp loadit: ld a,0c3h ld (0),a ld (5),a ld hl,bios+3 ld (1),hl ld hl,bdos+6 ld (6),hl xor a ld (drive),a ; ld e,1 ; To force loggin of disk. call home or a ; check for errors jr nz,lderr ; woops! ld a,cpmsec ld (sect),a ld hl,ccp ld (dmaadr),hl ld b,44 ; # sectors to read. load: push bc call rdsec pop bc or a ; check for errors jr nz,lderr ; woops! dec b ; Count sectors read ret z ; Read em all, done call pokefd ; Prevent motor timeout ld de,secsiz ld hl,(dmaadr) add hl,de ld (dmaadr),hl ld a,(sect) inc a ld (sect),a sub spt+1 ; Done with track? jr nz,load ; No, continue reading ld a,1 ld (sect),a ld hl,track inc (hl) push bc call seek or a ; check for errors jr nz,lderr ; woops! pop bc jr load ; Do a warm boot. bootw: ld sp,80h call loadit ld bc,80h call setdma ld a,(4) ld c,a jp ccp+3 ; Finish the cold boot. bootc: di ld sp,80h call loadit ld bc,80h call setdma xor a ld (yr),a ; Flag for "no date entered" ei ; Print the id message. ld hl,idmsg call putstr jp ccp idmsg: db ff,'DOS 2.2 BIOS 10.6',0 putstr: ld c,(hl) inc hl ld a,c or a ret z call pu3ot ; May want to be pu0ot jr putstr mos: db 31,28,31,30,31,30,31,31,30,31,30,31 clkint: exx ; CTC chan 3 comes here once a second ex af,af' ; Tick the timer. ld hl,(timer) dec hl ld (timer),hl ld a,(sec) inc a cp 60 jr nz,tiksec ld a,(min) inc a cp 60 jr nz,tikmin ld a,(hr) inc a cp 24 jr nz,tikhr ld a,(day) ld hl,mo ld e,(hl) dec e ; Months 0-11 to index table ld d,0 ld hl,mos add hl,de cp (hl) jr nz,tikday ; Not first day of new month ld a,(mo) cp 12 jr nz,tikmon ; Not a new year ld hl,yr inc (hl) xor a tikmon: inc a ld (mo),a xor a tikday: inc a ld (day),a xor a tikhr: ld (hr),a xor a tikmin: ld (min),a xor a tiksec: ld (sec),a ld hl,fdrunt ; Count down run time of floppy motor dec (hl) jr nz,tikdon ; Time not up yet in a,(pio.sd) ; Time up, deselect the drive and 0f8h if bigbrd or 44h ; Turn off motor and deselect endif out (pio.sd),a tikdon: ex af,af' exx ei reti ctcint: exx ; Channel 1 of CTC comes here ex af,af' ld hl,(dsktmr) dec hl ld (dsktmr),hl ex af,af' exx ei reti maclib BIOSVID.INC ; Include video handler (pu3ot) ; Seek to requested track. seek: call diskup ; Make sure disk is spinning ret nz ; It won't. Fail. ld a,(track) ; Desired track cp tracks ; In range? ret nc ; Fail if not. out (fdc.da),a ; Tell the 1771 where to go ld b,1ch ; Seek op call fdcsek ; Plug in step rate and do it and 98h ; Check for any errors ret z ; Done if OK call homeb ; Seek failed. Try recalibrating ret nz ; That failed too? ld a,(track) ; Try the seek again if recal worked out (fdc.da),a ld b,1ch call fdcsek and 98h ; Return success or fail this time ret ; Sector Read and Write routines wtsec: call fdcsts ; See if disk is write locked bit b6,a ret nz ; Don't try writing if locked ld b,0a8h ; Write opcode jr rdsec1 ; Much like read from here on rdsec: ld b,88h ; Read opcode rdsec1: ld hl,fdop ; Save the opcode in fdop ld (hl),b ld hl,retryc ; Point at retryc ld (hl),11 ; Set initial retry count again1: di ; Turn off interrupts during transfer ld hl,nmivec ; Use NMI mechanism for the data xfer ld d,(hl) ; Hold old contents of 0066H ld (hl),0c9h ; Put a "ret" there for now ld b,secsiz ; # bytes/sector set for ini/outi ld c,fdc.da ; I/O port for read and write data ld hl,(dmaadr) ; Memory address for the sector ld a,(sect) ; Tell it what sector we want out (fdc.se),a call fdcsts ; Stop old op, get status bit b5,a ; Head loaded yet? ld a,(fdop) ; Pick up the read or write jr nz,noload ; Go if no need to load it or 4 ; Tell it to load head noload: call fdcdo ; Do the read or write bit b5,a ; Is this a read or a write? jr nz,wrwait ; Go handle write rdwait: halt ; Wait for NMI ini ; Read a byte jp nz,rdwait ; Loop if not done, JP not JR, faster call fdcskw ; Wait for it to finish CRC and 9ch ; Error bits for read jr rwwat1 ; Go clean up at end wrwait: halt ; Wait for NMI, write case outi ; Output a byte jp nz,wrwait ; Loop till sector done, JP not JR call fdcskw ; Wait for CRC to go out, get status and 0bch ; Error bits for write case. rwwat1: ld hl,nmivec ; Restore the contents of 0066H ld (hl),d ei ; Allow interrupts again ret z ; Return if no errors ld hl,retryc ; Nuts, there were errors. dec (hl) ; Count the retries jr nz,again ; If some left, try it all again or a ; Else set NZ with error bits ret ; and fail the read/write. again: call seek jr again1 ; And re-do the read or write. fdcsek: ld a,stept ; Pick up step rate bits or b ; Put in seek/home operation call fdcdo ; Hand it to the chip fdcskw: in a,(fdc.cs) ; Wait for it to finish bit b0,a jr nz,fdcskw ret fdcdo: out (fdc.cs),a ; Do the requested op in 1771 call fdcdow ; And wait a little while fdcdow: ex (sp),hl ; since 1771 is a bit slow on ex (sp),hl ; getting the status right ret fdcsts: ld a,0d0h ; Reset current op call fdcdo ; Do that and delay in a,(fdc.cs) ; See what status is ret pokefd: ld a,whirtm ; Restart motor timer at max ld (fdrunt),a call pokef1 ; A little delay in a,(pio.sd) ; Get system PIO for drive bits pokef1: ret diskup: call pokefd ; Restart timer, get drive bits ld e,0 ; A success code in case allready up. if xerox and 7 jr nz,dskupx ; Return if one is selected endif if bigbrd and 4 jr z,dskupx ; Return if one is selected endif push hl ; Save two reg pairs push bc ld a,(ldrive) ; Get desired drive ld c,a ; Make a copy if xerox and 2 ; If 2 or 3 get a 2 to add to turn that 2 bit add a,c ; into a 4 bit for side select. inc a ; If 0 or 4 turn on bit 1 to select drive A/C, ld c,a ; if 1 or 5 turn that 1 bit to a 2 bit to ; select drive B/D. endif if bigbrd ; Nothing to do here endif in a,(pio.sd) ; Get other bits, we know that drive/side bits or c ; are allready zero or we wouldn't be here, if bigbrd and 0bbh ; Turn on drive motor and select endif out (pio.sd),a ; so we don't have to mask them. ld a,87h ; Tell CTC 1 to time us out (ctc.c1),a if clk4 tkpms equ 250 ; processor clocks per ms / 16 else tkpms equ 156 endif ld a,tkpms ; for a while (1 Ms ticks). out (ctc.c1),a ld hl,2000 ; This many Ms. says disk doesn't turn ld (dsktmr),hl call fdcsts ; See if index bit is on and 2 ld b,a call idxwat ; Wait for index bit to change jr c,offlin ; If it didn't, give up on drive dskup2: ld hl,(dsktmr) ; It changed. Now wait for up to speed call idxwat jr c,offlin ; Go if failed call idxwat ; Wait for two half square-waves jr c,offlin ; Go if failed ld de,(dsktmr) ; Now see if it's fast enough sbc hl,de ld (idxtim),hl ; Save rotation time ld de,msprev or a ; Clear CY for sbc sbc hl,de ; Is it this fast yet? jr nc,dskup2 ; If not, let it turn some more ld e,0 ; Fast enough.Success code. Go accept it jr oflin1 offlin: in a,(pio.sd) ; Disconnect from this drive. It's bad and 0f8h if bigbrd or 4 ; Deselect endif out (pio.sd),a ld e,80h ; Failure code oflin1: ld a,3 ; Through with this CTC channel di ; Because of CTC chip bug out (ctc.c1),a ei pop bc ; Restore registers pop hl dskupx: ld a,e ; Success/fail code or a ; Non-Z says OK ret idxwat: call fdcsts ; Get Index hole bit and 2 xor b ; Compare to old bit jr nz,idxchg ; Go when it changes ld a,(dsktmr+1) ; See if time to give up bit b7,a jr z,idxwat ; No, keep waiting scf ; Failed. This thing isn't turning ret idxchg: ld a,b ; Aha. It changed. xor 2 ; Remember the new Index value ld b,a ret ; Return from idxwat drive: ds 1 ; Drive wanted, 0-3 ldrive: db 0ffh ; Previous drive wanted, 0-3 track: ds 1 trkvec: db 0ffh,0ffh,0ffh,0ffh sect: ds 1 dmaadr: ds 2 fdrunt: db whirtm ; Seconds to let motor run. fdop: db 0 ; Opcode for current floppy op retryc: db 0 ; Retry count for read or write on floppy dsktmr: dw 0 ; Time counter for disk up to speed test idxtim: dw 0 ; Time for disk to turn dirbuf: ds 128 alv0: ds 31 csv0: ds 16 if mxdrv gt 1 alv1: ds 31 csv1: ds 16 endif if mxdrv gt 2 alv2: ds 31 csv2: ds 16 endif if mxdrv gt 3 alv3: ds 31 csv3: ds 16 endif bufbas equ 256*(($ shr 8)+1) bslen equ $-bios ; Length of the bios load end