PAGE 60 TITLE 'Cromemco 4FDC/16FDC BIOS in 8080 Code' ; ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; modifications by Mike Goetz and Hank Kee - 9/2/81 ; ; corrected PIP A:=B:filename.ext on Persci 277/299 ; Persci's must be configured as A: and B: ; and/or C: and D: since they have but one ; arm to access two drives ; modified sign-on message to properly generate size ; from MEMSIZE definition during assembly ; corrected current drive assignment on warm boot ; inclusion of LIST serial driver for CCS 8250 (30 CPS) ; option to support LA34/LA36 ; ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; ; This version modified for 4 drives, A, B, C, D, 2-8" and 2-5" ; In this first version, the boot drive is "A:" only, the 1st 8" ; ; MISC EQUATES ; PERSCI EQU 0 ;ONE IF PERSCI DRIVE LARGESW EQU 1 OR PERSCI ;ONE IF MAXI DRIVE NBRDRV EQU 2 ;ONE TO FOUR (MUST BE SAME TYPE) BIGIO EQU 1 ;ZERO IF NO LIST, NO PUNCH SERIAL EQU 1 ;SERIAL PRINTER OPTION W/8250 ; VERS EQU 22 ;VERSION 2.2 MEMSIZ EQU 48 ;48K SYSTEM MEMT EQU MEMSIZ*1024 MEMONES:EQU MEMSIZ-(MEMSIZ/10*10) MEMTENS:EQU MEMSIZ/10 ; ; THE FOLLOWING PARAMETER VALUES FOR VERSION 2.X (INCL. 2.2) ; BOOTSIZ EQU 1*128 CCPSIZ EQU 16*128 BDOSIZ EQU 28*128 BIOSIZ EQU 12*128 ;7 ON DISK, EXCESS UNINITIALIZED RAM STACK EQU MEMT ; BIOS EQU MEMT-BIOSIZ BDOS EQU BIOS-BDOSIZ CCP EQU BDOS-CCPSIZ BASE EQU 0 ;BASE OF USABLE RAM IOBYTE EQU BASE+3 ;I/O DEVICE ASSIGNMENT BYTE LOGDSK EQU BASE+4 ;WHERE CPM STORES DEFAULT DRIVE FCB EQU BASE+5CH ;DEFAULT FILE CONTROL BLOCK BUFF EQU BASE+80H ;DEFAULT DISK BUFFER TPA EQU BASE+100H ;BASE OF TRANSIENT PGM AREA ; ; LOCATIONS AT SYSGEN TIME ; SYSGEN EQU 900H BOOTGEN EQU SYSGEN CCPGEN EQU BOOTGEN+BOOTSIZ BDOSGEN EQU CCPGEN+CCPSIZ BIOSGEN EQU BDOSGEN+BDOSIZ BIOSEND EQU BOOTGEN+(52*128)-1 OFFSET EQU CCPGEN-CCP ; ; CROMEMCO 4FDC I/O ASSIGNMENTS ; CSTAT EQU 00H ;3P + S, 4FDC, TU-UART, OR SCC CONDATA EQU 01H CIMODE EQU 02H ;(3P + S DOESNT HAVE THESE) CIMASK EQU 03H PARPORT EQU 04H ;NOT USED ; FDCSTAT EQU 30H ;4FDC OR CCS 2422 BOARD FDCTRK EQU 31H FDCSECT EQU 32H FDCDATA EQU 33H FDCFLAG EQU 34H ; MBANKPT EQU 40H ;MEMORY BANKING PORT ; ; 1771/179X EQUATES ; HDLOAD EQU 08H VERIFY EQU 04H ; IF BIGIO = 1 ; ; OTHER DEFINITIONS (OPTIONAL) ; BAUDRATE EQU 01H ;110 BAUD FOR READER ? RFDCSTAT EQU 10H RBAUDPORT EQU RFDCSTAT ;TU-UART BOARD FOR PAPER TAPE RFDCDATA EQU RFDCSTAT+1 PFDCSTAT EQU 10H PBAUDPORT EQU PFDCSTAT ;PAPER TAPE PUNCH ALSO PFDCDATA EQU PFDCSTAT+1 LFDCSTAT EQU 54H ;PARALLEL PRINTER POARD LFDCDATA EQU LFDCSTAT ENDIF TRUE EQU -1 FALSE EQU 0 NULLS EQU 6 ; NUMBER OF NULLS AFTER LF LF EQU 0AH ; LINE FEED SDATA EQU 20H ; DATA PORT SINTEN EQU 21H ; INTERRUPT REGISTER SIDENT EQU 22H ; INTERRUPT IDENTIFICATION SLCTRL EQU 23H ; LINE CONTROL REGISTER SMDMCT EQU 24H ; MODEM CONTROL REGISTER SLSTAT EQU 25H ; LINE STATUS REGISTER SMDMST EQU 26H ; MODEM STATUS REGISTER SSTATP EQU 25H ; SERIAL PRINTER STATUS PORT (INPUT) STBE EQU 20H ; SERIAL PRINTER TBE BIT ; ORG BIOS ; ; CPM JUMP VECTOR ENTRY POINTS ; JMP CBOOT JMP WBOOT JMP CONSTAT JMP CONIN JMP CONOUT JMP LIST JMP PUNCH JMP READER JMP HOME JMP SELDSK JMP SETTRK JMP SETSEC JMP SETDMA JMP READ JMP WRITE JMP LISTAT JMP SECTRAN ; ; CP/M 2.2 DISK CONTROL BLOCKS ; DPBASE DW XLT8 DW 0000 DW 0000 DW 0000 DW DIRBUF DW SD8PBLK DW CSV0 DW ALV0 ; IF NBRDRV > 1 DW XLT8 DW 0000 DW 0000 DW 0000 DW DIRBUF DW SD8PBLK DW CSV1 DW ALV1 ENDIF ; IF NBRDRV > 2 DW XLT8 DW 0000 DW 0000 DW 0000 DW DIRBUF DW SD8PBLK DW CSV2 DW ALV2 ENDIF ; IF NBRDRV > 3 DW XLT8 DW 0000 DW 0000 DW 0000 DW DIRBUF DW SD8PBLK DW CSV3 DW ALV3 ENDIF ; ; disk parameter blockS, common to all disks ; this implements a "standard" CP/M directory ; with 64 entries, and 1K blocks. ; SD8PBLK DW 26 ;sectors per track DB 3 ;block shift factor DB 7 ;block mask DB 0 ;null mask DW 242 ;disk size-1 DW 63 ;directory max DW 00C0H ;INITIAL FREE SPACE MAP DW 16 ;check size DW 2 ;track offset ; ; this implements the cdos standard 5" single density ; 18 sectors (128 bytes) per track, 40 tracks, 3 reserved ; sectors, 83k of storage ; SD5PBLK DW 18 ;sectors per track DB 3 ;block shift factor DB 7 ;block mask DB 0 ;null mask DW 82 ;disk size-1 DW 63 ;directory max DW 00C0H ;INITIAL FREE SPACE MAP DW 16 ;check size DW 3 ;track offset ; ; sector translate vector ; XLT8 DB 1,7,13,19,25,5,11,17,23,3,9,15,21 DB 2,8,14,20,26,6,12,18,24,4,10,16,22 ; XLT5 DB 1,6,11,16,3,8,13,18,5 DB 10,15,2,7,12,17,4,9,14 ; SIGNON DB 0DH,0AH,0AH DB MEMTENS+30H DB MEMONES+30H DB 'k CP/M version 2.2' DB 0DH,0AH,0 ; SELDSK: LXI H,0 MOV A,C ;C CONTAINS REQUESTED DRIVE NO. CPI NBRDRV RNC ;IGNORE IF TOO HIGH STA DKNUMB MOV L,C ;L=disk number 0,1,2,3 DAD H ;*2 DAD H ;*4 DAD H DAD H ;*16 (size of each header) LXI D,DPBASE DAD D ;HL=.dpbase(diskno*16) RET ; ;TRANSLATE SECTOR IN C USING TABLE AT DE ; SECTRAN:MVI B,0 XCHG ;TABLE ADDR TO HL DAD B ;GET ADDRESS MOV L,M ;GET BYTE MVI H,0 ;ANSWER IN HL RET ; SETSEC: MOV A,C ;JUST SAVE SECTOR NUMBER STA DKSECT RET ; SETDMA: PUSH H MOV L,C MOV H,B SHLD DKDMA POP H RET ; ; ERROR CHECKING READ AND WRITE RTNS FOR ; CROMEMCO CBIOS ; READ: CALL CLEAR RTYRD: CALL RD4FDC ;READ SECTOR RZ ;SUCCESSFUL READ. RETURN CALL ERROR ;INCREMENT RTYCNT JNZ RTYRD ;RETRY 20 TIMES ORI 01H ;CP/M CONVENTION FOR PERMANENT ERROR RET ; CLEAR: XRA A STA TRKERCT ;ZERO OUT TRACK ERROR COUNTER STA RTYCNT ;ZERO OUT CRC ERROR COUNTER RET ; WRITE: CALL CLEAR RTYWRT: CALL WR4FDC ;WRITE SECTOR RZ ;SUCCESSFUL WRITE. RETURN CALL ERROR ;INCREMENT RTYCNT JNZ RTYWRT ;RETRY 20 TIMES ORI 01H ;CP/M CONVENTION FOR PERMANENT ERROR RET ; ERROR: PUSH H ANI 10H ;CHECK FOR NRF JNZ TRKERR LXI H,RTYCNT INR M ;INCREMENT RTYCNT MOV A,M POP H SUI 20 ;20 TRIES? RET ; TRKERR: LXI H,TRKERCT INR M ;INCREMENT NO OF TRACK ERRORS MOV A,M SUI 10 ;ALLOW ONLY 10 TRACK ERRORS POP H RZ ;IF >10, RETURN A FAILURE PUSH B CALL HOME ;HOME THE HEAD LDA DKTRACK MOV C,A ;GET TRACK IN C CALL SETTRK ;RESEEK TO CORRECT TRACK POP B ORI 0FFH ;RETRY RET ; ; RESTORE THE DISK TO TRACK ZERO ; HOME: SUB A ;ZERO OUT TRACK COUNTER STA DKTRACK CALL DSKSEL ;NOW SELECT THE DISK OUT FDCFLAG MVI A,02H+HDLOAD+VERIFY ;USE SLOW STEPPING SPEED FOR ALL DISKS OUT FDCSTAT RSTI: IN FDCFLAG ;NOW CHECK STATUS RAR JNC RSTI ;LOOP BACK UNTIL DONE JMP SEEKTST ; ; HERE, ACTUALLY DO THE READ OPERATION ; RD4FDC: PUSH B PUSH H ;FIRST, SAVE REGS PUSH D MVI E,88H ;READ COMMAND (VALID FOR 1771, 179X) CALL INIT4FDC RDI1: IN FDCFLAG ;NOW CHECK FLAGS RAR JC RDI3 ;IF PREMATURELY DONE, STOP ; INI IN FDCDATA MOV M,A INX H DCR B ; JNZ RDI1 ;READ ANOTHER BYTE INTO CORE UNTIL DONE RDI2: IN FDCFLAG ;CHECK FLAGS RAR JNC RDI2 ;LOOP UNTIL READY RDI3: IN FDCSTAT ;NOW CHECK STATUS ANI 9CH RDWREND:EI POP D ;RESTORE REGS POP H POP B RET ; ; ACTUALLY DO WRITE OPERATION ; WR4FDC: PUSH B PUSH H ;SAVE REGS PUSH D MVI E,0A8H ;WRITE COMMAND (VALID FOR 1771, 179X) CALL INIT4FDC WRI1: IN FDCFLAG ;CHECK FLAGS RAR JC WRI3 ;IF PREMATURELY DONE, STOP ; OUTI MOV A,M OUT FDCDATA INX H DCR B JNZ WRI1 ;WRITE DATA FROM MEMORY TIL DONE WRI2: IN FDCFLAG ;CHECK FLAGS RAR JNC WRI2 ;LOOP TIL DONE WITH WHOLE OPERATION WRI3: IN FDCSTAT ;NOW CHECK ERROR STATUS ANI 0FCH JMP RDWREND ; ; SET THE TRACK, AND MOVE DISK ARM THERE ; SETTRK: MOV A,C STA DKTRACK ;STORE THE TRACK NUMBER SUB A CALL DSKSEL ;NOW SELECT THE DISK OUT FDCFLAG MOV A,C OUT FDCDATA ;TELL 1771 ABOUT TRACK WANTED LDA DKSECT OUT FDCSECT ;TELL IT ABOUT SECTOR WANTED PUSH H PUSH D LXI H,DRVTBL ;NOW, SEE WHERE DISK ARM IS NOW LDA DKNUMB MOV E,A ;LOOK UP IN TABLE MVI D,0 DAD D ;GET BYTE MOV A,M OUT FDCTRK ;TELL 1771 WHERE ARM IS NOW MOV A,M SUB C POP D ;NOW CAN RESTORE THE REGS POP H RZ ;IF ALREADY AT THAT TRACK, QUIT MVI A,12H-LARGESW*2+HDLOAD+VERIFY OUT FDCSTAT ;PERFORM THE SEEK THAT IS NEEDED SKI: IN FDCFLAG ;CHECK FLAGS RAR JNC SKI ;LOOP UNITL OPERATION DONE SEEKTST:IN FDCSTAT ;NOW CHECK ERROR STATUS ANI 98H RNZ ;ZERO IS ALL OK PUSH D MVI D,0 ;UPDATE TRACK TABLE LDA DKNUMB MOV E,A ;WITH NEW POSITION ; IF PERSCI = 1 CALL ADDSHFT ;PERSCI DRIVES HAVE ONLY ONE ARM MOV A,E XRI 01H MOV E,A CALL ADDSHFT POP D RET ENDIF ; ADDSHFT: ;IF NOT PERSCI, FALL THRU HERE PUSH H LXI H,DRVTBL ;GET CORRECT ADDRESS OF DISK DAD D LDA DKTRACK ;AND GET CURRENT TRACK NUMBER MOV M,A SUB A ;PUT IT IN POP H ; IF PERSCI = 0 POP D ;AND MISC CLEANUP ENDIF ; RET ; ; INITIALIZE THE 4FDC, BY SELECTING THE DISK AND ; TURNING ON THE DISK MOTORS, AND INITIALIZE THE ; REGISTERS FOR THE I/O OPERATION ; INIT4FDC: MVI A,80H ;FIRST, SELECT THE DISK CALL DSKSEL LHLD DKDMA ;INITIALIZE HL AND BC REGS FOR I/O LXI B,8000H+FDCDATA ;80H IS 128 BYTE SECTORS MOV D,A ;SAVE THE A REG DI ;DISABLE INTERRUPTS LDA DKSECT OUT FDCSECT ;SET THE SECTOR WANTED IN FDCFLAG CMA ;SEE IF HEAD IS LOADED ANI 20H JZ IN0 MVI A,04H ;THIS IS THE HEAD LOAD BIT IN0: ADD E ;ADD HEAD LOAD BIT TO COMMAND MOV E,A MOV A,D ;RESTORE THE A REG OUT FDCFLAG MOV A,E OUT FDCSTAT ;OUTPUT THE COMMAND RET ; ; FIGURE OUT WHICH DISK TO SELECT AND PUT BIT THERE ; DSKSEL: PUSH B ;FIRST, SAVE THIS, ITS NEEDED MOV C,A LDA DKNUMB ;GET DISK NUMBER 0-3 MOV B,A INR B ;ADD ONE SUB A STC ;SET A BIT IN TO BE SHIFTED SHFTBIT:RAL ;NOW, ROTATE BITS OVER ; DJNZ SHFTBIT DCR B JNZ SHFTBIT ; MOV B,A ;SAVE THE NEW VALUE XRA A ORI 20H+LARGESW*10H ;CONDITION 4FDC FOR MOTOR ON AND MAXI ORA B ORA C ;OR IN DRIVE SELECT AND COMMAND WANTED POP B RET ; ; GETS CONTROL ON A WARM START ; WBOOT: MVI A,01H ;FIRST, RESTORE DEFAULT BANKING OUT MBANKPT ; LXI SP,80H LDA DKNUMB STA CURDRV ;STORE SELECTED DRIVE ; STARTBOOT: MVI C,0 CALL SELDSK ;SELECT DRIVE A TO REBOOT CALL HOME LXI H,CCP-128 ;WILL INCREMENT BY 128 LATER SHLD DKDMA LXI B,44*256+1 ;SECTOR COUNT, FIRST SECTOR-1 RDSEC: MOV A,C ;C IS SECTOR NUMBER CPI 18+LARGESW*8 JZ NXTTRK LXI D,128 ;INCREMENT DMA ADDRESS LHLD DKDMA DAD D SHLD DKDMA ;STORE DMA ADDRESS INR C ;INCREMENT SECTOR NUMBER CALL SETSEC CALL READ ;READ THE DATA JNZ WBOOT ;ON READ FAILURE TRY AGAIN ; DJNZ RDSEC DCR B JNZ RDSEC JMP BOOT ;READ CCP AND BDOS; NXTTRK: LDA DKTRACK CPI 2-LARGESW JZ BOOT ;STOP AT TRACK 2 MOV C,A INR C ;SEEK NEXT TRACK IF NOT CALL SETTRK MVI C,0 JMP RDSEC ; ; GET HERE AFTER A COLD BOOT ; CBOOT: LXI SP,80H ;INITIALIZE STACK FOR ROUTINE LXI H,SIGNON CALL PRMSG ;PRINT MESSAGE BOOT: DI MVI A,0C3H ;SET UP PARAMETERS ON PAGE 0 STA 0 LXI H,BIOS+3 SHLD 1 SHLD 7*8+1 STA 5 LXI H,BDOS+6 SHLD 6 ; ; CROMEMCO INITIALIZATION HERE (READER, LIST NOT NEEDED) ; NOTE .. THE CONSOLE TUART IS INITIALIZED BY RDOS ; IF BIGIO = 1 MVI A,BAUDRATE OUT PBAUDPORT ENDIF ; IF SERIAL = 1 CALL L2INIT ENDIF ; LXI B,80H ;SET DEFAULT DMA ADDR CALL SETDMA ; LDA CURDRV ;RESELECT THE DRIVE THAT WAS ACTIVE MOV C,A ; BEFORE THE WARM START EI JMP CCP ; ; ERRMSG: DB 13,10,'?? ERROR ??',13,10,0 ; ;PRINT MESSAGE AT H,L UNTIL 0 ; PRMSG: MOV A,M ORA A ;ZERO? RZ MOV C,A ;GO PRINT CHAR CALL CONOUT INX H ;GET NEXT CHAR JMP PRMSG ; ; ;HARDWARE UART CONSOLE ROUTINES ; CONSTAT: IN CSTAT ;CHECK CONSOLE STATUS ANI 40H RZ ;ZERO MEANS NO INPUT BYTE READY MVI A,0FFH RET ;FF MEANS INPUT ; CONIN: CALL CONSTAT ;CHECK STATUS TIL GOT A BYTE JZ CONIN IN CONDATA ;READ THE BYTE AND KILL OFF 80H ANI 7FH ; ; THE FOLLOWING CODE REPLACES THE DEL KEY WITH A CTRL-U, ; SO THAT DEL MEANS LINE DELETE. USE BACKSPACE TO DELETE ; A SINGLE CHARACTER. ; ; CPI 127 ; DEL ; RNZ ; SUI 127-21 ; CTRL-U RET ; ; CONSOLE OUTPUT ROUTINE ; CONOUT: IN CSTAT ;FIRST, LOOP UNITL TRANSMITTER BUFFER ANI 80H ;IS EMPTY JZ CONOUT MOV A,C ;NOW, OUTPUT CHARACTER TO CONSOLE OUT CONDATA RET ; ; LIST STATUS CHECK FOR CP/M 2.2 ; LISTAT: IF BIGIO = 1 IN LFDCSTAT ;IF LIST TRANSMITTER IS BUSY, CMA ;THEN RETURN WITH ZERO ANI 20H RZ ORI 0FFH ;LIST TRANSMITTER BUFFER IS EMPTY RET ENDIF ; LIST: IF BIGIO = 1 AND SERIAL = 0 CALL LISTAT ;CHECK IF PRINTER BUSY JZ LIST MOV A,C ;NOW OUTPUT CHARACTER ; SET 7,A ORI 80H OUT LFDCDATA,A ;WITH HIGH STROBE ; RES 7,A ANI 7FH OUT LFDCDATA,A ;NOW LOW STROBE ; SET 7,A ORI 80H OUT LFDCDATA,A ;NOW HIGH STROBE AGAIN RET ENDIF ; IF SERIAL = 1 MOV A,C JMP L2OUT ENDIF ; PUNCH: IF BIGIO = 1 IN PFDCSTAT ;CHECK IF PUNCH BUFFER EMPTY ANI 80H JZ PUNCH ;LOOP UNTIL READY MOV A,C OUT PFDCDATA ;OUTPUT CHARACTER RET ENDIF ; READER: IF BIGIO = 1 IN RFDCSTAT ;SEE IF READER BUFFER FULL ANI 40H JZ READER ;LOOP UNTIL FULL IN RFDCDATA ;AND READ IT RET ENDIF ; IF BIGIO NE 1 XRA A,A ;DUMMY ROUTINE FOR LIST,PUNCH,READER RET ENDIF ; ; Serial Printer Initialization Routine ; L2INIT: MVI A,0FH ; SETUP THE 8250 OUT SMDMCT MVI A,83H ; SET DIVISOR REGISTER ACCESS OUT SLCTRL MVI A,80H ; SET THE DIVISOR TO 384=300 BAUD OUT SDATA MVI A,01H ; OUT SINTEN MVI A,03H ; SET DATA REGISTER ACCESS OUT SLCTRL MVI A,0H ; DISABLE INTERRUPT OUT SINTEN OUT SLSTAT ; RESET ERROR FLAGS RET ; ; Get Serial Printer Output Status ; Upon Exit: A = -1 (FFH) and Z-flag is reset if ready for char. ; A = 0 and Z-flag is set if not ready for character ; L2RDY: IN SSTATP ; GET LIST-OUT STATUS ANI STBE ; CHECK PRINTER TBE FLAG RZ ; PRINTER NOT READY FOR CHARACTER MVI A,-1 ; PRINTER READY FOR CHARACTER RET ; ; Serial Printer Output Routine ; Upon Entry: A contains the character to be output ; L2OUT: PUSH PSW ; SAVE CHARACTER FOR A MOMENT L2OT30: CALL L2RDY ; GET LIST-OUT STATUS JZ L2OT30 ; ZERO MEANS PRINTER BUSY POP PSW ; RESTORE CHARACTER OUT SDATA ; OUTPUT THE CHARACTER CPI LF ; CHECK FOR END OF LINE RNZ ; RETURN IF NOT LINE FEED CHARACTER MVI A,NULLS+1 ; IF LF, GET NUMBER OF NULLS L2RTN: DCR A ; CHECK FOR 0 NULLS AT TOP OF LOOP RZ ; RETURN IF ALL NULLS OUTPUT PUSH PSW ; SAVE NULLS COUNTER SUB A ; PRINT A SINGLE NULL CALL L2OUT ; CHARACTER (RECURSIVE) POP PSW ; RESTORE NULLS COUNTER JMP L2RTN ; LOOP TO PRINT NEXT NULL ; DKNUMB EQU 4 ;THIS IS WHERE THE DISK NUMBER IS ;KEPT IN NORMAL CP/M CURDRV DB 0 ;THE SAVE AREA FOR THE DEFAULT DRIVE DRVTBL DB 2-LARGESW,0,0,0 ;FOUR DRIVES MAX DKSECT DB 0 ;SECTOR NUMBER DKTRACK DB 0 ;TRACK NUMBER DKDMA DW 0 ;DMA ADDRESS TRKERCT DB 0 ;NUMBER OF TRACK ERRORS RTYCNT DB 0 ;NUMBER OF READ ERRORS ; ; FROM HERE ON, THE AREAS ARE INITIALIZED BY CP/M AS NEEDED ; DIRBUF DS 128 ;SAVE AREA FOR DISK DIRECTORY OPERATIONS ALV0 DS 32 ; IF NBRDRV > 1 ALV1 DS 32 ENDIF ; IF NBRDRV > 2 ALV2 DS 32 ENDIF ; IF NBRDRV > 3 ALV3 DS 32 ENDIF ; CSV0 DS 16 ; IF NBRDRV > 1 CSV1 DS 16 ENDIF ; IF NBRDRV > 2 CSV2 DS 16 ENDIF ; IF NBRDRV > 3 CSV3 DS 16 ENDIF ; ; Note: The last assembled byte of this module MUST NOT be a Define ; Storage (DS or DEFS) pseudo-op to assure proper operation with CDOSGEN END