; Support for CP/M 2.0 added 5 Sept 79 - J R Pierce ;HARD DISK VERSION 8-26-79 ; BIOS FOR MICRO-2 COMPUTER ; ;THE IOBYTE IS IMPLEMENTED ; ;INTERRUPTS ARE NOT IMPLEMENTED ; maclib diskdef ; ;THIS VERSION CONTAINS DISK DRIVERS FOR THE DIGITAL SYSTEMS ;FDC-3 CONTROLLER BOARD. THIS BOARD CAN HANDLE DOUBLE DENSITY ; ; NOTE : MSIZE DETERMINES WHERE THIS CBIOS IS LOCATED MSIZE EQU 64 ;CP/M VERSION MEMORY SIZE IN KILOBYTES VERS EQU 20 ndisks equ 2 ram$top equ msize*1024 bios equ ram$top-6*256 bdos equ bios-0e00h ccp equ bdos-0800h warm$boot equ ccp-0080h org bios ; WE WILL USE A SCRATCH AREA STARTING AT 40H SCRAT EQU 40H ;START OF SCRATCH AREA TRACK EQU SCRAT ;CURRENT TRACK ON DRIVE 0 TRAK1 EQU TRACK+1 ;CURRENT TRACK ON DRIVE 1 TRAK2 EQU TRAK1+1 TRAK3 EQU TRAK2+1 SECTOR EQU TRAK3+1 ;CURRENTLY SELECTED SECTOR DMAAD EQU SECTOR+1 ;CURRENT DMA ADDRESS DISKNO EQU DMAAD+2 ;CURRENT DISK NUMBER DUMMY EQU DISKNO+1 ;MUST BE 0 FOR DOUBLE ADD ERRORS EQU DUMMY+1 PORT EQU 4AH PORTOUT EQU PORT+1 DENSITY EQU PORTOUT+1 ; IOBYTE EQU 3 ; ;SET UP INPUT OUTPUT PORTS DATA EQU 40H STATUS EQU DATA+1 DATA1 EQU 48H STATUS1 EQU DATA1+1 DATA2 EQU 50H STATUS2 EQU DATA2+1 PARALLEL EQU 61H ; ; ; JUMP VECTOR FOR INDIVIDUAL SUBROUTINES JMP WARMBOOT ;COLD START WBOTE: JMP WBOOT ;WARM START JMP CONST ;CONSOLE STATUS JMP CONIN ;CONSOLE CHARACTER IN JMP CONOUT ;CONSOLE CHARACTER OUT JMP LIST ;LIST CHARACTER OUT JMP PUNCH ;PUNCH CHARACTER OUT JMP READER ;READER CHARACTER OUT JMP HOME ;MOVE HEAD TO HOME POSITION JMP SELDSK ;SELECT DISK JMP SETTRK ;SET TRACK NUMBER JMP SETSEC ;SET SECTOR NUMBER JMP SETDMA ;SET DMA ADDRESS JMP READ ;READ DISK JMP WRITE ;WRITE DISK jmp lstat ; list status routine jmp sectran ; logical->physical sector mapping ;THE WARM BOOT ROUTINE EXPECTS TO FIND THE LOG ON MESSAGE HERE ;SINCE THERE WAS NOT ENOUGH ROOM IN IT DW 0D0AH ;CR,LF DB MSIZE/10+'0',MSIZE MOD 10+'0' DB 'k CP/M Vers' DB VERS/10+'0','.',VERS MOD 10+'0' DB 0 ; ; INDIVIDUAL SUBROUTINES TO PERFORM EACH FUNCTION ; WBOOT: ;READ IN TRACK 0 SECTOR 2 WHICH WILL DO THE REST OF THE WARM START ; LXI SP,80H ;USE SPACE BELOW BUFFER FOR STACK MVI C,0 ;SELECT DISK 0 CALL SELDSK CALL HOME ;GO TO TRACK 00 ; ;SET SINGLE DENSITY LDA PORT ANI 0F7H STA PORT ;SET DMA ADDRESS TO 80 BELOW START OF CCP LXI B,WARMBOOT CALL SETDMA MVI C,2 CALL SETSEC ;NOW READ CALL READ ORA A ;ANY ERRORS? JNZ WBOOT ;RETRY THE ENTIRE BOOT IF AN ERROR OCCURS ; ; JMP WARMBOOT+3 ;GO FINISH THE WARM START ;I/O HANDLERS ; CONST: CALL CONS ORA A RZ MVI A,0FFH RET ; CONS: CALL condsptch DW TTYST DW CRTST DW BATST DW UC1ST ; CONIN: CALL condsptch DW TTYIN DW CRTIN DW BATIN DW UC1IN ; CONOUT: CALL condsptch DW TTYOUT DW CRTOUT DW BATOUT DW UC1OUT ; LIST: LDA IOBYTE RLC! RLC CALL IODSPRLC DW TTYOUT DW CRTOUT DW LPTOUT DW UL1OUT lstat: ; Dummy Routine xra a ! ret PUNCH: LDA IOBYTE RRC! RRC! RRC CALL IODISPATCH DW TTYOUT DW PTPOUT DW UP1OUT DW UP2OUT ; READER: LDA IOBYTE RRC CALL IODISPATCH DW TTYIN DW PTRIN DW UR1IN DW UR2IN ; condsptch: lda io$byte IODSPRLC: RLC IODISPATCH: ANI 6H XTHL ;GET TABLE ADDRESS PUSH D MOV E,A MVI D,0 DAD D MOV A,M INX H MOV H,M MOV L,A POP D XTHL RET ; ; PORT0ST: ;CONSOLE STATUS, RETURN 0FFH IF CHARACTER READY, 00H IF NOT IN STATUS ANI 2 RZ MVI A,0FFH RET ; ; PORT0IN: ;CONSOLE CHARACTER INTO REGISTER A CALL PORT0ST JZ PORT0IN IN DATA ANI 7FH ;STRIP PARITY BIT RET ; ; PORT0OUT: ;CONSOLE CHARACTER OUTPUT FROM REGISTER C in status rrc jnc port0out MOV A,C OUT DATA RET ; ; PORT1ST: IN STATUS1 ANI 2 RZ MVI A,0FFH RET ; PORT1IN: CALL PORT1ST JZ PORT1IN IN DATA1 ANI 7FH ;STRIP PARITY BIT RET ; PORT1OUT: ;LIST CHARACTER FROM REGISTER C IN STATUS1 RRC JNC PORT1OUT MOV A,C ;CHARACTER TO REGISTER A OUT DATA1 RET ;NULL SUBROUTINE ; PORT2ST: IN STATUS2 ANI 2 RZ MVI A,0FFH RET ; PORT2IN: CALL PORT2ST JZ PORT2IN IN DATA2 ANI 7FH RET ; PORT2OUT: IN STATUS2 RRC JNC PORT2OUT MOV A,C OUT DATA2 RET ; ; PORTPOUT: IN PARALLEL ANI 80H JNZ PORTPOUT MOV A,C OUTIT: ORI 80H OUT PARALLEL ANI 7FH OUT PARALLEL ORI 80H OUT PARALLEL RET ; ;CP/M PHYSICAL DEVICE TO MICRO2 PHYSICAL DEVICE MAP ; TTYST EQU PORT0ST TTYIN EQU PORT0IN TTYOUT EQU PORT0OUT CRTST EQU PORT1ST CRTIN EQU PORT1IN CRTOUT EQU PORT1OUT BATST EQU PORT2ST BATIN EQU PORT2IN BATOUT EQU PORT2OUT UC1ST EQU PORT0ST UC1IN EQU PORT0IN UC1OUT EQU PORT0OUT LPTOUT EQU PORTPOUT UL1OUT EQU PORT2OUT PTPOUT EQU PORT2OUT PTRIN EQU PORT2IN UP1OUT EQU PORT0OUT UP2OUT EQU PORT1OUT UR1IN EQU PORT0IN UR2IN EQU PORT1IN ; ; COMAND1 EQU 80H STAT EQU 80H HADDR EQU 81H LADDR EQU 82H COMAND2 EQU 83H ; ; ; I/O DRIVERS FOR THE DISK FOLLOW ; HOME: ;MOVE TO THE TRACK O0 POSITION OF CURRENT DRIVE CALL HEADLOAD ; H,L POINT TO WORD WITH TRACK FOR SELECTED DISK HOMEL: MVI M,00 ;SET CURRENT TRACK PTR BACK TO 0 IN STAT ;READ FDC STATUS ANI 4 ;TEST TRACK 0 BIT RZ ;RETURN IF AT 0 STC ;DIRECTION=OUT CALL STEP ;STEP ONE TRACK JMP HOMEL ;LOOP ; SELDSK: ;SELECT DISK GIVEN BY REGISTER C ;MAKE SURE DUMMY IS 0 (FOR USE IN DOUBLE ADD TO H,L) cpi ndisks ! jnc bad$drive XRA A STA DUMMY MOV A,C ANI 07H ;GET ONLY DISK SELECT BITS STA DISKNO MOV C,A MOV B,A ;GET DENSITY OF SELECTED DRIVE ;B HAS DRIVE NUMBER FROM 0-7 LDA DENSITY ;DENSITY BIT 0= DENSITY FOR DRIVE A, BIT 1=DRIVE B ETC ;A 1 MEANS DOUBLE DENSITY GETDR: DCR B RRC JP GETDR ;NOT AT PROPER BIT YET JC SETDOB SETSING: LXI D,SINGTAB lxi h,stagger JMP OVERBOTH SETDOB: LXI D,DOBTAB lxi h,0 ; no stagger table MOV A,C ;SET THE CHANGE DENSITY BIT ORI 08 MOV C,A OVERBOTH: lda port ! ani 0f0h ! ora c ! sta port push h ; save stagger table address lhld diskno dad h ! dad h ! dad h ! dad h lxi b,dpbase+10 dad b ; points HL to right disk parameter table mov m,e ! inx h ! mov m,d ; save diskdef address lxi b,-10 ; point back to tran table address dad b pop d ; get current tran table mov m,d ! dcx h ! mov m,e ; store its address ; HL now points to appropriate drive parameter block RET bad$drive: ; here if invalid drive code lxi h,0 ! ret ; zero means select error SETTRK: ;SET TRACK GIVEN BY REGISTER C CALL HEADLOAD ;H,L REFERENCE CORRECT TRACK INDICATOR ACCORDING TO ;SELECTED DISK MOV A,C ;DESIRED TRACK CMP M RZ ;WE ARE ALREADY ON THE TRACK SETTKX: CALL STEP ;STEP TRACK-CARRY HAS DIRECTION ;STEP WILL UPDATE TRACK INDICATOR MOV A,C CMP M ;ARE WE WHERE WE WANT TO BE JNZ SETTKX ;NOT YET ;HAVE STEPPED ENOUGH SEEKRT: ;DELAY 10 MSEC FOR FINAL STEP TIME AND HEAD SETTLE TIME ;THE DELAY ROUTINE DELAYS .5 MILLISECOND MVI A,20D CALL DELAY RET ;END OF SETTRK ROUTINE ; DELAY: ;ROUTINE TO DELAY C(A) .5 MILLISECONDS PUSH B DELAY2: MVI C,086H ;ADJUST FOR .5 MSEC LOOP DELAY ;THIS IS THE VALUE FOR OUR IMSAI LDXA: DCR C JNZ LDXA ;LOOP 1 MSEC DCR A JNZ DELAY2 POP B RET ;END OF DELAY ROUTINE ; sectran: mov h,b ! mov l,c ! inx h ; in case we aren't using translation mov a,d ! ora e ! rz ; we return logical+1 xchg ! dad b ! mov l,m ! mvi h,0 ; else fetch physical ret ; back to bdos SETSEC: ;SET SECTOR GIVEN BY REGISTER C MOV A,C STA SECTOR RET ; SETDMA: ;SET DMA ADDRESS GIVEN BY REGISTERS B AND C MOV L,C ;LOW ORDER ADDRESS MOV H,B ;HIGH ORDER ADDRESS SHLD DMAAD ;SAVE THE ADDRESS RET ; ; READ: ;PERFORM READ OPERATION. ;THIS IS SIMILAR TO WRITE, SO SET UP READ COMMAND AND USE ;COMMON CODE IN WRITE MVI B,040H ;SET READ FLAG JMP WAITIO ;TO PERFORM THE ACTUAL I/O ; WRITE: ;PERFORM A WRITE OPERATION MVI B,080H ;SET WRITE COMMAND ; WAITIO: ;ENTER HERE FROM READ AND WRITE TO PERFORM THE ACTUAL I/O ;OPERATION. RETURN A 00H IN REGISTER A IF THE OPERATION COMPLETES ;PROPERLY, AND 01H IF AN ERROR OCCURS DURING THE READ OR WRITE ; ;IN THIS CASE, WE HAVE SAVED THE DISK NUMBER IN 'DISKNO' ; THE TRACK NUMBER IN 'TRACK' ; THE SECTOR NUMBER IN 'SECTOR' ; THE DMA ADDRESS IN 'DMAAD' ;B STILL HAS R/W FLAG MVI A,10D ;SET ERROR COUNT STA ERRORS ;RETRY SOME FAILURES 10 TIMES ;BEFORE GIVING UP TRYAGN: PUSH B CALL HEADLOAD ;H,L POINT TO TRACK BYTE FOR SELECTED DISK POP B MOV C,M ; DECIDE WHETHER TO ALLOW DISK WRITE PROCOMPENSTATION MVI A,39D ;PRECOMP SHOULD BE INHIBITED ON TRACKS ;0-39 CMP C JC ALLOWIT ;INHIBIT PRECOMP MVI A,10H ORA B MOV B,A ;GOES OUT ON THE SAME PORT AS READ/WRITE ALLOWIT: LHLD DMAAD ;GET BUFFER ADDRESS PUSH B ;B HAS R/W CODE C HAS TRACK DCX H ;SAVE AND REPLACE 3 BYTES BELOW ;BUF WITH TRACK,SECTOR,ADDRESS MARK MOV E,M ;FIGURE CORRECT ADDRESS MARK LDA PORT ANI 08H MVI A,0FBH JZ SIN ANI 0FH ;WAS DOUBLE ;0BH IS DOUBLE DENSITY ;0FBH IS SINGLE DENSITY SIN: MOV M,A ;FILL IN SECTOR DCX H MOV D,M LDA SECTOR ;NOTE THAT INVALID SECTOR NUMBER ;WILL RESULT IN HEAD UNLOADED ;ERROR, SO DONT CHECK MOV M,A ;FILL IN TRACK DCX H POP B MOV A,C MOV C,M MOV M,A MOV A,H ;SET UP FDC DMA ADDRESS OUT HADDR ;HIGH BYTE MOV A,L OUT LADDR ;LOW BYTE MOV A,B ;GET R/W FLAG OUT COMAND1 ;START DISK READ/WRITE RWWAIT: IN STAT ;READ FDC STATUS ANI 088H ;TEST FOR HEAD UNLOAD OR IOF JZ RWWAIT MOV M,C ;RESTORE 3 BYTES BELOW BUF INX H MOV M,D INX H MOV M,E IN STAT ;TEST FOR ERRORS ANI 0F0H RZ ;A WILL BE 0 IF NO ERRORS ERRTN: ;COME HERE ON ERROR FROM DISK PUSH PSW ;SAVE ERROR CONDITION ;CHECK FOR 10 ERRORS LXI H,ERRORS DCR M JNZ REDO ;NOT TEN YET. DO A RETRY ;WE HAVE TOO MANY ERRORS. PRINT OUT HEX NUMBER FOR LAST ;RECEIVED ERROR TYPE. CPM WILL PRINT PERM ERROR MESSAGE. POP PSW ;GET CODE RRC RRC RRC RRC ;MAKE IT ASCII ORI 030H STA BDOS+0A4H ;SET ERROR RETURN FOR OPERATING SYSTEM MVI A,1 RET REDO: ;B STILL HAS READ/WRITE FLAG POP PSW ;GET ERROR CODE ANI 0E0H ;RETRY IF NOT TRACK ERROR JNZ TRYAGN ; ;WAS A TRACK ERROR SO NEED TO RESEEK PUSH B ;SAVE READ/WRITE INDICATOR ;FIGURE OUT THE DESIRED TRACK LXI D,TRACK LHLD DISKNO ;SELECTED DISK DAD D ;POINT TO CORRECT TRACK INDICATOR MOV A,M ;DESIRED TRACK PUSH PSW ;SAVE IT CALL HOME POP PSW MOV C,A CALL SETTRK POP B ;GET READ/WRITE INDICATOR JMP TRYAGN ; ; ; STEP: ;STEP HEAD OUT TOWARDS ZERO ;IF CARRY IS SET; ELSE ;STEP IN ; H,L POINT TO CORRECT TRACK INDICATOR WORD JC OUTX INR M ;INCREMENT CURRENT TRACK BYTE MVI A,04H ;SET DIRECTION = IN DOSTEP: ORI 2 OUT COMAND1 ;PULSE STEP BIT ANI 0FDH OUT COMAND1 ;TURN OFF PULSE ;THE FDC-2 HAD A STEPP READY LINE. THE FDC-3 RELIES ON ;SOFTWARE TIME OUT MVI A,16D ;WAIT FOR STEP READY ;DELAY ROUTINE DELAYS FOR .5 MSEC TIMES THE CONTENTS OF REG A CALL DELAY RET ; OUTX: DCR M ;UPDATE TRACK BYTE XRA A JMP DOSTEP ; HEADLOAD: ;SELECT AND LOAD THE HEAD ON THE CORRECT DRIVE LXI H,PORTOUT ;OLD SLECT INFO MOV B,M DCX H ;NEW SELECT INFO MOV A,M INX H MOV M,A OUT COMAND2 ;SELECT THE DRIVE ;SET UP H.L TO POINT TO TRACK BYTE FOR SELECTED DISK LXI D,TRACK LHLD DISKNO DAD D ;NOW CHECK FOR NEEDING A 35 MS DELAY ;IF WE HAVE CHANGED DRIVES OR IF THE HEAD IS UNLOADED ;WE NEED0TO WAIT 35 MS FOR HEAD SETTLE CMP B ;ARE WE ON THE SAME DRIVE AS BEFORE JNZ NEEDDLY ;WE ARE ON THE SAME DRIVE ;IS THE HEAD LOADED? IN STAT ANI 80H RZ ;ALREADY LOADED NEEDDLY: XRA A OUT COMAND1 ;LOAD THE HEAD ;THE DELAY ROUTINE DELAYS FOR .5 MSEC MVI A,70D CALL DELAY RET ; ; disks 2 dpbase: dpe0: dw $-$, 0 dw 0, 0 dw dirbuf, $-$ dw csv0, alv0 dpe1: dw $-$, 0 dw 0, 0 dw dirbuf, $-$ dw csv1, alv1 dobtab: ; diskdef 0,1,58,,2048,256,128,128,2 dw 58 db 4,15,1 dw 255,127 db 192,0 dw 32,2 singtab: ; diskdef 1,1,26,6,1024,243,64,64,2 dw 26 db 3,7,0 dw 242,63 db 192,0 dw 16,2 stagger: ; Standard CP/M Stagger Table (Skew 6) db 1,7,13,19,25 db 5,11,17,23 db 3,9,15,21 db 2,8,14,20,26 db 6,12,18,24 db 4,10,16,22 ; endef begdat: dirbuf: ds 128 alv0: ds 32 csv0: ds 32 alv1: ds 32 csv1: ds 32 enddat equ $ end