TITLE 'BIOS - NORTH STAR DD CP/M 2.2 OF 02/21/82' ; PAGE 44 ; ; These routines copyright (c) 1980, 1981, 1982 by Steve Bogolub, ; 2338 S. Scoville Ave., Berwyn, IL 60402. Jade Double D (tm) ; disk handlers contributed to the public domain for use on Jade ; equipment by special permission of Jade Computer Products. ; This software may be used freely for non-commercial purposes ; only, and may not be sold. ; ; THIS BIOS CONTAINS ROUTINES TO SUPPORT THE ; FOLLOWING HARDWARE: ; ; DISK: NORTH STAR MDS-AD2 DOUBLE DENSITY 5.25" ; FLOPPY DISK UNITS 1 AND 2 AS CP/M DRIVES ; A: AND B: ; ; JADE DOUBLE D REV C 8" FLOPPY DISK UNITS ; 0 AND 1 AS CP/M DRIVES C: AND D: ; ; SERIAL I/O: HORIZON LEFT (LOW) SERIAL I/O PORT AS ; CP/M DEVICE CRT: ; ; QT IO+ BLOCK D (RIGHT) USART AS CP/M DEVICE ; UC1:, INIT'ED TO 19.2K BAUD RATE ; ; QT IO+ BLOCK C (LEFT) USART AS CP/M DEVICE ; TTY:, INIT'ED TO 300 BAUD RATE ; ; HORIZON RIGHT (HIGH) SERIAL I/O PORT AS ; CP/M DEVICE LPT:, WHICH IS SUPPORTED AS ; A MODEL 40 TELETYPE WITH THE SIMPLIFIED ; (HANDSHAKE) EIA INTERFACE, AND IS GENNED-IN ; AS THE LIST DEVICE. ; ; THE GENNED-IN CONSOLE IS DETERMINED BY SOLICITING ; A SPACE CODE (20H) FROM CRT:, TTY:, AND UC1:. IF ; NO SPACE CODE IS RECEIVED AFTER ABOUT EIGHT SECONDS ; (AT 4 MHZ), CRT: IS ASSUMED TO BE THE CONSOLE. IF ; A SPACE IS READ FROM ONE OF THE DEVICES, THAT DEVICE ; IS ASSUMED TO BE THE CONSOLE. ; ; LIST DEVICE UL1: PRINTS ON THE CURRENT CON: DEVICE. ; IF TTY: IS THE LIST DEVICE, DTR MUST BE HIGH, PROVIDING ; A SERIAL PRINTER PORT. BAT: IS NOT SUPPORTED, AND IS ; SET TO LPT: FOR OUTPUT AND CRT: FOR INPUT. ; ; SINCE THIS BIOS IS SO LARGE, MOVCPM SHOULD BE ; RUN FOR TWO LESS THAN THE DESIRED SYSTEM SIZE ; IN K, I.E. SPECIFY 30 FOR A 32K SYSTEM. ; ; *** IMPORTANT BOOTSTRAP INFO *** ; ; THIS BIOS IS INTENDED TO RUN IN THE LAST ; 3.5K OF MEMORY. THE LAST .5K IS SET ASIDE ; FOR A NORTH STAR SECTOR DEBLOCKING BUFFER. ; THE FIRST 3K IS GENNED-IN BIOS CODE, AND ; IS EXPECTED TO RESIDE ON TRACK 0 OF NORTH ; STAR DISK UNIT 1 (A:) IN SECTORS 5-9, AND ; 4. THE NORTH STAR BOOT PROM READS IN SECTOR ; 4 INTO CONSECUTIVE PAGES OF MEMORY, STARTING ; WITH THE PAGE NUMBER SPECIFIED BY THE FIRST ; BYTE OF SECTOR DATA, THEN JUMPS TO THAT ; ADDRESS + 10 (0AH). THIS BIOS IS SET UP TO ; USE THAT METHOD TO COLD BOOT ITSELF. CODE ; AT THAT ADDRESS READS THE LAST FIVE SECTORS ; OF TRACK ZERO INTO MEMORY AT ADDRESS "BIOS", ; THEN JUMPS TO THE BIOS COLD START ADDRESS. ; THE BIOS COLD BOOT EXPECTS THE JADE DOUBLE D ; CONTROL CODE ("NDCM") TO RESIDE ON SECTORS ; ONE AND TWO OF TRACK ZERO, AND LOADS THOSE ; SECTORS FROM THE DISK INTO THE JADE DD MEMORY, ; THEN RESETS AND STARTS UP THE JADE DD RESIDENT ; Z80. THE BIOS WARM BOOT EXPECTS THE CCP/BDOS ; CODE TO RESIDE ON TRACKS 0-9 OF TRACK ONE ; (IN OTHER WORDS, THE WHOLE TRACK), AND THE ; LAST SECTOR IS EXPECTED TO BE ON TRACK ZERO, ; SECTOR 3. TRACK ZERO, SECTOR ZERO IS RESERVED ; FOR LIFEBOAT-COMPATIBLE ID INFO AND FUTURE ; ADDITIONS. THE MODIFIED VERSION OF SYSGEN ; NAMED "NSGEN" IS SET UP TO TRANSFER CP/M TO ; AND FROM DISK IN THIS REQUIRED MANNER. ; ; WE USE THE Z80 INSTRUCTION "LD A,I" ON ENTRY ; TO THE DISK READ AND WRITE ROUTINES TO GET ; THE STATUS OF THE INTERRUPT FLIP/FLOP, SINCE ; WE MUST DISABLE INTERRUPTS WHILE THE NORTH ; STAR DISK READS OR WRITES ARE PERFORMED. ; USING THE Z80 STATUS, WE CAN RE-ENABLE ; INTERRUPTS AFTER WE LEAVE. THIS CHECK IS ; DONE BY THE ROUTINES "INTDI" AND "INDEN", ; AND CAN EASILY BE REMOVED IF WE EVER GO ; TO A NON-Z80 CPU (NEVER, I HOPE). ; LDAI EQU 057EDH ;Z80 "LD A,I" BYTE-SWAPPED ; FOR "DW" USE ; ; DISK OPERATING SYSTEM ADDRESSES ; NKSYS EQU 56 ;SYS SIZE IN K BYTES KBYTE EQU 1024 ;1K BYTE SIZE CPMSZ EQU NKSYS*KBYTE ;TOP SYSTEM ADDRESS CPMBS EQU CPMSZ-(22*KBYTE);CP/M BIAS VALUE CCP EQU CPMBS+3400H ;ADDRESS OF CCP BDOS EQU CPMBS+3C00H ;ADDRESS OF BDOS BIOS EQU CPMBS+4A00H ;ADDRESS OF BIOS BIOSR EQU 1F80H-BIOS+400H ;DDT LOAD OFFSET ;(LEAVES ROOM FOR NDCM) IOBYTE EQU 0003H ;IOBYTE ADDRESS DEFDSK EQU 0004H ;DEFAULT DISK FOR CCP SECSZ EQU 128 ;BYTES PER SECTOR HSTSIZ EQU 512 ;BYTES PER N* SECTOR NDRVS EQU 4 ;# DRIVES IN SYSTEM ; ; BDOS CONSTANTS ON ENTRY TO WRITE ; WRALL EQU 0 ;WRITE TO ALLOCATED WRDIR EQU 1 ;WRITE TO DIRECTORY WRUAL EQU 2 ;WRITE TO UNALLOCATED ; ; NORTH STAR MEMORY MAPPED I/O ADDRESSES ; NSROM EQU 0E800H ;COLD BOOT ROM ADDRESS WDATA EQU 0E900H ;WRITE DATA. DATA IS ; LOW 8 ADRESS BITS. CORDER EQU 0EA00H ;CONTROLLER ORDER CCMND EQU 0EB00H ;CONTROLLER COMMAND ; ; DOUBLE D HARDWARE PARAMETERS ; DPORT EQU 043H ;DOUBLE D PORT ADDRESS DBASE EQU 0E000H ;DOUBLE D WINDOW MEM BASE ADDR DSHLT EQU 001H ;STATUS PORT INDICATOR ; ; DOUBLE D HARDWARE COMMANDS ; DCSIN EQU 001H ;SWITCH DD BANK 0 INTO SYS DCMB0 EQU 001H ;SELECT DD BANK 0 DCMB1 EQU 003H ;SELECT DD BANK 1 DCSOT EQU 000H ;SWITCH DD MEM OUT OF SYS DCINT EQU 002H ;ISSUE DD Z80A INTERRUPT DCBGN EQU 080H ;RESET DD Z80A AND EXECUTE ; ; DISK CONTROLLER MODULE LINKAGE (DCM - VER 2.2) ; ; -- COMMAND BLOCK DEFINED ; DDCBT EQU 0370H ;COMMAND BYTE (BANK 0) DDDRV EQU 0371H ;DRIVE NUMBER (BANK 0) DDTRK EQU 0372H ;TRACK NUMBER (BANK 0) DDSEC EQU 0373H ;SECTOR NUMBER (BANK 0) DDSTS EQU 0377H ;COMMAND STATUS (BANK 0) DDBBF EQU 0000H ;1024 SECTOR BUFFER (BANK 1) DDFBF EQU 0300H ;FORMAT BUFFER (BANK 1) FMTSZ EQU 0100H ;FORMAT BUFF SIZE DDDPB EQU 0020H ;ID SEC DPB (BANK 1) DDDDF EQU DDDPB+0011H ;ID SEC FLAGS (BANK 1) ; ; -- DCM COMMANDS ; DCLOG EQU 000H ;LOG ON DISKETTE DCRDS EQU 001H ;READ SECTOR DCWRS EQU 002H ;WRITE SECTOR DCFMT EQU 003H ;FORMAT TRACK DCIDL EQU 007H ;STAY IDLE ; ; CONSOLE DEFINITIONS ; MOTHR EQU 000H ;HORIZON MOTHERBOARD BASE CRT EQU MOTHR+2 ;HORIZON LEFT SERIAL BASE ; QTIO EQU 0A0H ;QT IO+ BOARD BASE QTCTRL EQU QTIO+03H ;QT CONTROL PORT QTBAUD EQU QTIO+01H ;QT BAUD RATE PORT TTY EQU QTIO+08H ;QT LEFT SERIAL BASE UC1 EQU QTIO+0CH ;QT RIGHT SERIAL BASE ; CR EQU 0DH ;ASCII CARRIAGE RETURN LF EQU 0AH ;ASCII LINE FEED SPACE EQU 20H ;ASCII SPACE ; ; PRINTER DEFINITIONS ; LPD EQU MOTHR+4 ;DATA OUT LPC EQU LPD+1 ;STATUS LPIO EQU MOTHR+6 ;CHAIN CONTROL WORD CHAIN EQU 10H ;CHAIN RUNNING BIT IN LPIO FF EQU 0CH ;ASCII FORM FEED ; ; ; BIOS JUMP VECTOR TABLE ; ORG BIOS ;START OF BIOS CODE ; JMP NSROM ;COLD BOOT FROM ROM JMP WARM ;RELOAD CCP/BDOS ; ; USE VECTORS FOR INTERNAL CALLS ON CONSOLE ROUTINES ; INSTEAD OF CALLING THE ROUTINES DIRECTLY SO THAT ; BIOS ERROR I/O CAN BE HANDLED BY "BYE" AND OTHER ; VECTOR-PATCHING PROGRAMS. ; BCNSCK: JMP CNSCK ;GET CONSOLE STATUS BCNSIN: JMP CNSIN ;CONSOLE INPUT BCNSOT: JMP CNSOT ;CONSOLE OUTPUT ; JMP LIST ;PRINTER OUTPUT JMP PUNCH ;PUNCH OUTPUT JMP READER ;READER INPUT JMP HOME ;HOME SELECTED DRIVE JMP SELDSK ;SELECT DISK DRIVE JMP SETTRK ;SET TRACK NUMBER JMP SETSEC ;SET SECTOR NUMBER JMP SETDMA ;SET TRANSFER ADDRESS JMP DISKRD ;PERFORM DISK READ JMP DISKWR ;PERFORM DISK WRITE JMP LISTST ;RETURN LIST STAT JMP SECTRN ;TRANSLATE SECTOR JMP FORMAT ;FORMAT A JADE DD TRACK ; ; INIT - COLD START ENTRY ** DIRECTORY BUFFER OVERLAY ** ; DIRBF EQU $ ;BUFFER BEGINNING ; ; SCRATCH RAM FOR BDOS OVERLAY ; D0ALL EQU DIRBF+SECSZ ;OVERLAY ALLOCATE AND CHECK D0CHK EQU D0ALL+23 ; BUFFERS HERE TOO D1ALL EQU D0CHK+16 D1CHK EQU D1ALL+23 D2ALL EQU D1CHK+16 ;NOTE THAT THE JADE DISKS D2CHK EQU D2ALL+39 ; (C: AND D:) MAY NEED D3ALL EQU D2CHK+32 ; MUCH MORE ALLOC AND D3CHK EQU D3ALL+39 ; CHECK SPACE THAN N* ; ENDOV EQU D3CHK+32 ;END OF OVERLAY AREA ; ; THE INIT ROUTINE IS ACTUALLY A CONTINUATION OF THE ; COLD BOOT BEGUN DOWN AT "BOOT". ; INIT: LXI SP,HSTBUF+HSTSIZ ;SET SP TO SCRATCH AREA LXI H,CCP ;WHERE TO START CP/M PUSH H XRA A ;INIT HORIZON MOTHERBOARD OUT MOTHR+6 STA COLDB ;BIOS NOW IN MEMORY MVI A,0B1H ;INIT QT IO+ BOARD OUT QTCTRL MVI A,01FH ;INIT UC1: TO 19.2K BAUD OUT QTBAUD MVI A,005H ;INIT TTY: TO 300 BAUD OUT QTBAUD CALL DELAY ;DELAY FOR 8251'S MVI A,0AEH ;INIT SERIAL PORTS OUT CRT+1 ;OUTPUT DUMMY MODE TO INSURE OUT TTY+1 OUT UC1+1 OUT LPC CALL DELAY MVI A,040H ; CMD EXPECTED, THEN OUTPUT OUT CRT+1 ; RESET CMD OUT TTY+1 OUT UC1+1 OUT LPC CALL DELAY MVI A,04EH ;MODE: 1 STOP BIT, 16X CLK, OUT CRT+1 ; 8 DATA BITS, NO PARITY OUT TTY+1 OUT UC1+1 OUT LPC CALL DELAY MVI A,037H ;CMD: RTS, ER, RXEN, DTR, TXEN OUT CRT+1 OUT TTY+1 OUT UC1+1 CALL DELAY CALL LGOOSE ;FINISH INIT OF MODEL 40 IN CRT ;FLUSH RECEIVER INPUTS IN TTY IN UC1 ; ; WAIT APPROX 8 SECONDS FOR OPERATOR TO STRIKE ; THE SPACE BAR ON CRT:, UC1:, OR TTY: TO DETERMINE ; CONSOLE. IF NO SPACE RECEIVED, DEFAULT TO CRT:. ; LXI H,0 ;SET UP WAIT MVI B,2 ; FOR ABOUT 8 SECS ; ICHK: CALL CRTCHK ;CHECK CRT: JZ ICHK5 ;PASS IF NOTHING IN CRT ; ELSE READ CHAR ANI 07FH ;STRIP PARITY CPI SPACE ;IS IT SPACE? JNZ ICHK5 ;TOSS IF NOT JMP ICHK20 ;IF SO, CON:=CRT: ; ICHK5: CALL TTYCHK ;CHECK TTY: JZ ICHK10 IN TTY ANI 07FH CPI SPACE JNZ ICHK10 MVI A,00B ;CON:=TTY: JMP ICHK25 ;GO SET IT ; ICHK10: CALL UC1CHK ;CHECK UC1: JZ ICHK15 IN UC1 ANI 07FH CPI SPACE JNZ ICHK15 MVI A,11B ;CON:=UC1: JMP ICHK25 ;GO SET IT ; ICHK15: DCX H ;COUNT DOWN MOV A,H ORA L JNZ ICHK DCR B JNZ ICHK ; ICHK20: MVI A,01B ;TIME UP, SET CON:=CRT: ; ICHK25: ADI 80H ;DEFAULT LST:=LPT: STA IOBYTE ;SET INITIAL I/O BYTE LXI H,MSGSO ;SIGN-ON MSG ADDRESS CALL MSGOT ;ISSUE MESSAGE ; ; MUST NOW LOAD UP THE JADE DOUBLE D CONTROL CODE ; OFF THE DISK AND START UP THE DOUBLE D. ; MVI A,DCSIN ;REQUEST BANK ZERO OUT DPORT MVI A,1 ;CODE STARTS ON SEC 1 STA HSTSEC ;WE KNOW HSTTRK STILL 0 IN DPORT ;INPUT DD BOARD STATUS ANI 0EH ;MASK FOR ADDRESS SWITCHES RLC ;POSITION BITS ORI DBASE SHR 8 ;OR IN BASE ADDRESS MOV H,A ;FORM WINDOW ADDRESS IN HL MVI L,0 SHLD DADDR ;SAVE ADDRESS FOR ALL JADE I/O SHLD HSTADR ;SET NS I/O ADDR TO DD MEM CALL WSETUP ;KICK MOTORS AND SEEK TRK CALL WRMRD ;READ SECTOR AND CHECK ERR MVI A,2 ;CODE EXTENDS INTO SEC 2 STA HSTSEC LXI D,512 LHLD DADDR DAD D SHLD HSTADR CALL WRMRD MVI A,DCBGN ;NOW START UP DD Z80 OUT DPORT ; JMP CPMLD ;GO PERFORM WARM BOOT FUNCS ; ; THIS DELAY SUBROUTINE IS USED TO HELP US OUT ON ; TIMING WHEN RESETTING THE 8251 CONSOLE SERIAL DEVICE. ; DELAY: LXI B,600H DEL5: DCX B MOV A,B ORA C JNZ DEL5 RET ; MSGSO: DB CR,LF,'North Star / ' DB 'Jade DD ' DB (NKSYS/10)+'0',(NKSYS MOD 10)+'0' DB 'K CP/M 2.2 of 02/21/82',CR,LF+80H ; IF ($-ENDOV) SHR 15 ORG ENDOV ;FILL OUT OVERLAY SIZE ENDIF ; ; SELECT DRIVE - LOGON ; SELDSK: LXI H,0 ;ERROR RETURN CODE MOV A,C ;PUT DRIVE # IN A CPI NDRVS ;CHECK IF LEGAL DRIVE RNC ;NO CARRY IF ILLEGAL STA SEKDSK ;STORE DRIVE NUMBER MOV B,E ;SAVE LOGON REQ REG MOV L,C ;L = DISK NUMBER MVI H,0 ;ZERO H REG DAD H ; *2 DAD H ; *4 DAD H ; *8 DAD H ; *16 (SIZE OF HEADER) LXI D,D0DPH ;DRIVE 0 DPH DAD D ;HL = DRIVE N DPH SHLD DTPTR ;STORE DRIVE TBL PTR ; ; LOG-ON - SET DISK PARAMETER BLOCK ; ; -- CHECK IF LOG-ON REQUESTED ; XRA A STA LOGFLG ;ASSUME NO JADE LOGON MOV A,B ;CHECK LOG REQUEST ANI 001H ;LOG ON BIT TEST JNZ NOLOG ;PASS IF NO REQUEST CALL HFLUSH ;MAKE SURE HOST BUF JNZ LOGERR ; AVAILABLE, OUT ON ERR ; ; SEE IF DISK IS JADE OR NORTH STAR. ; LDA SEKDSK ;GET DISK # CPI 2 JNC LOGJAD ;IF JADE, GO ELSEWHERE ; ; LOG ON NORTH STAR BY READING ID SECTOR, THEN DECIDE ; WHICH TABLE TO USE BASED ON SINGLE OR DOUBLE DENSITY. ; IF DOUBLE DENSITY, LOOK FOR LIFEBOAT 2.X FLAG BYTE ; AT OFFSET 05CH IN THE SECTOR, AND USE LIFEBOAT 2.X ; DPB IF THAT BYTE CONTAINS 0B0H. IF NOT, ASSUME OUR ; NORMAL LIFEBOAT 1.4-COMPATIBLE DPB FOR DOUBLE DENSITY. ; STA HSTDSK ;GOING TO ACCESS THIS DISK XRA A STA HSTTRK ;TRACK ZERO STA HSTSEC ;N* SECTOR ZERO CALL INTDI ;INTERRUPTS OFF CALL READHST ;READ THE SECTOR CALL INTEN ;INTERRUPTS RESTORED LDA ERFLAG ;CHECK FOR READ ERROR ORA A JNZ LOGERR ;LOG-ON ERROR IF SO LDA NSDENS ;IF OK, GET DENSITY FLAG LXI B,TRAN5S ;ASSUME SINGLE DENSITY, LXI D,DPBNSS ; EVEN THO USUALLY WRONG ORA A ;CHECK JNZ LOGNSD ;GO ON IF RIGHT LXI B,TRAN5D ;DOUBLE DENS, COMMON XLATE LXI D,DPBNSD ; BUT ASSUME OUR 1.4 DPB LDA HSTBUF+05CH ;GET LIFEBOAT FLAG BYTE CPI 0B0H ;IS IT LIFEBOAT 2.X FLAG? JNZ LOGNSD ;KEEP OUR DPB IF NOT LXI D,DPBNSL ;GO TO LIFEBOAT 2.X IF NOT ; LOGNSD: LHLD DTPTR ;LOAD DRIVE TBL PTR MOV M,C ;SET TRANSLATE TABLE ADDR INX H MOV M,B LXI B,9 ;PT TO DPB ADDR DAD B MOV M,E ;SET DPB ADDR INX H MOV M,D JMP NOLOG ;GO COMPLETE LOGON NOW ; ; -- READ JADE IDENTITY SECTOR ; LOGJAD: STA LOGFLG ;LOGGING ON JADE DISK MVI A,DCSIN ;SWITCH DD INTO SYS OUT DPORT ;ISSUE HARDWARE CMND MVI A,DCLOG ;LOAD DCM LOG-ON CMND CALL DSKEX ;PERFORM DISK OP JZ LOGCK ;PASS IF OK CALL DSKER ;ERROR, BAD LOG ON ; LOGERR: LXI H,0 RET ; ; -- CHECK FOR JADE ID ; LOGCK: MVI A,DCMB1 ;SELECT BANK 1 OUT DPORT ; BECAUSE BUFFER IS THERE LHLD DADDR ;GET DD BUFFER ADDR IN HL LXI D,JADEID ;DE PNTS TO BIOS ID MVI B,IDSZE ;SET LABEL SIZE LOGID: LDAX D ;GET LABEL CHARACTER CMP M ;DOES ID SECTOR MATCH? JNZ LG3740 ;ASSUME 3740 IF NOT INX H ;ADVANCE PTRS INX D DCR B JNZ LOGID ;GO BACK IF MORE TO MATCH ; ; -- DISKETTE CONTAINS ID ; CALL TRNONE ;ASSUME DDENS CALL DPBAD ;GET DPB ADDR IN DE LXI B,DDDPB ;GET ID DPB ADDR IN HL LHLD DADDR DAD B LXI B,DPBSZ ;DPB SIZE IN BYTES CALL BLOCK ;MOVE INTO DPB LXI B,DDDDF ;CALC ADDR OF ID FLAGS LHLD DADDR DAD B MOV A,M ;GET ID FLAGS IN ACC PUSH PSW ;SAVE FLAGS ANI 0F0H ;STRIP LOW FLAGS RAR ;MOVE SECTOR SHIFT BIT RAR ; FLAG RIGHT RAR DCX D ;PT INTO DPB ADD-ON STAX D ;SAVE SECTOR SHIFT ORA A ;IS THERE A SHIFT? CNZ TR1024 ;USE 1024 BYTES/SECTOR ; XLATE TABLE IF SO POP PSW ;GET ORIG FLAGS AGAIN ANI 04H ;TEST DATA DENSITY CZ TR3740 ;IF ZERO USE 3740 TRN ; ; SET UP DEBLOCKING VARIABLES FROM DPB VALUES ; NOLOG: CALL DPBAD ;GET DPB ADDR XCHG ; IN HL MOV A,M ;SET UP DEBLOCK STA CPMSPT ; SECTORS PER TRACK INX H ;GET TO GROUP MASK INX H INX H MOV A,M ;GET IT INR A ;CALC # BLOCKS/GROUP STA UNAVAL ; AND SAVE THAT LXI D,12 DAD D ;PT TO SECTOR SHIFT MOV A,M ; BIT FLAG ORA A ;HAVE WE GOT ONE? JZ NOSHF ;PASS IF NOT MVI B,0FFH ;INIT SHIFT COUNT ; CALCSH: INR B ;COUNT UP A SHIFT RAR ;SEE IF DONE JNC CALCSH ;GO BACK IF NOT YET MOV A,B ;SHIFT COUNT TO ACC ; NOSHF: STA SECSHF ;STORE SHIFT COUNT MOV A,M ;RELOAD BIT FLAG DCR A ;FORM SECTOR MASK STA SECMSK ; AND SET IT LHLD DTPTR ;RELOAD PTR LDA LOGFLG ;WAS JADE LOGGED ON? ORA A ;FLAG NON-ZERO IF SO, JNZ DSKOK ; GO HANDLE DCM MORE RET ;IF NOT, DONE, RET ZERO ; ; -- ASSUME 3740 DISKETTE ; LG3740: CALL TR3740 ;SET SECTOR TRANSLATE CALL DPBAD ;SET REGISTER DE LXI B,DPBSZ ;DPB SIZE IN BYTES LXI H,DPB8 ;ADDRESS OF BLK IMAGE CALL BLOCK ;MOVE INTO DPB JMP NOLOG ;GO SET UP (CLEAR) THE ; DEBLOCK VARIABLES ; ; -- SET 3740 SECTOR TRANSLATION ; TR3740: LXI D,TRAN8 ;SECTOR TRAN TBL ADDR ; TRCOM: LHLD DTPTR ;ADDR DISK PARA HDER MOV M,E INX H MOV M,D RET ; ; -- SET 1024 BYTES/SECTOR TRANSLATION ; TR1024: LXI D,TRN124 ;SECTOR TRAN TBL ADDR JMP TRCOM ;GO SET IT ; ; -- SET NO SECTOR TRANSLATION ; TRNONE: XRA A ;ZERO A REG LHLD DTPTR MOV M,A INX H MOV M,A RET ; ; -- GET DRIVE PARA BLK ADDR ; DPBAD: LHLD DTPTR ;ADDR DISK PARA HDED LXI D,10 ;DPB TBL PNTR OFFSET DAD D ;NOW AT DPB PNTR MOV E,M ;LOAD INTO DE INX H MOV D,M RET ;RETURN TO LOG USER ; ; HOME DRIVE ; HOME: MVI C,0 ;SET TRACK TO ZERO TO HOME LDA HSTWRT ;CHECK FOR PENDING WRITE ORA A JNZ SETTRK STA HSTACT ;CLEAR HOST ACTIVE IF NOT ; ; SET TRACK ; SETTRK: MOV A,C ;MOVE TRACK NUMBER STA SEKTRK ; THEN SAVE IT RET ;RETURN TO CALLER ; ; SET SECTOR ; SETSEC: MOV A,C ;MOVE SECTOR NUMBER STA SEKSEC ; THEN SAVE IT RET ;RETURN TO CALLER ; ; SET TRANSFER ADDRESS ; SETDMA: MOV H,B ;MOVE ADDR TO HL MOV L,C SHLD DMAADR ; THEN SAVE IT RET ;RETURN TO CALLER ; ; SECTOR TRANSLATION ; SECTRN: MOV A,E ;IS THERE A TABLE? ORA D JZ JTRAN ;JADE SPECIAL XLATE IF NOT XCHG ;MAP OFF TABLE IF SO DAD B MOV L,M MVI H,0 RET ; JTRAN: MOV H,B ;JADE XLATE OFF BY ONE MOV L,C ; SO FIX FOR CALLER INX H RET ; THEN DONE ; ; IOBYTE IS SUPPORTED FOR CONSOLE AND LIST DEVICE ; ; CONSOLE STATUS ; CNSCK: CALL CONS ;GET HARDWARE STATUS RZ ;IF NO CHAR READY, RETURN 0 MVI A,0FFH ; ELSE RETURN 0FFH RET ; CONS: LDA IOBYTE ;GET IOBYTE CALL ROUTE ;DISPATCH TO STATUS ROUTINE DW TTYCHK ;TTY: DW CRTCHK ;CRT: DW CRTCHK ;BAT: (RDR: NOT SUPPORTED) DW UC1CHK ;UC1: ; ; CONSOLE INPUT ; CNSIN: CALL CONS ;GET HARDWARE STATUS JZ CNSIN ;WAIT FOR CHAR READY CALL CONIN ; THEN GET CHAR ANI 07FH ;STRIP PARITY RET ; AND RETURN IN ACC ; CONIN: LDA IOBYTE CALL ROUTE ;DISPATCH TO INPUT ROUTINE DW TTYIN ;TTY: DW CRTIN ;CRT: DW CRTIN ;BAT: NOT SUPPORTED, USE CRT: DW UC1IN ;UC1: ; ; HXBOT DISPLAYS HEXIDECIMAL EQUIV OF ACC CONTENTS. ; HXBOT: PUSH PSW ;SAVE CHAR RRC ;SWAP NIBBLES RRC RRC RRC CALL HXNOT ;OUTPUT HIGH NIBBLE POP PSW ; THEN LOW NIBBLE HXNOT: ANI 0FH ;STRIP HIGH NIBBLE ADI 90H ;CVT TO ASCII DECIMAL DAA ACI 40H DAA MOV C,A ;CHAR TO C JMP BCNSOT ;THRU VECTOR TO CNSOT ; ; CONSOLE OUTPUT ; CNSOT: LDA IOBYTE CALL ROUTE ;DISPATCH TO OUTPUT ROUTINE DW TTYOT ;TTY: DW CRTOT ;CRT: DW LPTOT ;BAT: NOT SUPPORTED, USE LPT: DW UC1OT ;UC1: ; ; READER INPUT ; READER: EQU CNSIN ;SAME AS CONSOLE INPUT ; ; PUNCH OUTPUT ; PUNCH: EQU CNSOT ;SAME AS CONSOLE OUTPUT ; ; LIST STATUS ; LISTST: XRA A ;SAY IT'S READY DCR A RET ; ; LIST OUTPUT ; LIST: LDA IOBYTE RLC ;ROTATE LST: BITS TO LOW RLC ; BIT POSITIONS CALL ROUTE ;DISPATCH TO LIST ROUTINE DW TTYOTS ;TTY: WITH DTR FOR ; SERIAL PRINTERS DW CRTOT ;CRT: DW LPTOT ;LPT: DW CNSOT ;UL1: USES CURRENT CON: ; ; THE ROUTING ROUTINE ; ROUTE: RLC ;DOUBLE FOR WORD OFFSET ANI 06H ;STRIP UNUSED BITS XTHL ;GET DISPATCH TABLE ADDR ADD L ;ADD OFFSET TO GET MOV L,A ; TO CORRECT VECTOR JNC ROUTE5 INR H ROUTE5: MOV A,M ;PULL VECTOR INX H MOV H,M MOV L,A XTHL ;STACK VECTOR, RESTORE HL RET ; THEN OFF TO ROUTINE ; ; HORIZON TTY: ROUTINES ; TTYCHK: IN TTY+1 ;GET PORT STATUS ANI 2 ;CHECK RECEIVER RET ;RET NON-ZERO IF READY ; TTYIN: IN TTY ;GET PORT DATA RET ; TTYOT: IN TTY+1 ;GET PORT STATUS RRC ;XMIT BUFFER EMPTY? JNC TTYOT ;IF NOT, WAIT TIL IS ; ; HERE FROM TTYOTS BELOW TO OUTPUT CHAR FROM C ; TTYOTC: MOV A,C ; THEN OUTPUT CHAR FROM C OUT TTY RET ; ; SERIAL PRINTER ROUTINE. WAIT FOR DTR HIGH, XMIT ; BUFFER EMPTY, AND XMITTER READY BEFORE SENDING NEXT ; CHAR. THIS ROUTINE HAS BEEN TESTED WITH THE EPSON ; MX-80 WITH 2K SERIAL BUFFER, WHICH SHOULD BE ; TYPICAL OF SERIAL PRINTERS. ; TTYOTS: IN TTY+1 ANI 85H CPI 85H ;NEED ALL 3 BITS HIGH JNZ TTYOTS JMP TTYOTC ;NOW OUTPUT ; ; HORIZON CRT: ROUTINES. SAME AS TTY:, BUT DIFF PORT ; CRTCHK: IN CRT+1 ;GET PORT STATUS ANI 2 ;CHECK RECEIVER RET ;RET NON-ZERO IF READY ; CRTIN: IN CRT ;GET PORT DATA RET ; CRTOT: IN CRT+1 ;GET PORT STATUS RRC ;XMIT BUFFER EMPTY? JNC CRTOT ;IF NOT, WAIT TIL IS MOV A,C ; THEN OUTPUT CHAR FROM C OUT CRT RET ; ; UC1 CONSOLE SERIAL ROUTINES ; UC1CHK: IN UC1+1 ;GET PORT STATUS ANI 02H ;CHECK RECEIVER RET ;RET NON-ZERO IF READY ; UC1IN: IN UC1 ;GET PORT DATA RET ; UC1OT: IN UC1+1 ;GET PORT STATUS RRC ;XMIT BUFFER EMPTY? JNC UC1OT ;IF NOT, WAIT TIL IS MOV A,C ; THEN OUTPUT CHAR FROM C OUT UC1 RET ; ; LIST CHAR OUT TO MODEL 40 LINE PRINTER ; LPTOT: PUSH H ;SAVE HL PUSH B ;SAVE CHAR TO OUTPUT IN LPIO ;SEE IF M40 CHAIN RUNNING ANI CHAIN JZ LOUT7 ;SKIP RE-INIT IF SO ; LOUT5: CALL LGOOSE ;GOOSE PRINTER ; LOUT7: LXI H,0 ;LOAD UP FOR NICE LONG DELAY MVI B,8 ; LOUT10: DCX H ;COUNT DOWN MOV A,H ;SEE IF ZERO YET ORA L JNZ LOUT20 ;PASS IF NOT DCR B ;COUNT OFF HIGHER BITS JNZ LOUT20 ;PASS IF NOT ZERO YET ; LOUT15: MVI A,'P' ;DISK P: NOT READY, FOR PRINTER CALL PNTRDY ;REPORT, AND RETURN IF NO CTRL-C JMP LOUT5 ;NOW GO RE-INIT THE PRINTER ; LOUT20: IN LPIO ;CHECK FOR CHAIN NOT RUNNING ANI CHAIN JNZ LOUT10 ;COUNT DOWN TIMEOUT IF SO IN LPC ;GET 8251 STATUS ANI 85H ;STRIP OUT REQ NXT CHAR, XMIT CPI 85H ; BUF RDY, AND XMIT EMPTY, ; THEN CHECK THEM JNZ LOUT10 ;ALL 3 MUST BE UP TO SEND ; ; PRINTER NOW READY. OUTPUT CHAR. ; POP B ;RESTORE CHAR TO C MOV A,C ; AND GET IT TO ACC ANI 7FH ;STRIP PARITY OUT LPD ;OUTPUT THE CHAR CPI FF ;WAS CHAR FORM FEED? JNZ LOUT35 ;PASS IF NOT MVI L,6 ;MUST FOLLOW WITH NULLS IF SO ; LOUT30: IN LPC ;WAIT FOR BUFFER READY RRC JNC LOUT30 XRA A ; THEN OUTPUT NULL OUT LPD DCR L ; AND COUNT IT OFF JNZ LOUT30 ;GO BACK IF MORE TO DO ; LOUT35: MOV A,C ;RESTORE CHAR TO ACC POP H ;RESTORE HL RET ; AND RETURN ; ; TOGGLE DTR FOR PRINTER TO GOOSE IT INTO STARTING CHAIN UP ; LGOOSE: MVI A,035H ;RAISE DTR OUT LPC CALL LWAIT ;DELAY MVI A,037H ;NOW DROP DTR TO TOGGLE OUT LPC LWAIT: XRA A ;DELAY LPIL: XTHL XTHL XTHL XTHL DCR A JNZ LPIL RET ; ; DEBLOCKING DISK WRITE ; DISKWR: CALL INTDI ;MASK INTERRUPTS CALL DSKWR ;DO THE WRITE JMP INTEN ;GO RESTORE INTS ; ; DEBLOCKING DISK READ. NOTE THAT ON DEBLOCKING READ ; AND WRITE, THE CALLER SECTOR NUMBERS RANGE FROM 1-XX ; DECIMAL. THIS IS FOR COMPATIBILITY WITH 8" FORMATS ; THAT ARE 1-ORIGIN. WE ADJUST FOR THIS THROUGHOUT THE ; DEBLOCKING ROUTINES BY SUBTRACTING 1 FROM SEKSEC ; BEFORE WE USE IT. VARIABLES TO CONTROL DEBLOCKING ; ARE SET UP FOR US WHEN DISK IS SELECTED. ; DISKRD: CALL INTDI ;DISABLE INTERRUPTS CALL DSKRD ;DO THE I/O ; INTEN: PUSH PSW ;SAVE DISK I/O ERROR CODE LHLD ENTPSW ;GET ENTRY PSW TO TEST PUSH H POP PSW JPO INTEN5 ;PASS IF INTS OFF WHEN ENTERED EI ; ELSE RE-ENABLE INTS ; INTEN5: POP PSW ;RESTORE DISK I/O ERROR CODE RET ; THEN RETURN TO CALLER ; INTDI: MVI A,7FH ;CHECK FOR RUNNING ON INR A ; 8080 OR Z80 JPO NOTZ80 ;CAN'T GET INT STATE ON 8080 DW LDAI ;GET INT STATE INTO PUSH PSW ; PARITY FLAG, THEN POP H ; SAVE PSW IN MEMORY SHLD ENTPSW ; NOTZ80: DI ;ALLOW NO INTERRUPTS RET ; DURING THE I/O XFER ; DSKRD: LDA SECSHF ;SEE IF DEBLOCKING ORA A JZ JADERD ;MUST BE JADE IF NOT LDA SEKDSK ;IF SO, SEE IF JADE ANYWAY CPI 2 JC NOTJR ;PASS IF NOT, N* READ LDA SEKTRK ;IF SO, IS TRACK 0 OR 1? CPI 2 JC JADERD ;NORMAL SECTOR SIZE IF SO ; NOTJR: MVI A,WRUAL STA WRTYPE ;TREAT AS UNALLOC STA READOP ;READ OPERATION JMP ALLOC ;GO END UNALLOC SECTORS ; AND FORCE READ ; DSKWR: LDA SECSHF ;SEE IF DEBLOCKING ORA A JZ JADEWR ;MUST BE JADE IF NOT LDA SEKDSK ;IF SO, SEE IF JADE ANYWAY CPI 2 JC NOTJW ;PASS IF NOT, N* WRITE LDA SEKTRK ;IF SO, IS TRACK 0 OR 1? CPI 2 JC JADEWR ;NORMAL SECTOR SIZE IF SO ; NOTJW: XRA A STA READOP ;NOT A READ OPERATION MOV A,C ;WRITE TYPE IN C STA WRTYPE CPI WRUAL ;WRITE UNALLOCATED? JNZ CHKUNA ;CHECK FOR UNALLOC ; ; WRITE TO UNALLOCATED, SET PARAMETERS ; LDA UNAVAL ;NEXT UNALLOC RECS STA UNACNT LHLD SEKDSK ;PICK UP SEKDSK AND SEKTRK SHLD UNADSK ;UNADSK=SEKDSK, UNATRK=SEKTRK LDA SEKSEC STA UNASEC ;UNASEC = SEKSEC ; ; CHECK FOR WRITE TO UNALLOCATED SECTOR ; CHKUNA: LDA UNACNT ;ANY UNALLOC REMAIN? ORA A JZ ALLOC ;SKIP IF NOT ; ; MORE UNALLOCATED RECORDS REMAIN ; DCR A ;UNACNT = UNACNT - 1 STA UNACNT LDA SEKDSK ;SAME DISK? LXI H,UNADSK CMP M ;SEKDSK = UNADSK? JNZ ALLOC ;SKIP IF NOT ; ; DISKS ARE THE SAME, CHECK TRACKS ; LDA SEKTRK LXI H,UNATRK CMP M ;SEKTRK = UNATRK? JNZ ALLOC ;SKIP IF NOT ; ; TRACKS ARE THE SAME, CHECK SECTORS ; LDA SEKSEC LXI H,UNASEC CMP M ;SEKSEC = UNASEC? JNZ ALLOC ;SKIP IF NOT ; ; MATCH, MOVE TO NEXT SECTOR FOR FUTURE REF ; INR M ;UNASEC = UNASEC+1 LDA CPMSPT ;CHECK FOR END OF TRACK CMP M JNC NOOVF ;SKIP IF STILL ON TRACK ; ; OVERFLOW TO NEXT TRACK ; MVI M,1 ;UNASEC = 1 LXI H,UNATRK INR M ;UNATRK = UNATRK+1 ; ; MATCH FOUND, MARK AS UNNECESSARY READ ; NOOVF: XRA A STA RSFLAG ;RSFLAG = 0 JMP RWOPER ;GO DO WRITE ; ; NOT AN UNALLOCATED RECORD, REQUIRES PRE-READ ; ALLOC: XRA A STA UNACNT ;UNACNT = 0 INR A STA RSFLAG ;RSFLAG = 1 ; ; COMMON CODE FOR READ AND WRITE FOLLOWS ; RWOPER: XRA A STA ERFLAG ;NO ERRORS (YET) LDA SECSHF ;GET SECTOR SHIFT COUNT MOV B,A ; IN COUNT REG LDA SEKSEC ;COMPUTE HOST SECTOR DCR A ;ADJUST FOR 1-ORIGIN ; RWOPSH: ORA A ;CARRY = 0 RAR ;SHIFT RIGHT DCR B ;COUNT OFF A SHIFT JNZ RWOPSH ;LOOP IF MORE STA SEKHST ;HOST SECTOR TO SEEK ; ; ACTIVE HOST SECTOR? ; LXI H,HSTACT ;HOST ACTIVE FLAG MOV A,M MVI M,1 ;ALWAYS BECOMES 1 ORA A ;WAS IT ALREADY? JZ FILHST ;FILL HOST IF NOT ; ; HOST BUFFER ACTIVE, SAME AS SEEK BUFFER? ; LDA SEKDSK LXI H,HSTDSK ;SAME DISK? CMP M ;SEKDSK = HSTDSK? JNZ NOMATCH ; ; SAME DISK, CHECK TRACK ; LDA SEKTRK LXI H,HSTTRK CMP M ;SEKTRK = HSTTRK? JNZ NOMATCH ; ; SAME DISK AND TRACK, CHECK SECTOR ; LDA SEKHST LXI H,HSTSEC CMP M ;SEKHST = HSTSEC? JZ MATCH ;SKIP IF MATCH ; ; MUST FLUSH HOST BUFFER FOR NEW SECTOR ; NOMATCH: LDA HSTWRT ;HOST WRITTEN? ORA A CNZ WRITEHST ;CLEAR HOST BUFFER LDA ERFLAG ;CHECK FOR ERROR ORA A RNZ ;RETURN ERROR IF SO ; FILHST: LHLD SEKDSK ;MAY HAVE TO FILL HOST BUFFER SHLD HSTDSK ;HSTDSK=SEKDSK,HSTTRK=SEKTRK LDA SEKHST STA HSTSEC XRA A STA HSTWRT ;NO PENDING WRITE LDA RSFLAG ;NEED TO READ? ORA A JZ MATCH ;NO IF FLAG ZERO LDA HSTDSK ;SEE WHICH DISK CPI 2 ;IF C: OR D:, JADE JNC NOTNSR ;BRANCH IF JADE CALL READHST ;IF A: OR B:, READ N* LDA ERFLAG ;CHECK FOR ERRORS ORA A RNZ ;NO MORE IF SO CALL GNSDEN ;CHECK DENSITY LXI H,NSDENS CMP M ;IS DENSITY RIGHT? JZ MATCH ;OK IF SO LXI H,SDEMSG ;ERROR IF NOT CALL DSKERR ;REPORT IT MVI A,5 STA ERFLAG RET ;RET NZ ACC TO CALLER ; NOTNSR: CALL JHREAD ;READ JADE DISK LDA ERFLAG ;READ ERROR? ORA A RNZ ;LEAVE BUF ALONE IF SO ; ; COPY DATA TO OR FROM BUFFER ; MATCH: LDA SECMSK ;GET MASK MOV H,A ; INTO TEMP REG LDA SEKSEC ;MASK SECTOR BUFFER NUMBER DCR A ;ADJUST FOR 1-ORIGIN ANA H ;LEAST SIGNIF BITS RAR ;GET VALUE SHIFTED MOV H,A ; LEFT 7 IN HL MVI A,0 RAR MOV L,A ; ; HL CONTAINS RELATIVE HOST BUFFER ADDRESS ; LXI D,HSTBUF ;ASSUME USING N* BUFFER LDA HSTDSK ;CHECK DISK SELECTED CPI 2 JC NJBUF ;RIGHT IF NOT JADE DISK MVI A,DCMB1 ;USING JADE BUFFER, OUT DPORT ; SWITCH BANK ONE IN XCHG ;PUT OFFSET IN DE NOW LHLD DADDR ;PT HL AT BUFFER ; NJBUF: DAD D ;HL = HOST ADDRESS XCHG ;NOW IN DE LHLD DMAADR ;GET/PUT CP/M DATA MVI C,SECSZ ;LENGTH OF MOVE LDA READOP ;WHICH WAY? ORA A JNZ RWMOVE ;SKIP IF READ ; ; WRITE OPERATION, MARK AND SWITCH DIRECTION ; INR A ;ACC KNOWN ZERO ABOVE STA HSTWRT ;HSTWRT = 1 XCHG ;SOURCE/DEST SWAP ; RWMOVE: LDAX D ;SOURCE CHARACTER INX D MOV M,A ;TO DEST INX H DCR C ;LOOP 128 TIMES JNZ RWMOVE LDA HSTDSK ;IS THIS JADE DISK? CPI 2 CNC DSKOUT ;FIX MEM STATUS IF SO ; ; DATA HAS BEEN MOVED TO/FROM HOST BUFFER ; LDA WRTYPE ;WRITE TYPE CPI WRDIR ;TO DIRECTORY? MVI A,0 ;NO ERROR AT THIS PT RNZ ;RET IF NOT DIR WRITE ; ; CLEAR HOST BUFFER FOR DIRECTORY WRITE ; CALL WRITEHST LDA ERFLAG RET ; ; WRITEHST PERFORMS THE PHYSICAL WRITE TO THE ; NORTH STAR DISK. ON ENTRY, DRIVE IS IN HSTDSK, ; TRACK IS IN HSTTRK, SECTOR IS IN HSTSEC. ON ; EXIT, ERROR FLAG IS IN ERFLAG (ZERO IF NONE). ; ; IT IS ASSUMED INTERRUPTS ARE DISABLED AT ; THIS POINT. IF NOT, I/O MAY BE DISRUPTED. ; WRITEHST: ;WRITE HOST XRA A ;BUFFER WILL BE CLEARED STA HSTWRT LDA HSTDSK ;SEE IF N* DISK SELECTED CPI 2 JNC JHWRIT ;IF NOT, DO JADE WRITE CALL GNSDEN ;GET DENSITY BASED ON ID STA NSCNT ;SET WRITE BYTE COUNT CALL SETUP ;SELECT DRIVE AND ; SEEK TO TRACK LDA ERFLAG ;CHECK FOR ERRORS ORA A RNZ ;INDEX PULSE NOT FOUND IS FATAL CALL POSEC ;POSITION TO SECTOR JNZ SNFERR ;OUT ON ERROR LDA CCMND+20H ;GET B-STATUS ANI 02H ;IS DISK WRITE-PROTECTED? MVI A,6 ;ASSUME SO, ERROR CODE 6 STA ERFLAG JNZ WPERR ;WRITE ALWAYS FAILS IF SO LHLD HSTADR ;GET ADDRESS TO WRITE FROM MVI B,31 ;ASSUME DOUBLE DENSITY MVI E,2 LDA NSCNT ;CHECK MOV C,A ;SAVE COUNT FOR LATER ORA A JZ WRIT5 ;GO ON IF COUNTS RIGHT MVI B,15 ;CORRECT FOR SINGLE DENS DCR E ; WRIT5: LDA CCMND+16H ;INITIATE SECTOR WRITE ; WRIT10: LDA CCMND+15H ;KEEP MOTORS RUNNING, GET A-STAT ANI 08H ;MUST LOOP UNTIL 96 USEC WINDOW JNZ WRIT10 ; PAST, SO LOOP WHILE WI TRUE ; ; NOW WRITE THE 31 OR 15 BYTES OF LEADING ZEROES ON THE SECTOR ; WRIT15: LDA WDATA+00H ;WRITE A BYTE OF ZEROES NOP ;KILL TIME MVI D,WDATA SHR 8 ;KILL TIME BY LOADING D FOR LATER DCR B ;COUNT OFF A ZERO BYTE JNZ WRIT15 ;DO THEM ALL ; ; FOLLOW WITH ONE OR TWO SYNCH BYTES ; WRIT17: LDA WDATA+0FBH ;WRITE A SYNCH BYTE DCR E ;CHECK FOR 2ND NEEDED JNZ WRIT17 ;GO BACK IF SO ; ; NOW WRITE OUT 256 OR 512 BYTES. DO THIS TWO AT A ; TIME SO WE CAN USE A SINGLE PRECISION COUNTER. B IS ; ZERO FROM ABOVE TO INIT THE CRC BYTE. ; WRIT20: MOV A,M ;GET NEXT BYTE TO OUTPUT MOV E,A ; IN OUTPUT REG XRA B ;ADD INTO CHECKSUM RLC MOV B,A ;LEAVE CHECKSUM IN B LDAX D ;WRITE DATA BYTE TO DISK INX H ;BOP BUF PTR MOV A,M ;REPEAT FOR NEXT BYTE MOV E,A XRA B RLC MOV B,A LDAX D INX H DCR C ;COUNT OFF LAST PAIR OF BYTES JNZ WRIT20 ;GO BACK IF MORE TO WRITE MOV E,B ;IF NOT, TIME TO WRITE CHECKSUM INX B ;KILL TIME LDAX D ; THEN WRITE THE BYTE XRA A ;DONE, INDICATE NO ERROR STA ERFLAG RET ;RETURN TO CALLER ; WPERR: LXI H,WPEMSG ;WRITE PROTECT ERROR JMP DSKERR ;TELL OPERATOR ON WAY OUT ; ; GNSDEN IS CALLED BY N* I/O ROUTINES TO DETERMINE THE ; DENSITY OF THE CURRENT BLOCK BASED ON THE DENSITY OF ; THE ID BLOCK. THAT IS DETERMINED BY TAKING ADVANTAGE ; OF THE FACT THAT THE HOST DISK HAS NO TRANSLATE TABLE ; IF SINGLE DENSITY. ; GNSDEN: LXI H,D0DPH ;ASSUME A: LDA HSTDSK ;NOW CHECK ORA A JZ GNSDA ;GO ON IF SO LXI H,D1DPH ;B: IF NOT GNSDA: MOV A,M ;SEE IF GOT XLATE TABLE INX H ORA M ;IF NOT, SINGLE DENSITY MVI A,80H ;ASSUME SINGLE RZ ;DONE IF RIGHT XRA A ;CORRECT IF DOUBLE DENSITY RET ; ; DISK I/O ROUTINES FOR JADE DOUBLE D CONTROLLER ; ; JADE HOST WRITE FOR FLUSHING BIG SECTOR BUFFER. ; LIKE JADE HOST READ, THESE ROUTINES ARE CALLED BY ; SECTOR DEBLOCKING I/O ROUTINES TO HANDLE JADE DISKS ; WITH DATA SECTORS OF LARGER THAN 128 BYTES/SECTOR. ; JHWRIT: MVI B,DCWRS ;WRITE OP JMP JHOP ; ; JADE HOST READ FOR FILLING BIG SECTOR BUFFER ; JHREAD: MVI B,DCRDS ;READ OP ; JHOP: LDA HSTDSK ;SET UP DISK TO USE SUI 2 ;OFFSET FOR 2ND CTRLR STA BTDSK LDA HSTTRK ;THIS TRACK STA BTTRK LDA HSTSEC ;THIS SECTOR INR A ;ADJUST FOR 1-ORIGIN STA BTSEC MVI A,DCSIN ;SWITCH IN JADE MEM OUT DPORT MOV A,B ;SET READ OR WRITE STA BTCMD CALL DSKEXR ;DO THE OPERATION JZ DSKOK ;IF OK, GO THERE JMP DSKER ;IF ERROR, GO REPORT ; ; READ A JADE DISK SECTOR ROUTINE. LIKE JADE DISK ; WRITE, THIS ROUTINE IS CALLED TO DO A NORMAL, NON- ; DEBLOCKING SECTOR I/O TO A DISK WITH 128-BYTE SECTORS. ; JADERD: CALL JFLUSH ;FLUSH JADE BUF MAYBE RNZ ;OUT ON ERROR MVI A,DCSIN ;SWITCH DD INTO SYS OUT DPORT MVI A,DCRDS ;READ SECTOR COMMAND CALL DSKEX ;PERFORM OPERATION JNZ DSKER ;ERROR EXIT LHLD DMAADR ;LOAD USER BUF ADDRESS XCHG LHLD DADDR ;GET SECTOR BUF ADDR IN HL LXI B,SECSZ ;LOAD SECTOR SIZE MVI A,DCMB1 ;BUFFER IS IN BANK ONE OUT DPORT CALL BLOCK ;BLOCK MOVE ROUTINE JMP DSKOK ;NORMAL RETURN ; ; WRITE A JADE DISK SECTOR ; JADEWR: CALL JFLUSH ;FLUSH JADE BUF MAYBE RNZ ;OUT ON ERROR MVI A,DCMB1 ;SWITCH DD BANK ONE IN OUT DPORT LHLD DADDR ;LOAD BUFFER ADDR XCHG ;GET IT IN DE LHLD DMAADR ;LOAD USER BUF ADDRESS LXI B,SECSZ ;LOAD SECTOR SIZE CALL BLOCK ;BLOCK MOVE ROUTINE MVI A,DCMB0 ;DSKEX NEEDS BANK 0 OUT DPORT ; SO SWITCH IT IN MVI A,DCWRS ;LOAD WRITE SEC COMMAND CALL DSKEX ;CALL DISK EXEC JNZ DSKER ;IF ERROR, JUMP, ELSE ; FALL INTO DSKOK ; ; JADE DISK READ/WRITE/FORMAT EXITS ; DSKOK: XRA A ;ZERO PSW JMP DSKOUT ; DSKER: PUSH PSW ;SAVE ERROR CODE LXI H,JIOMSG ;PT TO LEADER CALL MSGOT ;OUTPUT MESSAGE POP PSW CALL HXBOT ;FORMAT AND OUTPUT HEX CODE LDA BTTRK ;IT HAPPENED ON THIS TRK STA HSTTRK LDA BTSEC ; AND THIS SECTOR STA ERSEC LDA BTDSK ; WITH THIS DISK ADI 2 ;OFFSET FOR C: CALL JAERR ;CALL COMMON CODE TO DO REST ; FMTER: XRA A ;SIGNAL ERROR TO CALLER DCR A ; WITH NON-ZERO STATUS ; DSKOUT: PUSH PSW ;SAVE RETURN STATUS PUSH H ; AND OTHER REGS PUSH D LHLD DADDR ;PT TO DD MEM LXI D,DDCBT ;CALC DCM CMD BUF ADDR DAD D MVI A,DCMB0 ;CALL FOR BANK ZERO OUT DPORT MVI M,DCIDL ;MAKE SURE DCM STAYS IDLE MVI A,DCSOT ;SWITCH OUT DD MEM OUT DPORT POP D ;RESTORE REGS NOW POP H POP PSW ;GET RETURN PSW RET ;RET ZERO OR NON-ZERO ; ; JFLUSH IS CALLED BY NORMAL JADE SECTOR ROUTINES TO ; MAKE SURE THE JADE BUFFER IS NOT IN USE WHEN A ; NORMAL READ, WRITE, OR FORMAT OPERATION NEEDS ; THE BUFFER. ; JFLUSH: XRA A ;NO ERROR YET STA ERFLAG LDA HSTDSK ;IF N* DISK, CPI 2 JC NOJFL ; NOT USING OUR BUFFER ; ; COME HERE FROM SELDSK TO CLEAR ALL PENDING HOST WRITES ; HFLUSH: LDA HSTWRT ;SEE IF BUFFER NEEDS FLUSH STA ERFLAG ;NO ERROR IF NOT ORA A CNZ WRITEHST ;FLUSH IF SO, IF JADE ; WILL USE BUFFER XRA A STA HSTACT ;TOSS READ CONTENTS STA UNACNT ;BUFFER LEAVING, NEED ; PRE-READ ON NEXT UNALLOC ; NOJFL: LDA ERFLAG ;SET UP ERROR STATUS ORA A RET ; ; FORMAT A JADE DISK TRACK ROUTINE ; FORMAT: LDA SEKDSK ;ONLY FORMAT JADE DISK CPI 2 JC FMTER ;ERROR IF ANY OTHER CALL JFLUSH ;FLUSH JADE BUF MAYBE RNZ ;OUT ON ERROR MVI A,DCMB1 ;SWITCH DD BANK ONE OUT DPORT ; INTO SYS LXI B,FMTSZ ;FORMAT PROG SIZE LXI D,DDFBF ;CALC FORMAT BUF ADDR LHLD DADDR DAD D XCHG ;GET IT IN DE LHLD DMAADR ;FORMAT PROGRAM ADDR CALL BLOCK ;BLOCK MOVE ROUTINE MVI A,DCMB0 ;RESELECT DD BANK 0 OUT DPORT MVI A,DCFMT ;LOAD FORMAT TRK CMND CALL DSKEX ;CALL DISK EXEC JMP DSKOK ;GO RETURN STATUS TO CALLER ; ; DOUBLE D EXECUTION SUBROUTINE ; DSKEX: STA BTCMD ;STORE DCM COMMAND LDA SEKDSK ;MAP DISK TO JADE DD # SUI 2 STA BTDSK ; AND STORE JADE DISK # LDA SEKTRK ;SET TRACK # STA BTTRK LDA SEKSEC ; AND SECTOR # STA BTSEC ; ; HERE ON RETRY AFTER DISK NOT READY ; DSKEXR: LXI B,7 ;# BYTES TO MOVE LXI D,DDCBT ;CALC DCM COMMAND BUF ADDR LHLD DADDR DAD D XCHG ;GET IT IN DE LXI H,BTCMD ;BIOS COMMAND BLOCK CALL BLOCK ;PERFORM BLOCK MOVE MVI A,DCINT ;INTERRUPT DD OUT DPORT XCHG ;EXCHANGE SRC/DST DSKWT: IN DPORT ;READ DD STATUS ANI DSHLT ;TEST HALT* FLAG JNZ DSKWT ;WAIT UNTIL DD HALTED MVI A,DCSIN ;SWITCH DD INTO SYS OUT DPORT MOV A,M ;GET DD STATUS BYTE STAX D ;SAVE IN MEM STA ERFLAG ;SET ERFLAG FOR JHOP ANA A ;TEST FOR ERRORS RP ;RETURN TO CALLER IF DISK READY CALL NOTRDY ;IF NOT READY, REPORT JMP DSKEXR ;IF CAME BACK, RETRY ; ; BLOCK - BLOCK MOVE (Z80 LDIR REGISTER USAGE) ; BLOCK: MOV A,M ;GET BYTE STAX D ;STORE IT INX H ;BOP PTRS INX D DCX B ;COUNT OFF BYTE MOV A,B ;CHECK FOR DONE ORA C JNZ BLOCK ;GO BACK IF NOT RET ; ; ; HERE TO REPORT DISK NOT READY. TELL CONSOLE WHICH ; DISK IT WAS, THEN SOLICIT REPLY. IF CTRL-C, WARM ; BOOT TO A:, ELSE RETURN TO CALLER. ; NOTRDY: LDA SEKDSK ;STUFF DISK ADI 'A' ; ; ENTER HERE TO REPORT LINE PRINTER NOT READY ; PNTRDY: STA NRD ; INTO MSG LXI H,NRDMSG CALL MSGOT ;REPORT TO OPERATOR CALL BCNSIN ;GET REPLY SUI 3 ;IS ANSWER CTRL-C? RNZ ;RETURN TO CALLER IF NOT STA DEFDSK ;SELECT A: IF SO, ; THEN WARM BOOT IT ; ; WARM BOOT ENTRY. LOAD CCP/BDOS AND INITIALIZE ; WARM: DI ;NO PROCESSOR INTS DURING BOOT LDA DEFDSK ;GET CURRENT DEFAULT DISK MOV B,A ;SAVE IT ANI 0F0H ;GET USER # MOV C,A ;SAVE THAT MOV A,B ANI 00FH ;ISOLATE DISK # CPI NDRVS ;IS IT LEGAL? JC WRMOK ;GO ON IF SO XRA A ;BACK TO DRIVE ZERO IF NOT MOV C,A ;USER # PROB BAD TOO WRMOK: ORA C ;COMBINE WITH USER # STA DFIMG ;PUT TO BASE PAGE IMAGE LXI SP,HSTBUF+HSTSIZ ;SET SP TO SCRATCH RAM LXI H,CCP+3 ;CP/M WARM START ADDR PUSH H ; ; MERGE HERE FROM COLD BOOT ; CPMLD: LDA IOBYTE ;GET CURRENT IOBYTE STA IOIMG ; INTO BASE PAGE IMAGE LXI B,8 ;MOVE ZERO PAGE STUFF LXI D,0 ; DOWN TO ZERO LXI H,BSIMG CALL BLOCK LXI H,05959H ;FORCE HOME OF N* DRIVES SHLD NSTRK ; A, B MOV A,H ;MAKE SURE NEW DISK SELECTED STA CURDSK XRA A ;DRIVE ZERO VALUE STA HSTDSK STA HSTACT ;HOST BUFFER INACTIVE STA HSTWRT ;NO HOST WRITE PENDING STA UNACNT ;CLEAR UNALLOC COUNT LXI H,CCP ;CP/M CCP ADDRESS SHLD HSTADR ;READ INTO THERE INR A ;GET 1 IN ACC STA HSTTRK ;READ FROM TRACK ONE CALL WSETUP ;KICK MOTORS AND SEEK TRK XRA A ;SECTOR ZERO ; ; READ ALL TEN SECTORS FROM TRACK ONE THEN ONE SECTOR ; FROM TRACK ZERO (SECTOR 3). ; WREAD: STA HSTSEC ;SET CURRENT SECTOR TO READ CALL WRMRD ;READ SECTOR AND CHK STATUS LXI D,HSTSIZ ;SECTOR SIZE LHLD HSTADR ;CALC NEW ADDRESS DAD D SHLD HSTADR LDA HSTSEC ;BOP SECTOR # INR A CPI 10 ;OFF END OF TRACK ONE? JNZ WREAD ;GO GET NEXT SECTOR IF NOT XRA A ;TO TRACK ZERO, STA HSTTRK CALL WSETUP MVI A,3 ; SECTOR 3 IF SO STA HSTSEC CALL WRMRD ;READ THE LAST SECTOR LXI H,HSTBUF ;NOW READ AND WRITE SHLD HSTADR ; USING DEBLOCK BUFFER LDA DFIMG ;RETRIEVE LAST USED DRIVE MOV C,A ; FOR BDOS RET ; THEN GO TO CP/M ; WSETUP: CALL SETUP ;KICK MOTORS AND SEEK TRK JMP WECHK ; WRMRD: CALL READNS ;READ HOST SECTOR WECHK: LDA ERFLAG ;CHECK FOR ERROR ORA A RZ ;RETURN IF NONE LXI H,MSGLE ;GET ERROR MESSAGE CALL MSGOT ;TYPE IT HLT ; THEN GIVE UP ; ; MSGOT DISPLAYS STRING OF CHARS PT'ED AT BY HL ON ; CONSOLE, UNTIL CHAR WITH PARITY BIT SET IS OUTPUT. ; MSGOT: PUSH PSW ;SAVE CALLER FLAGS MSGL: MOV C,M ;LOAD CHAR PUSH H ;SAVE HL CALL BCNSOT ;OUTPUT CHAR IN C POP H ;RESTORE HL MOV A,M ;GET CHAR BACK TO LOOK AT INX H ;LEAVE PTR ON NEXT CHAR RAL ;IS HIGH BIT ON? JNC MSGL ;GO BACK IF NOT POP PSW ; ELSE RESTORE FLAGS RET ; AND RETURN ; ; DISK ERROR MESSAGES ; WPEMSG: DB 'Protec','t'+80H SDEMSG: DB 'Densit','y'+80H RERMSG: DB 'CR','C'+80H SYEMSG: DB 'Syn','c'+80H NIPMSG: DB 'Inde','x'+80H SNFMSG: DB 'Secto','r'+80H JIOMSG: DB CR,LF,'Jade DD',' '+80H ; ERRMSG: DB ' err ' ASCDSK: DB ' : trk',' '+80H SECMSG: DB ' sec',' '+80H NRDMSG: DB CR,LF,'Disk ' NRD: DB ' : not ready',' '+80H CRLF: DB CR,LF+80H ; MSGLE: DB CR,LF,'Boot er','r'+80H ; ; JADE ID LABEL DEFINITIONS ; JADEID: DB 'Jade DD ' ;ID LABEL IDSZE EQU $-JADEID ;LABEL SIZE ; ; NORTH STAR DD SECTOR TRANSLATE TABLE. AFTER ; DEBLOCKING, WORKS OUT TO SKEW FACTOR OF 5. ; TRAN5D: DB 01,02,03,04 DB 21,22,23,24 DB 05,06,07,08 DB 25,26,27,28 DB 09,10,11,12 DB 29,30,31,32 DB 13,14,15,16 DB 33,34,35,36 DB 17,18,19,20 DB 37,38,39,40 ; ; NORTH STAR DD DISK PARAMETER BLOCK. THIS IS OUR ; NORMAL DPB, AND IS COMPATIBLE WITH LIFEBOAT CP/M 1.4 ; DISKS. ; DPBNSD: DW 40 ;SECTORS PER TRACK DB 3 ;BLOCK SHIFT FACTOR DB 07H ;BLOCK MASK DB 0 ;EXM MASK DW 165-1 ;DISK SIZE - 1 DW 63 ;DIRECTORY MAX DB 11000000B ;ALLOC 0 DB 0 ;ALLOC 1 DW 16 ;CHECK SIZE DW 2 ;TRACK OFFSET ; ; THE NEXT BYTE IS ONE WE HAVE ADDED SPECIFICALLY ; FOR THIS BIOS TO CONVENIENCE OUR DEBLOCKING ROUTINES. ; IT IS A SECTOR SHIFT MASK, AND EACH DISK PARAMETER ; BLOCK HAS ONE IN THIS POSITION. THE ALLOWABLE VALUES ; ARE AS FOLLOWS: ; ; 1000B ;1024 BYTES/SECTOR ; 0100B ; 512 BYTES/SECTOR ; 0010B ; 256 BYTES/SECTOR ; 0000B ; 128 BYTES/SECTOR ; ; FOR THE NORTH STAR DD DISKS, 512 IS HARD-CODED. FOR ; THE NORTH STAR SD DISKS, 256 IS HARD-CODED. FOR ; JADE DISKS, THE VALUE IS READ IN FROM THE ID SECTOR. ; IF THE ID LOOKS BOGUS, THE STANDARD 8" DPB IS USED, ; WHICH HAS 128 CANNED-IN. ; DB 0100B ;512 BYTES/SECTOR ; ; NORTH STAR DD DISK PARAMETER BLOCK, SET UP ; FOR COMPATIBILITY WITH LIFEBOAT CP/M 2.X ; FORMAT DISKS. ; DPBNSL: DW 40 ;SECTORS PER TRACK DB 4 ;BLOCK SHIFT FACTOR DB 0FH ;BLOCK MASK DB 1 ;EXM MASK DW 82-1 ;DISK SIZE - 1 DW 63 ;DIRECTORY MAX DB 10000000B ;ALLOC 0 DB 0 ;ALLOC 1 DW 16 ;CHECK SIZE DW 2 ;TRACK OFFSET DB 0100B ;512 BYTES/SECTOR ; ; THERE IS NO NORTH STAR SINGLE DENSITY TRANSLATE ; TABLE. ; TRAN5S: EQU 0 ;TELL SECTRN TO JUST ADD 1 ; ; NORTH STAR SD DISK PARAMETER BLOCK ; DPBNSS: DW 20 ;SECTORS PER TRACK DB 3 ;BLOCK SHIFT FACTOR DB 07H ;BLOCK MASK DB 0 ;EXM MASK DW 80-1 ;DISK SIZE - 1 DW 63 ;DIRECTORY MAX DB 11000000B ;ALLOC 0 DB 0 ;ALLOC 1 DW 16 ;CHECK SIZE DW 3 ;TRACK OFFSET DB 0010B ;256 BYTES/SECTOR ; ; STANDARD 8" SECTOR TRANSLATE TABLE ; TRAN8: DB 1,7,13,19,25,5,11,17,23 DB 3,9,15,21,2,8,14,20,26 DB 6,12,18,24,4,10,16,22 ; ; STANDARD 8" DISK PARAMETER BLOCK ; DPB8: DW 26 DB 3 DB 7 DB 0 DW 242 DW 63 DB 0C0H DB 0 DW 16 DW 2 ; DB 0000B ;128 BYTES/SECTOR ; ; 1024 BYTES/SECTOR TRANSLATE TABLE ; TRN124: DB 1,2,3,4,5,6,7,8 DB 25,26,27,28,29,30,31,32 DB 49,50,51,52,53,54,55,56 DB 9,10,11,12,13,14,15,16 DB 33,34,35,36,37,38,39,40 DB 57,58,59,60,61,62,63,64 DB 17,18,19,20,21,22,23,24 DB 41,42,43,44,45,46,47,48 ; ; DRIVE PARAMETER HEADER AREA ; ; DRIVES A: AND B: ARE NORTH STAR 5" ; D0DPH: DW 0 ;SECTOR TRAN TBL SET BY SELDSK DW 0 ;SCRATCH DW 0 ;SCRATCH DW 0 ;SCRATCH DW DIRBF ;DIRECTORY BUFFER DW 0 ;DRIVE PARAM BLK SET BY SELDSK DW D0CHK ;DRIVE CHANGE BLK DW D0ALL ;DRIVE ALLOCATION ; D1DPH: DW 0,0,0,0,DIRBF,0,D1CHK,D1ALL ; ; DRIVES C: AND D: ARE JADE 8" SHUGART ; D2DPH: DW 0,0,0,0,DIRBF,D2DPB,D2CHK,D2ALL ; D3DPH: DW 0,0,0,0,DIRBF,D3DPB,D3CHK,D3ALL ; ; ZERO PAGE IMAGE -- BLOCK MOVED TO BASE PAGE ; BSIMG: JMP BIOS+03H ;WARM BOOT VECTOR IOIMG: DS 1 ;IOBYTE SET BY CPMLD DFIMG: DB 0 ;DEFAULT DISK -- ZERO ; AFTER COLD BOOT JMP BDOS+06H ;BDOS CALL VECTOR ; ; BIOS VARIABLE STORAGE ; SEKDSK: DB 0 ;DRIVE NUMBER SEKTRK: DS 1 ;TRACK NUMBER (MUST IMM FOLLOW ; SEKDSK FOR LHLD) SEKSEC: DS 1 ;SECTOR NUMBER SEKHST: DS 1 ;SEEK SHR SECSHF HSTACT: DS 1 ;HOST ACTIVE FLAG HSTWRT: DS 1 ;HOST WRITTEN FLAG DTPTR: DW 0 ;DRIVE TABLE PTR LOGFLG: DB 0 ;NON-ZERO IF SELDSK ; LOGGED ON JADE DISK ; UNACNT: DS 1 ;UNALLOC REC CNT UNADSK: DS 1 ;LAST UNALLOC DISK UNATRK: DS 1 ;LAST UNALLOC TRACK (MUST IMM ; FOLLOW UNADSK FOR LHLD) UNASEC: DS 1 ;LAST UNALLOC SECTOR ; ; DEBLOCKING INFO ABOUT THE CURRENTLY-SELECTED DISK ; UNAVAL: DS 1 ;# UNALLOC RECS/GROUP CPMSPT: DS 1 ;# CP/M SECTORS/TRACK SECSHF: DS 1 ;SECTOR SHIFT COUNT, ; LOG2 (# CP/M SECTORS/HOST BLOCK) SECMSK: DS 1 ;SECTOR MASK, # CP/M ; SECTORS/HOST BLOCK - 1 ; ; JADE DOUBLE D DCM PARAMETER BLOCK. ENTRIES ; MUST BE IN THIS ORDER TO MATCH DCM ROUTINE ; BLOCK. ; BTCMD: DB 0 ;DCM COMMAND BTDSK: DS 1 ;JADE DD DRIVE # BTTRK: DS 1 ;TRACK NUMBER BTSEC: DS 1 ;SECTOR NUMBER BTSP0: DB 0 ;SPARE BYTE 0 BTCHR: DB 0 ;LIST CHAR BTMOD: DB 0 ;MODE CONTROLS BTSTS: DB 0 ;COMMAND STATUS BTLAD: DW 0 ;LOAD ADDRESS BTLNG: DW 0 ;LOAD LENGTH ; ; JADE-ONLY VARIABLES ; DADDR: DW 0 ;DD MEMORY WINDOW ADDRESS ; ; RESERVE DRIVE PARAMETER BLOCKS FOR JADE DRIVES ; DPBSZ EQU 16 ;SIZE IS 16 BYTES D2DPB: DS DPBSZ ;RESERVE 16 BYTES/DISK C-D ; ; EVERYTHING FROM THIS POINT FOR THE NEXT ; 512 BYTES IS READ INTO MEMORY BY THE NORTH ; STAR BOOT PROM. OUR GOAL HERE IS TO GET ; A REASONABLE AMOUNT OF NORMAL BIOS CODE IN, ; BUT AT THE SAME TIME WE NEED THE PORTIONS ; THAT CAN READ THE DISK. ; ; IF THIS SECTION OVERLAYS PREVIOUS BIOS CODE ; OR DATA SPACE, THE PRECEDING BIOS MUST BE ; TRIMMED DOWN. ; PRVCHK EQU $ ;** MUST NOT EXCEED BOOTA ** ; ORG BIOS+(HSTSIZ*5) ;ORG TO COLD BOOT ; BOOTA: DB BOOTA SHR 8 ;TELL PROM LOAD ADDR ; ; THE NEXT NINE LOCATIONS ARE SKIPPED OVER ; BY THE BOOT PROM, WHICH READS THIS SECTOR ; THEN JUMPS TO BOOTA+0AH, SO PUT USEFUL VARIABLES ; HERE. NOTE THAT ALL VARIABLES IN THIS SECTOR ; WITH GENNED-IN VALUES ARE SET UP FOR COLD ; BOOT LOADING OF BIOS. ; HSTADR: DW BIOS ;READ BIOS INTO HERE TO START, ; BUT NORMALLY PTS TO HSTBUF HSTDSK: DB 0 ;HOST DISK NUMBER HSTTRK: DB 0 ;HOST TRACK NUMBER (MUST IMM ; FOLLOW HSTDSK FOR LHLD) HSTSEC: DB 5 ;HOST SECTOR NUMBER ERFLAG: DB 0 ;ERROR REPORTING CURDSK: DB 059H ;CURRENT ACTIVE DISK, ; GENNED-IN FORCE SELECT COLDB: DB 1 ;COLD BOOT WHEN NON-ZERO ERSEC: DS 1 ;SECTOR IN ERROR ; ; THIS IS THE SECTION OF THE COLD BOOT THAT ; READS IN THE REST OF BIOS. ON MORE CONVENTIONAL ; CP/M SYSTEMS, THIS WOULD BE THE BLOCK ZERO ; BOOT. READ BIOS INTO MEMORY, THEN JUMP TO ; IT TO FINISH COLD BOOTING. ; D3DPB: EQU $ ;*** OVERLAY N* COLD BOOT *** ; BOOT: DI ;ALLOW NO PROCESSOR INTERRUPTS LXI SP,HSTBUF+HSTSIZ ;SET SP TO SCRATCH RAM CALL SETUP ;GIVE MOTORS EXTRA KICK ; AND SEEK TRK 0 ; BOOTL: CALL READNS ;READ NEXT SECTOR OF BIOS ;WITH COLDB NON-ZERO, NO ; RETURN IF I/O ERROR LXI H,HSTSEC ;IF OK, BOP SEC NUM INR M LHLD HSTADR ; AND ADVANCE READ ADDR LXI D,HSTSIZ DAD D SHLD HSTADR MVI A,BOOTA SHR 8 ;SEE IF READ ENUFF CMP H JNZ BOOTL ;LOOP IF NOT JMP INIT ;GO START BIOS IF SO ; ; REMAINING NEEDED VARIABLES ; RTCNT: DS 1 ;ERROR RETRY COUNTER NSDENS: DS 1 ;DENSITY OF LAST NS DISK BLOCK ; READ, SD=128, DD=0 NSCNT: DB 00H ;COUNT AND DENSITY FLAG, SET ; BY WRITEHST TO MATCH ID SECTOR ; ; NORTH STAR CURRENT TRACK TABLE ; NSTRK: DB 059H ;NO CURRENT TRACK YET DB 059H ;EACH ENTRY CONTAINS ; DB 059H ; THE LAST TRACK POSITION ; DB 059H ; FOR THE UNIT 1-4 ; ; OTHER VARIABLES HERE BECAUSE THERE IS SPACE, CAN ; BE MOVED IF NECESSARY ; RSFLAG: DS 1 ;READ SECTOR FLAG READOP: DS 1 ;1 IF READ OPERATION WRTYPE: DS 1 ;WRITE OPERATION TYPE DMAADR: DS 2 ;LAST DMA ADDRESS ENTPSW: DW 0 ;ENTRY PSW AT DISK READ/WRITE ; ; READHST PERFORMS THE PHYSICAL READ FROM THE ; NORTH STAR DISK. ON ENTRY, DRIVE IS IN HSTDSK, ; TRACK IS IN HSTTRK, SECTOR IS IN HSTSEC. ON ; EXIT, ERROR FLAG IS IN ERFLAG (ZERO IF NONE). ; ; *** INTERRUPTS MUST BE DISABLED HERE *** ; READHST: MVI A,10 ;RETRY COUNT ON ERROR STA RTCNT ; READRT: CALL SETUP ;SELECT DRIVE AND ; SEEK TO TRACK LDA ERFLAG ;CHECK FOR ERRORS ORA A RNZ ;INDEX PULSE NOT FOUND IS FATAL ; ; HERE TO READ NORTH STAR WITHOUT TRACK SEEK FOR SPEED ; READNS: CALL POSEC ;POSITION TO OUR SECTOR JNZ SNFERR ;ERROR IF NOT FOUND MVI B,08CH ;COUNT FOR SYNC CHAR LOOP LXI D,CCMND+40H ;SET UP READ DATA REGS ; ; WAIT FOR RE SO WE CAN DO SECTOR READ, AND TEST ; THE DOUBLE DENSITY BIT. ; WAITRE: LDA CCMND+10H ;GET A-STATUS ANI 04H ;CHECK RE JZ WAITRE ;LOOP UNTIL RE TRUE XTHL ;KILL TIME TO GET INTO ZEROES XTHL XTHL XTHL XTHL XTHL XTHL XTHL LDA CCMND+10H ;GET A-STATUS AGAIN ANI 20H ;ARE WE READING DOUBLE DENS? RAL ;MAKE 80H IF NOT, ZERO IF SO RAL XRI 80H STA NSDENS ;SAVE DENSITY FOR CALLER ; ; NOW WAIT FOR SYNC CHAR DETECTED. ERROR IF WE HAVE TO ; WAIT TOO LONG. ; READ5: LDA CCMND+10H ;WAIT FOR SYNC CHAR DETECTED RRC ;CHECK BIT JC READ15 ;OUT IF GOT IT DCR B ;COUNT DOWN IF NOT JNZ READ5 ;GO BACK IF STILL OK MVI A,1 ;SYNC ERROR IF WAITED TOO LONG LXI H,SYEMSG ;SYNC ERROR MESSAGE ; RERR: STA ERFLAG ;STORE ERROR CODE LDA RTCNT ;COUNT OFF A RETRY DCR A STA RTCNT JNZ READRT ;IF COUNT LEFT, GO RETRY ; ; READ ERROR RETRIES FAILED. FLAG ERROR. ; JMP DSKERR ;TELL OPERATOR OF ERROR THEN OUT ; SNFERR: STA ERFLAG ;SET ERROR FLAG LXI H,SNFMSG JMP DSKERR ;REPORT ERROR ON WAY OUT ; ; READ THE DATA INTO HSTBUF ; READ15: LHLD HSTADR ;READ INTO HERE MVI B,0 ;INIT CHECKSUM BYTE LDA NSDENS ;GET # BYTES TO READ MOV C,A ;PUT READ COUNT IN C-REG ; READ20: LDAX D ;READ NEXT DATA BYTE MOV M,A ; AND PUT IT IN BUFFER XRA B ;ADD TO CHECKSUM RLC MOV B,A INX H ;BOP BUFFER PTR NOP ;KILL TIME LDAX D ;GET NEXT BYTE MOV M,A XRA B RLC MOV B,A INX H DCR C ;COUNT OFF LAST PAIR JNZ READ20 ;GO BACK IF MORE TO DO LDAX D ; ELSE READ CRC BYTE XRA B ; AND CHECK IT AGAINST OURS STA ERFLAG ;IF OK, ZERO EFLAG RZ ; AND RETURN TO CALLER MVI A,2 ; ELSE FLAG CHECKSUM ERROR LXI H,RERMSG ;MESSAGE IF NEEDED JMP RERR ;GO MAYBE RETRY ; ; SETUP SELECTS THE UNIT CORRESPONDING TO HSTDSK, ; THEN SEEKS TO THE TRACK SPECIFIED BY HSTTRK. ; WRITE PRECOMPENSATION IS SET IF REQUIRED AS ; WELL. ON ERROR, ERFLAG CONTAINS A NON-ZERO ; VALUE ON RETURN. ONLY UNITS 1 AND 2 ARE SUPPORTED ; TO SAVE SPACE. ; SETUP: LDA NSCNT ;GET DENSITY FLAG XRI 80H ;INVERT TO SET DD MOV C,A ;SAVE MASK LDA HSTDSK ;WANT THIS DISK INR A ;REMAP TO CONTROLLER MASK ; CPI 03H ;IF UNIT 1 OR 2, ; JC SET0 ; GOT CORRECT MASK ; RAL ;IF 3 OR 4, MUST MAP ; ANI 0CH ; TO 4 OR 8 RESPECTIVELY ;SET0: ORA C ;OR IN DENSITY MASK MOV C,A ;SAVE IN REG C CALL WAIT1S ;WAIT A SECTOR TIME, GET A-STAT ANI 10H ;ARE MOTORS ALREADY ON? LDA CCMND+15H ;GIVE THEM EXTRA KICK ANYWAY JNZ SET5 ;BRANCH IF SO MVI D,17H ;WAIT FOR MOTORS TO CALL SCWAIT ; COME UP TO SPEED IF NOT JMP SET10 ; THEN GO SELECT DRIVE ; SET5: LDA CURDSK ;IS THIS THE CURRENT DISK? CMP C JZ SET20 ;GO SEEK TO TRACK IF SO ; ; MUST SELECT NEW DISK ; SET10: MOV A,C STA CURDSK ;NEW CURRENT DISK MVI B,CORDER SHR 8 ;SET UP FOR SELECT LDAX B ; AND DO IT MVI D,2 ;WAIT TWO SECTOR TIMES CALL SCWAIT ; BEFORE LOOKING FOR INDEX MVI B,12 ;DON'T LOOK TOO LONG ; SET15: CALL WAIT1S ;WAIT A SECTOR TIME, GET A-STAT ANI 40H ;INDEX HOLE SEEN? JNZ SET20 ;CAN GO SEEK NOW IF SO DCR B ;COUNT DOWN IF NOT JNZ SET15 ; AND MAYBE GO LOOK AGAIN ; ; INDEX HOLE NOT FOUND. DISK IS PROBABLY NOT LOADED. ; MVI A,4 ;NO INDEX PULSE STA ERFLAG LXI H,NIPMSG CALL DSKERR ;TELL OPERATOR CALL NOTRDY ; AND CLAIM DISK NOT READY MVI A,059H ;FORCE RESELECT OF DISK STA CURDSK JMP SETUP ;TRY TO GET DISK AGAIN ; ; SEEK TO TRACK SPECIFIED BY HSTTRK NOW, AND SET ; WRITE PRECOMPENSATION IF APPROPRIATE. ; SET20: LDA HSTDSK ;GET NSTRK INDEX MOV C,A MVI B,0 LXI H,NSTRK ;PT AT TABLE DAD B ; THEN AT CORRECT ENTRY MOV A,M ;GET CURRENT TRACK PUSH H ;SAVE NSTRK PTR XRI 59H ;DISK EVER ACCESSED? CZ SEEK ;IF NOT, HOME DRIVE POP H ;RESTORE NSTRK PTR LDA HSTTRK ;SEEK TO THIS TRACK PUSH PSW ;SAVE OVER CALL CALL SEEK ;DO FINAL SEEK POP PSW ;GET HSTTRK BACK CPI 14H+1 ;ARE WE BEYOND TRACK 14H? JC SET25 ;GO ON IF NOT LDA NSCNT ;SEE IF DD OR SD ORA A JNZ SET25 ;NO PRECOMP IF SD LDA CURDSK ;IF SO, GET CURRENT MASK ORI 20H ; AND SET PRECOMP BIT MVI H,CORDER SHR 8 MOV L,A MOV A,M ;SET BIT IN CONTROLLER ; SET25: XRA A STA ERFLAG ;NO ERRORS RET ; ; POSEC IS CALLED TO POSITION TO HSTSEC WITHIN ; THE CURRENT TRACK. ERROR IS RETURNED BY NON-ZERO ; IF WE CAN'T FIND THE SECTOR AFTER 30 TRIES. ; POSEC: LDA HSTSEC ;GET TARGET SECTOR MOV C,A ;IN C STA ERSEC ;SET UP FOR POSSIBLE ERROR MVI B,30 ;LOOK AT THIS MANY SECTORS ; POSC5: CALL WAIT1S ;WAIT FOR NEXT SECTOR LDA CCMND+35H ;KICK MOTORS AND GET SECNUM ANI 0FH ;STRIP NON-SECTOR BITS SUB C ;IS THIS THE TARGET SECTOR? RZ ;OUT IF GOT IT DCR B ;COUNT OFF IF DIDN'T JNZ POSC5 ;GO BACK IF NOT GIVING UP INR B ;SET NON-ZERO IF NOT FOUND RET ; ; SEEK TO TRACK SPECIFIED BY ACC. CURRENT PTR INTO ; NSTRK TABLE IS GIVEN BY HL. IF TRACK SPECIFIED IS ; ZERO, SEEK WILL BE DONE UNTIL TRACK ZERO FLAG IS ; SEEN, REGARDLESS OF CURRENT POSITION, UNLESS ; WE ARE ALREADY THERE, SINCE NO SEEK IS DONE ; IF ALREADY AT THE DESIRED TRACK. ; SEEK: MOV B,A ;SAVE TARGET TRK # SUB M ;SEE HOW FAR AWAY WE ARE ; FROM TARGET TRACK MOV M,B ; BUT ALWAYS SET NEW TRACK RZ ;IF THERE, DONE LXI H,CORDER+30H ;ASSUME STEPPING IN MOV C,A ;SAVE STEP COUNT JP STEPIN ;BRANCH IF RIGHT CMA ;IF WRONG, NEGATE COUNT INR A MOV C,A ; THEN SAVE THAT LDA CCMND+20H ;GET B-STATUS ANI 01H ;ARE WE ON TRACK ZERO? RNZ ;MUST BE DONE IF SO MVI L,10H ;IF NOT, STEPPING OUT ; STEPIN: LDA CURDSK ;GET CURRENT UNIT MASK ORA L ;FORM FINAL CORDER VALUE MOV L,A MOV D,M ;SET THE STEP FLIP-FLOP ORI 10H MOV L,A MOV D,M ;SET IT AGAIN FOR SOME REASON XRI 10H MOV L,A MOV D,M ;NOW RESET THE STEP FLIP-FLOP ; ; WAIT WHILE HEAD STARTS MOVING ; MVI A,14H STEPW1: MVI D,38H STEPW2: DCR D ;KILL TIME JNZ STEPW2 DCR A JNZ STEPW1 ; ; THE STEP RATE DETERMINES HOW LONG TO WAIT. ONE SECTOR ; TIME IS 20 MSEC. A SLOW SHUGART SA400 MAY REQUIRE 40 ; MSEC, OR TWO SECTOR TIMES. MOST TYPICAL SA400'S WILL ; WORK WITH 20 MSEC, SO THAT'S WHAT WE'LL USE HERE. WE ; WILL CALL SCWAIT INSTEAD OF WAIT1S IN CASE WE NEED TO ; PATCH THIS VALUE. ; MVI D,1 ;NOW WAIT 20 MSEC ; MVI D,2 ;NOW WAIT 40 MSEC CALL SCWAIT LDA CCMND+25H ;GET B-STATUS AND KICK MOTORS ANI 01H ;ARE WE AT TRACK ZERO? JNZ WAIT1S ;DONE STEPPING IF SO DCR C ; ELSE COUNT OFF LAST TRACK JNZ STEPIN ;GO BACK IF MORE TO DO CALL WAIT1S ;WAIT ONE MORE SECTOR TIME ; TO MAKE SURE DONE STEPPING INR B ;SEEK TO TRACK ZERO? DCR B RNZ ;DONE IF NOT LDA CCMND+25H ;MUST HAVE TRK ZERO FLAG IF SO ANI 01H RNZ ;OK IF SO JMP STEPIN ;GO STEP MORE IF NOT ; ; SECTOR WAIT. ON ENTRY, # SECTORS TO WAIT IS IN D. ; ON EXIT, D=0 AND ACC=A-STATUS. CALL WAIT1S TO WAIT ; ONE SECTOR TIME. ; WAIT1S: MVI D,1 ;WAIT ONE SECTOR TIME ; SCWAIT: LDA CCMND+11H ;RESET SECTOR FLAG SCW5: LDA CCMND+10H ;GET A-STATUS ORA A ;CHECK SECTOR FLAG JP SCW5 ;WAIT FOR IT IF NOT UP LDA CCMND+11H ; ELSE RESET SECTOR FLAG DCR D ;COUNT DOWN WAIT COUNTER JNZ SCW5 ;GO BACK IF MORE TO DO RET ; ELSE RETURN A-STATUS IN ACC ; ; DISK ERRORS REPORTED HERE, BECAUSE STUPID BDOS GIVES ; NO USEFUL INFORMATION. ; DSKERR: LDA COLDB ;IN COLD BOOT? ORA A ;IF SO, CNSOT NOT IN MEM YET JNZ NSROM ;BACK TO N* BOOT ROM ON SAME PUSH H ;SAVE BODY ADDRESS LXI H,CRLF ;EJECT LINE CALL MSGOT POP H CALL MSGOT ;REPORT BODY LDA HSTDSK ;THIS DISK ; ; ENTER HERE AFTER JADE DD I/O ERROR ; JAERR: ADI 'A' STA ASCDSK LXI H,ERRMSG CALL MSGOT LDA HSTTRK ;THIS TRACK CALL HXBOT LXI H,SECMSG CALL MSGOT LDA ERSEC ;THIS SECTOR JMP HXBOT ;OFF TO HXBOT TO FINISH ; EBOOT EQU $-1 ;LAST USED COLD BOOT BYTE ; ; ; NORTH STAR HOST SECTOR BUFFER ; HSTBUF: DS HSTSIZ ;HOST BUFFER ; LAST EQU $-1 ;LAST USED BYTE IN MEM ; END