; TITLE 'MBIOS - N* DD CP/M 2.2 MODEM BIOS OF 05/02/82' ; ; These routines copyright (c) 1980, 1981, 1982 by Steve ; Bogolub, 2338 S. Scoville Ave., Berwyn, IL 60402. Elements ; of the public domain program "BYE", based on an original ; program written by Dave Jaffe and modified by many others, ; are incorporated. ; This software may be used freely for non-commercial ; purposes only, and may not be sold. ; ; ; THIS BIOS IS SPECIALIZED FOR REMOTE MODEM DIALUP ; USE. IF THE MEMORY LOCATION "MODEM" CONTAINS A ; VALUE OF ZERO WHEN THE COLD BOOT ROUTINE "INIT" ; IS ENTERED, A SPECIAL SET OF CONSOLE VECTORS ; IS PATCHED OVER THE GENNED-IN SET TO HANDLE THE ; PMMI MODEM INSTEAD OF THE NORMAL HORIZON DRIVERS. ; AFTER PATCHING THE VECTORS, THE ROUTINES WAIT FOR ; THE PHONE TO RING. UPON ANSWERING, THE BAUD RATE ; IS SENSED (110, 300, 450, 600, OR 710 BAUD) BY ; READING CARRIAGE RETURN OR LINE FEED CODES, THEN ; A PASSWORD IS REQUESTED. IF THE PASSWORD IS ; CORRECT, CCP AND BDOS ARE WARM BOOTED, AND THE ; MODEM TERMINAL CONTROLS THE MACHINE UNTIL CARRIER ; IS LOST OR CONTROL IS TRANSFERRED BY A PROGRAM ; TO THE MODEM COLD BOOT VECTOR, WHICH HANGS UP ; THE PHONE. AT THAT POINT, WE GO BACK TO WAITING ; FOR THE PHONE TO RING, AND START OVER AGAIN. ; ; IF MEMORY LOCATION "MODEM" CONTAINS A NON-ZERO ; VALUE, THE NORMAL HORIZON SERIAL DEVICES ARE ; USED FOR THE CONSOLE AND A SERIAL PRINTER. ; ; DISK: NORTH STAR MDS-AD2 DOUBLE DENSITY 5.25" ; FLOPPY DISK UNITS 1 AND 2 AS CP/M DRIVES ; A: AND B:, SINGLE-SIDED, DOUBLE DENSITY, ; 35 OR 40 TRACKS SELECTABLE AT ASSEMBLY ; TIME. UNITS 3 AND 4 ARE SUPPORTED AS ; CP/M DRIVES C: AND D:, DOUBLE-SIDED, ; DOUBLE DENSITY, 80 TRACKS. ; ; SERIAL I/O: HORIZON LEFT (LOW) SERIAL I/O PORT AS ; CP/M DEVICE CRT: ; ; HORIZON RIGHT (HIGH) SERIAL I/O PORT AS CP/M ; DEVICE TTY: WITHOUT DTR SUPPORT, AND REQUIRING ; DTR HIGH ON OUTPUT FOR LPT: ; ; IF "MODEM" IS ZERO, IOBYTE IS NOT SUPPORTED, ; AND CON:, LST:, RDR:, AND PCH: ALL USE THE ; PMMI MODEM ; ; 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. ; ; THERE ARE TWO POSSIBLE CONFIGURATIONS OF ; THE SYSTEM. THE NORMAL CONFIGURATION ASSUMES ; THAT MEMORY IS CONTIGUOUS, AND IS GENERATED ; IN A STRAIGHTFORWARD FASION WITH MOVCPM. ; FOR THIS CONFIGURATION, SET THE VARIABLE ; "NSHOLE" FALSE, THEN THIS BIOS WILL LOOK ; FOR CCP AND BDOS IN THEIR NORMAL LOCATIONS ; ON THE SYSTEM TRACKS: TRACK 1, SECTORS 0-9, ; AND TRACK ZERO, SECTOR 3 (NORTH STAR PHYSICAL ; SECTOR NUMBERS). A HIGHLY ABNORMAL CONFIGURATION ; IS POSSIBLE WHERE BDOS IS SPLIT INTO TWO ; PIECES, AND WRAPPED AROUND THE NORTH STAR ; CONTROLLER. THIS CONFIGURATION IS TOUGH TO ; ACHIEVE WITHOUT A HIGHLY INTIMATE KNOWLEDGE ; OF THE BDOS SO THAT THE SPLIT CAN BE DONE. ; IN THIS CASE, "NSHOLE" IS SET TO TRUE, AND ; ALLOWS FULL UTILIZATION OF ALL MEMORY IN ; A NORTH STAR MACHINE. ; ; THE BIOS WARM BOOT EXPECTS THE CCP AND ; THE LOW 2K OF BDOS TO RESIDE ON TRACK 1, ; SECTORS 0-7 INCLUSIVE, WITH THE REMAINING ; 1.5K OF BDOS ON TRACK 0, SECTORS 3, 1, AND ; 2, IN THAT ORDER. THIS PECULIAR LAYOUT IS ; DUE TO THE 1K "HOLE" IN THE CENTER OF OUR ; SPECIAL BDOS WHERE THE NORTH STAR CONTROLLER ; FITS. THE HOLE PUSHES THE LAST PART OF ; BDOS IN THE NSGEN MEMORY MAP UP INTO THE ; AREA USED ON THE STANDARD SYSTEM FOR THE ; JADE CONTROLLER CODE, WHICH IS UNUSED HERE. ; ; 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 ; FALSE EQU 0 TRUE EQU NOT FALSE ; NSHOLE EQU FALSE ;NO HOLE IN BDOS IS STANDARD ; ; DISK OPERATING SYSTEM ADDRESSES ; KBYTE EQU 1024 ;1K BYTE SIZE ; IF NSHOLE EXTRAK EQU KBYTE ;HOLE SIZE ENDIF IF NOT NSHOLE EXTRAK EQU 0 ;NO HOLE, ZERO SIZE ENDIF ; NKSYS EQU 56 ;SYS SIZE IN K BYTES CPMSZ EQU NKSYS*KBYTE ;TOP SYSTEM ADDRESS CPMBS EQU CPMSZ-(22*KBYTE)-EXTRAK ;CP/M BIAS VALUE CCP EQU CPMBS+3400H ;ADDRESS OF CCP BDOS EQU CPMBS+3C00H ;ADDRESS OF BDOS BIOS EQU CPMBS+4A00H+EXTRAK ;ADDRESS OF BIOS ;(EXTRA 1K IN BDOS IF ; NSHOLE TO SKIP N* CTRLR) BIOSR EQU 1F80H-BIOS+400H ;DDT LOAD OFFSET ; ;(LEAVES ROOM FOR JADE DCM OR BDOS) IOBYTE EQU 0003H ;IOBYTE ADDRESS DEFDSK EQU 0004H ;DEFAULT CCP DISK SECSZ EQU 128 ;BYTES PER SECTOR HSTSIZ EQU 512 ;BYTES PER N* SECTOR DSKSIZ EQU 165 ;35-TRACK DISK A: AND B: ;DSKSIZ EQU 190 ;40-TRACK DISK A: AND B: NDRVS EQU 4 ;# DRIVES IN SYSTEM ; ; STEP RATES. THESE ARE LOOP COUNTS, AND CORRESPOND ; TO 7.75 USEC PER COUNT AT 4 MHZ. A 40 MSEC VALUE ; WOULD BE -5161 (0EBD7H) FOR A SLOW SHUGART 400 DRIVE, ; AND A 20 MSEC VALUE WOULD BE -2580 (0F5ECH) FOR A ; TYPICAL NEW SHUGART 400 DRIVE. THE VALUES EMPIRICALLY ; DETERMINED FOR A WANGCO 82 AND MPI 92 ARE -758 (0FD0AH) ; AND -528 (0FDF0H) RESPECTIVELY. THE 4 MSEC FOR THE ; MPI SEEMS POSSIBLE, BUT THE 6 MSEC FOR THE WANGCO 82 ; SEEMS A BIT OVERSPEC. THE STEPPING ALGORITHM SEEMS ; TO HAVE A BIT OF SLOP IN IT, WHICH MAY HELP. ; STPSLW EQU -2580 ;SHUGART FOR TESTING ;STPSLW EQU -758 ;SLOW STEP CTR (WANGCO 82) STPFST EQU -528 ;FAST STEP CTR (MPI 92) ; ; 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 ; ; CONSOLE DEFINITIONS ; MOTHR EQU 000H ;HORIZON MOTHERBOARD BASE CRT EQU MOTHR+2 ;HORIZON LEFT SERIAL BASE TTY EQU MOTHR+4 ;HORIZON RIGHT SERIAL BASE ; ;PMMI modem port equates ; TPORT EQU 0E0H ;UART CONTROL/STATUS PORT DPORT EQU TPORT+1 ;DATA PORT RPORT EQU TPORT+2 ;RATE GEN/MODEM STATUS CPORT EQU TPORT+3 ;MODEM CONTROL ; ;Switch hook and modem commands, output to TPORT (port 0) ; P0BYE EQU 0 ;ON HOOK, OR DIALING BREAK P0ORIG EQU 1 ;OFF HOOK, ORIG. P0ANSW EQU 2 ;ANSWER PHONE P08BIT EQU 0CH ;8 DATA BITS P0NOPY EQU 10H ;NO PARITY P0EPS EQU 20H ;EVEN PARITY SELECT P0TSB EQU 40H ;2 STOP BITS P0EI EQU 80H ;ENABLE INTERRUPTS P0NORM EQU P08BIT+P0NOPY ;NORMAL 8 BITS, NO PARITY P0110 EQU P08BIT+P0NOPY+P0TSB ;SAME W/2 STOP BITS ; ;Modem status, input on RPORT (port 3) ; P2DTD EQU 1 ;DIAL TONE DETECT P2RDET EQU 2 ;RING DETECT P2CTS EQU 4 ;CTS (CARRIER DETECT) P2RXBRK EQU 8 ;RECEIVE BREAK P2CONN EQU 10H ;CONNECTED? (0=YES, 1=MODEM CHIP ; HUNG UP) P2TMPUL EQU 80H ;TIMER PULSES (40% UP CYCLE) ; ;Timer rate selection ; TRATE EQU 250 ;VALUE FOR .1 SEC ; ;PMMI modem status masks ; P0TBMT EQU 1 ;XMIT BUFF EMPTY P0DAV EQU 2 ;DATA AVAILABLE P0TEOC EQU 4 ;TEST END OF CHAR P0RPE EQU 8 ;REC'D PARITY ERR P0ORUN EQU 10H ;OVERRUN P0FERR EQU 20H ;FRAMING ERROR ; ;Baud rate divisors ; B110 EQU 142 ;110 BAUD B300 EQU 52 ;300 BAUD B450 EQU 35 ;450 BAUD B600 EQU 26 ;600 BAUD B710 EQU 22 ;710 BAUD ; CR EQU 0DH ;ASCII CARRIAGE RETURN LF EQU 0AH ;ASCII LINE FEED SPACE EQU 20H ;ASCII SPACE ; ; NORMAL BIOS JUMP VECTOR TABLE, IF NOT USING MODEM ; ORG BIOS ;START OF BIOS CODE ; BTABLE: JMP NSROM ;COLD BOOT FROM ROM JMP WARM ;RELOAD CCP/BDOS ; ; USE THE VECTORS TO THE CONSOLE ROUTINES FOR ; INTERNAL CONSOLE I/O INSTEAD OF CALLING THE ; ROUTINES DIRECTLY TO ALLOW USE OF BIOS ; MESSAGES WITH "BYE" AND OTHER VECTOR-PATCHING ; PROGRAMS. ; BCNSCK: JMP CNSCK ;GET CONSOLE STATUS BCNSIN: JMP CNSIN ;CONSOLE INPUT BCNSOT: JMP CNSOT ;CONSOLE OUTPUT BLIST: JMP LIST ;PRINTER OUTPUT ; JMP PUNCH ;PUNCH OUTPUT JMP READER ;READER INPUT ; ; VECTORS BEYOND THIS POINT ARE COMMON TO MODEM ; AND NON-MODEM SYSTEMS ; 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 ;LIST STATUS JMP SECTRN ;TRANSLATE SECTOR ; ; SET THE FOLLOWING BYTE TO ZERO BEFORE GENNING THE ; SYSTEM TO DISK TO HAVE THE MODEM CONTROL THE BIOS ; CONSOLE AND LIST DEVICES, OTHERWISE OPERATION IS ; FOR A FAIRLY STANDARD NORTH STAR HORIZON. ; MODEM: DB 0FFH ;NORMAL LOCAL USE ;MODEM: DB 000H ;MODEM USE ; ; FOLLOWING BYTE IS NON-ZERO IF MODEM IN USE AND ; TALKING TO PHONE, ZERO IF PHONE HUNG UP OR NOT ; TALKING TO PHONE. USED BY MSGOT TO DECIDE WHAT ; ROUTINE TO CALL TO OUTPUT TO CONSOLE. ; HUNGUP: DS 1 ; ; ACCESS PASSWORD WHEN RUNNING WITH MODEM (ENDS ; WITH CARRIAGE RETURN) ; PASSWD: DB 'PASSWORD' ;THE PASSWORD ITSELF DB CR ;END OF PASSWORD ; ;Allow room for bigger password to be patched in ; DB 0,0,0,0,0,0,0,0,0,0 ; ; SIGN-ON MSG TYPED AFTER LOCAL OR REMOTE USE MSG ; SIGNON: DB 'North Star ' DB (NKSYS/10)+'0',(NKSYS MOD 10)+'0' DB 'K CP/M 2.2 of 05/02/82',CR,LF+80H ; ; MODEM VECTOR TABLE TO PATCH OVER NORMAL TABLE IF ; "MODEM" CONTAINS ZERO AT INIT TIME ; MTABLE: JMP HANGUP ;HANG UP PHONE IF ; COLD BOOT CALLED JMP WARM ;RELOAD CCP/BDOS JMP MSTAT ;GET CONSOLE STATUS JMP MINPUT ;CONSOLE INPUT JMP MOUTPUT ;CONSOLE OUTPUT JMP MOUTPUT ;PRINTER OUTPUT JMP MOUTPUT ;PUNCH OUTPUT JMP MINPUT ;READER INPUT ; MTLEN: EQU $-MTABLE ;# BYTES TO MOVE ; ; THE INIT ROUTINE IS ACTUALLY A CONTINUATION OF THE ; COLD BOOT BEGUN DOWN AT "BOOT". ; INIT: MVI A,81H ;INIT THE IOBYTE STA IOBYTE ; FOR CON:=CRT:, ; AND LST:=LPT: LXI SP,HSTBUF+HSTSIZ ;SET SP TO SCRATCH AREA XRA A ;INIT HORIZON MOTHERBOARD OUT MOTHR+6 STA COLDB ;BIOS NOW IN MEMORY STA DFIMG ;COLD BOOT A:, USER ZERO CALL IDELAY ;DELAY FOR 8251'S MVI A,0AEH ;INIT SERIAL PORTS OUT CRT+1 ;OUTPUT DUMMY MODE TO INSURE OUT TTY+1 CALL IDELAY MVI A,040H ; CMD EXPECTED, THEN OUTPUT OUT CRT+1 ; RESET CMD OUT TTY+1 CALL IDELAY MVI A,04EH ;MODE: 1 STOP BIT, 16X CLK, OUT CRT+1 ; 8 DATA BITS, NO PARITY OUT TTY+1 CALL IDELAY MVI A,037H ;CMD: RTS, ER, RXEN, ; DTR, TXEN OUT CRT+1 OUT TTY+1 CALL IDELAY IN CRT ;FLUSH RECEIVER INPUTS IN TTY LDA MODEM ;SEE IF MODEM USE DESIRED ORA A JZ MINIT ;GO INIT FOR MODEM IF SO STA HUNGUP ;INDICATE NORMAL CONSOLE ; USE THRU BCNSOT IF NOT LXI H,CCP ;WHERE TO START CP/M PUSH H CALL ILPRT ;ANNOUNCE OURSELVES DB CR,LF,'Local',' '+80H LXI H,SIGNON ;ISSUE SIGNON CALL MSGOT 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. ; IDELAY: LXI B,600H IDEL5: DCX B MOV A,B ORA C JNZ IDEL5 RET ; ; IOBYTE IS SUPPORTED FOR CONSOLE AND LIST DEVICE IF ; NOT RUNNING WITH MODEM ; ; 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 CRTCHK ;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: (RDR: NOT SUPPORTED) DW CRTIN ;UC1: ; ; CONSOLE OUTPUT ; CNSOT: LDA IOBYTE CALL ROUTE ;DISPATCH TO OUTPUT ROUTINE DW TTYOT ;TTY: DW CRTOT ;CRT: DW LIST ;BAT: (LST: DEVICE) DW CRTOT ;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 TTYOT ;TTY: DW CRTOT ;CRT: DW LPTOT ;LPT: DW TTYOT ;UL1: ; ; 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 MOV A,C ; THEN OUTPUT CHAR FROM C OUT TTY RET ; ; 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 ; ; LIST CHAR OUT TO SERIAL PRINTER ON TTY: ; LPTOT: PUSH H ;SAVE HL PUSH B ;SAVE CHAR TO OUTPUT ; LOUT5: LXI H,0 ;LOAD UP FOR NICE LONG DELAY MVI B,20 ;APPROX 20 SECONDS AT 4 MHZ ; 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 MVI A,11H ;JAM PRINTER SELECT OUT TTY ; AFTER PUT ONLINE JMP LOUT5 ;NOW GO TRY AGAIN ; LOUT20: IN TTY+1 ;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 TTY ;OUTPUT THE CHAR MOV A,C ;RESTORE CHAR TO ACC POP H ;RESTORE HL RET ; AND RETURN ; ; ; THESE ARE THE MODEM ROUTINES. MINIT SETS UP THE MODEM ; VECTORS, THEN DROPS INTO HANGUP TO BEGIN NORMAL MODEM ; OPERATION. ; MINIT: LXI H,MTABLE ;MOVE VECTORS FROM HERE LXI D,BTABLE ; OVER NORMAL TABLE LXI B,MTLEN ;THIS MANY BYTES CALL BLOCK ;DO THE MOVE ; ;Clear DTR causing phone to hang up ; HANGUP: LXI SP,HSTBUF+HSTSIZ ;SET SP TO SCRATCH AREA LXI H,CCP ;INIT COLD CCP START ADDR PUSH H ;IT GOES ON STACK FOR CPMLD RET XRA A ;GET DISCONNECT VALUE OUT TPORT ;RESET ORIG/ANSW OUT CPORT ;TURN OFF DTR, DO BREAK STA DFIMG ;SET DEF DISK TO A:, USER ZERO STA HUNGUP ;PHONE HUNG UP CALL ILPRT DB CR,LF,'Phone hung up..','.'+80H ; ;Await ringing ; RINGWT: IN RPORT ;GET THE STATUS ANI P2RDET ;RINGING? JNZ RINGWT ;NO, WAIT ; ;The phone may be ringing. Wait .1 sec and look ;again to make sure it isn't just relay bounce ; CALL DELAY ;.1 SEC DELAY FOR DEBOUNCE IN RPORT ;GET STATUS ANI P2RDET ;STILL RINGING? JNZ RINGWT ;NO, IT WAS RELAY BOUNCE ; ;The phone is definitely ringing, now wait until ring ; is finished ; ENDRNG: CALL DELAY ;.1 SEC DELAY FOR DEBOUNCE IN RPORT ;GET STATUS ANI P2RDET ;STILL RINGING? JZ ENDRNG ;WAIT UNTIL RING FINISHED ; ;Setup modem ; ANSWER: MVI A,7FH ;TURN ON DTR OUT CPORT ;.. AND SET FILTER VALUE FOR 300 BAUD CALL DELAY ;GIVE TIME TO TURN ON MVI A,P0110+P0ANSW OUT TPORT ;ANSWER PHONE CALL DELAY ;GIVE TIME FOR ANSWER IN DPORT ;CLEAR MODEM PORT IN DPORT ;MAKE SURE ITS CLEAR MVI A,B110 ;SET DIVISOR OUT RPORT ;.. TO 110 BAUD RATE ; ;Output value allowing modem to hang up on loss of carrier ; MVI A,P0110 ;NORMAL MODE FOR 110 BAUD OUT TPORT CALL CHECK ;ABORT IF CARRIER LOST ; ;Now test input for baud rate ; CALL TSTBAUD ;SEE IF BAUD = 110 JZ WELCOME ;YES, EXIT ; MVI A,P0NORM ;SET FOR 1 STOP BIT, ETC. OUT TPORT MVI A,B300 ;SET DIVISOR OUT RPORT ;.. TO 300 RATE CALL TSTBAUD ;SEE IF BAUD = 300 JZ WELCOME ;YES, EXIT ; MVI A,B450 ;SET DIVISOR OUT RPORT ;.. TO 450 RATE MVI A,5FH ;SET FILTER VALUE OUT CPORT ;.. FOR > 300 CALL TSTBAUD ;SEE IF BAUD = 450 JZ WELCOME ;YES, EXIT ; MVI A,B600 ;SET DIVISOR OUT RPORT ;.. TO 600 RATE CALL TSTBAUD ;SEE IF BAUD = 600 JZ WELCOME ;YES, EXIT ; MVI A,B710 ;SET DIVISOR OUT RPORT ;.. TO 710 RATE CALL TSTBAUD ;SEE IF BAUD = 710 JZ WELCOME ;YES, EXIT JMP ANSWER ;TEST MORE - INVALID BAUD RATE ; ;Welcome to the system -- get the password ; WELCOME: MVI A,1 ;TALKING ON PHONE NOW STA HUNGUP CALL ILPRT ;ANNOUNCE OURSELVES DB CR,LF,'Remote',' '+80H LXI H,SIGNON CALL MSGOT ;TELL REST OF SIGNON MVI D,5 ;5 TRIES AT PASSWORD ; PASSINP: CALL ILPRT DB CR,LF,'Enter password:',' '+80H LXI H,PASSWD ;POINT TO PASSWORD MVI E,0 ;NO MISSED LETTERS IN DPORT ;CLEAR OUT GARBAGE ; PWMLP: CALL MINPUT ;GET A CHAR CPI 'U'-40H ;CTL-U? JZ PASSINP ;YES, RE-GET IT CPI 60H ;LOWER CASE? JC NOTLC ;NO, ANI 5FH ;MAKE UPPER CASE ALPHA ; NOTLC: CMP M ;MATCH PASSWORD? JZ PWMAT ;..YES MVI E,1 ;..NO, SHOW MISS CPI CR ;C/R? JNZ PWMLP ;..NO, WAIT FOR C/R ; ;Password didn't match ; PWNMAT: CALL ILPRT DB '++INCORRECT++',CR,LF+80H DCR D ;MORE TRIES? JNZ PASSINP ;YES JMP HANGUP ;NO, GO HANG UP ; ;Character matched in password ; PWMAT: INX H ;TO NEXT CHAR CPI CR ;END? JNZ PWMLP ;..NO, LOOP ; ;End of password. Any missed chars? ; MOV A,E ;GET FLAG ORA A JNZ PWNMAT ;NOT RIGHT ; ;Password correct ; CALL ILPRT DB CR,LF,'Entering CP/M . . .',' '+80H ;BOOT MSG JMP CPMLD ;LOAD AND START CCP/BDOS ; ;TSTBAUD attempts to read a LF or CR, returns with ;zero flag if the character read is one of these two. ; TSTBAUD: CALL MINPUT ;GET CHARACTER FROM MODEM CPI CR ;IF A CARRIAGE RETURN... RZ ;.. RETURN CPI LF ;IF A LINEFEED... RET ;RET ZERO FLAG, ELSE NOT ZERO ; ;Loss of connection test ; ;The PMMI modem automatically hangs up the phone after ;15 seconds of loss of carrier, providing you output to ;port 0 to allow it (we do). ; ;..so, this routine first checks if the modem has hung up, ;and if so, aborts out to HANGUP. If not, it checks for ;carrier and returns if carrier is on; otherwise waits for ;carrier while still testing for disconnect. ; ;It tests the PMMI "CTS" (clear to send) bit ;which is 0 when there is carrier ; CHECK: IN RPORT ;GET STATUS ANI P2CONN ;CONNECTED? JNZ HANGUP ;HUNG UP ; ;Still connected, check for carrier ; IN RPORT ;LOOK AT STATUS ANI P2CTS ;GET CARRIER DETECT BIT ; ;Loop until either connection lost, or carrier returns ; JNZ CHECK RET ; ;.1 sec delay routine for 4MHz clock Z80 ; DELAY: PUSH B ; LXI B,16667 ;4 MHZ TIMING CONSTANT ; LXI B,8334 ;2 MHZ TIMING CONSTANT ; DELAY1: DCX B MOV A,B ORA C JNZ DELAY1 POP B RET ; ;Modem status test routine ; MSTAT: CALL CHECK ;CHECK FOR CARRIER LOST CALL CNSCK ;GET LOCAL CONSOLE STATUS ORA A RNZ ;GO BACK IF GOT LOCAL CHAR IN TPORT ;GET STATUS ANI P0DAV ;DATA AVAILABLE? RZ ;RETURN IF NOT READY IN TPORT ;GET STATUS ANI 30H ;CHECK FRAMING AND OVERRUN BITS JZ MSTAT1 ;NO ERRORS...LEGIT CHARACTER IN DPORT ;SWALLOW CHARACTER (CLEARS PODAV) XRA A ;RETURN FALSE RET ; MSTAT1: XRA A ;SHOW READY DCR A RET ; ;Modem input function ; MINPUT: CALL CHECK ;CARRIER STILL ON? CALL MSTAT ;ANYTHING? ORA A JZ MINPUT ;LOOP TILL CHAR RCD CALL CNSCK ;CHAR AT LOCAL CONSOLE? ORA A JNZ CNSIN ;GO READ LOCAL CONSOLE IF SO IN DPORT ;GET DATA BYTE ANI 7FH ;DELETE PARITY JZ MINPUT ;IGNORE NULLS RET ; ;Modem output function ; MOUTPUT: CALL CHECK ;CARRIER STILL ON? IN TPORT ;GET MODEM STATUS ANI P0TBMT ;TRANSMIT BUFFER EMPTY? JZ MOUTPUT ;LOOP IF NOT READY MOV A,C ;GET CHAR ANI 7FH ;STRIP PARITY BIT OUT DPORT ;OUTPUT TO MODEM JMP CNSOT ;ECHO TO LOCAL CONSOLE ; ;Inline print routine ; ILPRT: XTHL ;SAVE HL, GET MSG PUSH B ;SAVE BC REGS CALL MSGOT ;OUTPUT STRING AT HL POP B ;RESTORE BC REGS XTHL ;RESTORE HL, RET ADDR RET ;RET PAST MSG ; ; 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 ; MOV A,B ;CHECK LOG REQUEST ANI 001H ;LOG ON BIT TEST JNZ NOLOG ;PASS IF NO REQUEST LDA SEKDSK ;NO LOGON IF C: OR D: CPI 2 JNC NOLOG CALL HFLUSH ;MAKE SURE HOST BUF JNZ LOGERR ; AVAILABLE, OUT ON ERR ; ; LOG ON NORTH STAR BY READING ID SECTOR, THEN DECIDE ; WHICH TABLE TO USE BASED ON SINGLE OR DOUBLE DENSITY. ; THE ACTUAL ID SECTOR INFO IS IGNORED. ; LDA SEKDSK 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,TRAN5D ;ASSUME DOUBLE DENSITY LXI D,DPBNSD ORA A ;CHECK JZ LOGNSD ;GO ON IF RIGHT LXI B,TRAN5S ;CORRECT TO SINGLE DENSITY LXI D,DPBNSS ; IF WRONG ; 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 ; ; 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 XRA A ;NO ERRORS RET ; SO RET ZERO ; LOGERR: LXI H,0 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 NOTRAN ;SPECIAL XLATE IF SO XCHG ;MAP OFF TABLE DAD B MOV L,M MVI H,0 RET ; NOTRAN: MOV H,B ;JUST BOP BY ONE MOV L,C ; FOR SINGLE DENSITY INX H RET ; ; THIS IS THE REST OF THE DISK I/O ERROR ROUTINE. ; IT IS HERE BECAUSE (1) IT CAN'T BE USED IN COLD BOOT, ; WHILE THE FIRST PART MUST BE, AND (2) IT SAVES ; A JUMP WHEN PLACED BEFORE HXBOT. ; DSKER2: PUSH H ;SAVE BODY ADDRESS LXI H,CRLF ;EJECT LINE CALL MSGOT POP H CALL MSGOT ;REPORT BODY LDA HSTDSK ;THIS DISK ADI 'A' STA ASCDSK LXI H,ERRMSG CALL MSGOT LDA HSTTRK ;THIS TRACK CALL HXBOT LXI H,SECMSG CALL MSGOT LDA ERSEC ;THIS SECTOR ;INTO HXBOT TO FINISH ; ; 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 ; ; 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: MVI A,WRUAL STA WRTYPE ;TREAT AS UNALLOC STA READOP ;READ OPERATION JMP ALLOC ;GO END UNALLOC SECTORS ; AND FORCE READ ; DSKWR: 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 LDA SEKDSK ;DISK TO SEEK STA UNADSK ;UNADSK = SEKDSK LDA SEKTRK STA UNATRK ;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: LDA SEKDSK ;MAY HAVE TO FILL ; HOST BUFFER STA HSTDSK LDA SEKTRK STA HSTTRK LDA SEKHST STA HSTSEC XRA A STA HSTWRT ;NO PENDING WRITE LDA RSFLAG ;NEED TO READ? ORA A JZ MATCH ;NO IF FLAG ZERO CALL READHST ;READ THE SECTOR 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 ; ; 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 ;PT TO HOST BUFFER 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 ; ; 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 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 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 CPI 2 ;IS DISK C: OR D:? JNC GNSDC ;ALWAYS DOUBLE IF SO ORA A ;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 GNSDC: XRA A ;CORRECT IF DOUBLE DENSITY RET ; ; 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 MAYBE ; WILL USE BUFFER XRA A STA HSTACT ;TOSS READ CONTENTS STA UNACNT ;BUFFER LEAVING, NEED ; PRE-READ ON NEXT UNALLOC LDA ERFLAG ;SET UP ERROR STATUS ORA A RET ; ; 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' ; ; MERGE HERE FROM PRINTER OFFLINE ; 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 SHLD NSTRK+2 ; C, D 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 ; ; IF NOT NSHOLE: ; ; READ ALL TEN SECTORS FROM TRACK ONE, THEN ONE MORE ; SECTOR FROM TRACK ZERO, SECTOR THREE. ; ; IF NSHOLE: ; ; READ EIGHT SECTORS FROM TRACK 1, SECTORS 0-7, THEN ; THREE SECTORS FROM TRACK 0, SECTORS 3, 1, AND 2, IN ; THAT ORDER. SKIP THE 1K "HOLE" IN BDOS WHEN LOADING ; WHERE THE NORTH STAR CONTROLLER WILL BE. ; 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 ; IF NOT NSHOLE CPI 10 ;THROUGH WITH TRACK ONE? ENDIF ;NOT NSHOLE IF NSHOLE CPI 8 ;THROUGH WITH TRACK ONE? ENDIF ;NSHOLE ; JNZ WREAD ;GO GET NEXT SECTOR IF NOT XRA A ;TO TRACK ZERO, STA HSTTRK MVI A,3 ; SECTOR 3 IF SO STA HSTSEC CALL WSETUP ;KICK MOTORS AND SEEK TRK 0 ; IF NSHOLE LXI D,KBYTE ;SKIP OVER N* CONTROLLER LHLD HSTADR DAD D SHLD HSTADR CALL WRMRD ;READ NEXT SECTOR LXI D,HSTSIZ ;ADVANCE PTR FOR NEXT SECTOR LHLD HSTADR DAD D SHLD HSTADR MVI A,1 ;READING SECTOR 1 STA HSTSEC CALL WRMRD ;READ IT LXI D,HSTSIZ ;REPEAT FOR SECTOR 2 LHLD HSTADR DAD D SHLD HSTADR MVI A,2 STA HSTSEC ENDIF ;NSHOLE ; CALL WRMRD ;READ THE LAST SECTOR LXI H,HSTBUF ;NOW READ AND WRITE SHLD HSTADR ; USING DEBLOCK BUFFER ; ; THE NEXT TWO INSTRUCTIONS ARE ONLY INTERESTING IF YOU ; HAVE AN EPSON PRINTER WITH DEVICE SELECT. OTHERS SHOULD ; REMOVE. THE "OUT TTY" INSTRUCTION IS HARD-WIRED HERE ; INSTEAD OF MAKING A CALL TO BLIST SO THAT THE ABSENCE OF ; AN EPSON WILL NOT HANG US UP IN AN INFINITE WARM BOOT LOOP. ; MVI A,011H ;TELL EPSON PRINTER TO SELECT OUT TTY ; IF THERE AND NOT SELECTED ; 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 LDA HUNGUP ;SEE IF TALKING ON PHONE ORA A PUSH PSW ;SAVE DECISION CZ CNSOT ;NO MODEM OUTPUT IF NOT POP PSW CNZ BCNSOT ;OUTPUT TO ALL CONSOLE ; IF SO POP H ;RESTORE HL MOV A,M ;GET CHAR TO LOOK AT IT 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 ; 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 ; ; 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 ; DPBNSD: DW 40 ;SECTORS PER TRACK DB 3 ;BLOCK SHIFT FACTOR DB 07H ;BLOCK MASK DB 0 ;NULL MASK DW DSKSIZ-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. ; 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 (35 TRACK ONLY) ; DPBNSS: DW 20 ;SECTORS PER TRACK DB 3 ;BLOCK SHIFT FACTOR DB 07H ;BLOCK MASK DB 0 ;NULL 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 ; ; SPECIAL DPB FOR 80 TRACKS, DOUBLE SIDED ; DPBNSC: DW 40 ;SECTORS PER TRACK DB 4 ;BLOCK SHIFT FACTOR DB 0FH ;BLOCK MASK DB 0 ;NULL MASK DW 395-1 ;DISK SIZE - 1 DW 127 ;DIRECTORY MAX DB 11000000B ;ALLOC 0 DB 0 ;ALLOC 1 DW 32 ;CHECK SIZE DW 2 ;TRACK OFFSET ; DB 0100B ;512 BYTES/SECTOR ; ; DRIVE PARAMETER HEADER AREA ; ; DRIVES A: AND B: ARE 40 TRACKS, SINGLE SIDED ; 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 80 TRACKS, DOUBLE SIDED ; D2DPH: DW TRAN5D,0,0,0,DIRBF,DPBNSC,D2CHK,D2ALL ; D3DPH: DW TRAN5D,0,0,0,DIRBF,DPBNSC,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: DS 1 ;DEF DISK AND USER, SET ; TO ZERO AT HANGUP JMP BDOS+06H ;BDOS CALL VECTOR ; ; BIOS VARIABLE STORAGE ; SEKDSK: DB 0 ;DRIVE NUMBER SEKTRK: DS 1 ;TRACK NUMBER SEKSEC: DS 1 ;SECTOR NUMBER SEKHST: DS 1 ;SEEK SHR SECSHF HSTACT: DS 1 ;HOST ACTIVE FLAG HSTWRT: DS 1 ;HOST WRITTEN FLAG ; 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 DTPTR: DW 0 ;DRIVE TABLE PTR ; UNACNT: DS 1 ;UNALLOC REC CNT UNADSK: DS 1 ;LAST UNALLOC DISK UNATRK: DS 1 ;LAST UNALLOC TRACK 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 ; ; DIRECTORY BUFFER ; DIRBF: DS SECSZ ; ; BDOS ALLOCATION AND CHECKSUM BUFFERS ; D0ALL: DS (DSKSIZ/8)+1 ;ALLOC 0 D0CHK: DS 16 ;CHECKSUMS 0 D1ALL: DS (DSKSIZ/8)+1 ;ALLOC 1 D1CHK: DS 16 ;CHECKSUMS 1 D2ALL: DS (395/8)+1 ;ALLOC 2 D2CHK: DS 32 ;CHECKSUMS 2 D3ALL: DS (395/8)+1 ;ALLOC 3 D3CHK: DS 32 ;CHECKSUMS 3 ; ; 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 HSTTRK: DB 0 ;HOST TRACK NUMBER HSTSEC: DB 5 ;HOST SECTOR NUMBER HSTDSK: DB 0 ;HOST DISK 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. ; 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 TRUTRK: DS 1 ;TRUE DISK SEEK TRACK ; ; 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 ; ; DRIVE STEPPING RATE TABLE (SEE STEP ROUTINE) ; STPTBL: DW STPSLW ;DRIVE A = SLOW DW STPSLW ;DRIVE B = SLOW DW STPFST ;DRIVE C = FAST DW STPFST ;DRIVE D = FAST ; ; 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. IF HSTTRK IS >= 80, SECOND SIDE ; IS SELECTED. ; SETUP: MVI B,00H ;ASSUME TOP SIDE LDA HSTTRK ;CHECK TARGET TRK STA TRUTRK ;ASSUME GOING HERE SUI 80 ;GOING TO SIDE TWO? JC SIDE1 ;PASS IF NOT MVI B,40H ;SIDE TWO SELECT STA TRUTRK ;GOING HERE SIDE1: 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 ORA B ;OR IN SIDE 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 ;CHECK DISK AND SIDE SELECT JZ SET20 ;GO SEEK TO TRACK IF MATCH ; ; MUST SELECT NEW DISK OR CHANGE SIDE ; 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 TRUTRK 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 TRUTRK ;SEEK TO THIS TRACK PUSH PSW ;SAVE OVER CALL CALL SEEK ;DO FINAL SEEK POP PSW ;GET TRUTRK 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. ; SEEK: MOV C,A ;SAVE TARGET TRK # SUB M ;SEE HOW FAR AWAY WE ARE ; FROM TARGET TRACK MOV M,C ; 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 STEP DELAY TIME GIVEN BY CORRESPONDING ENTRY ; IN STPTBL. NOTE THAT STEP TIMES ARE A FUNCTION OF ; PROCESSOR SPEED AND DRIVE MODEL, AND MAY BE ADJUSTED ; BY PATCHING STPTBL. THE DELAY LOOP BELOW TAKES ; 31 T-STATES. AT 4 MHZ, THAT'S 7.75 USEC. AT 2.5 MHZ, ; THAT'S 12.4 USEC. AT 4 MHZ WITH ONE M1 WAIT PER ; OPCODE, THAT'S 8.5 USEC. ; PUSH H ;SAVE SOME REGS LDA HSTDSK ;GET DRIVE # ADD A ;DOUBLE FOR WORD ENTRIES ADI STPTBL AND 0FFH ;CALC TABLE PTR MOV L,A ; AND PUT IN HL MVI A,(STPTBL SHR 8) AND 0FFH ACI 0 ;PICK UP POSS CARRY MOV H,A MOV A,M ;GET DELAY CTR INX H MOV H,M ; INTO HL MOV L,A ; STEPW: LXI D,1 ;DELAY DAD D JNC STEPW POP H ;RESTORE REGS 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 ; ELSE WAIT ONE MORE SECTOR ; TIME TO MAKE SURE SAFE ; FOR I/O ; ; 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 JZ DSKER2 ;CAN REPORT IF NOT JMP NSROM ;BACK TO N* BOOT ROM IF SO, ; SINCE CNSOT NOT IN MEM ; 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