LIST NOCOND,NOGEN ; ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; 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 ; ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; PERSCISW: EQU 1 ;ONE IF PERSCI DRIVE LARGESW: EQU 1 OR PERSCISW ;ONE IF MAXI DRIVE NUMDRIVES: EQU 2 ;ONE TO FOUR (MUST BE SAME TYPE) BIGIOSW: EQU 1 ;ZERO IF NO LIST, NO PUNCH ; MEMSIZE: EQU 64 SERIAL: EQU 1 ;SERIAL PRINTER OPTION W/8250 MEMSTAT: EQU MEMSIZE-9 ;2400 HEX OFFSET BEGINADR: EQU MEMSTAT*1024 ;THIS IS THE START OF CPM MEMUNITS: EQU MEMSIZE-(MEMSIZE/10*10) MEMTENTH: EQU MEMSIZE/10 ; ORG 1600H+BEGINADR ; ; CROMEMCO 4FDC I/O ASSIGNMENTS ; CSTATPORT: EQU 00H ;3P + S, 4FDC, TU-UART, OR SCC CDATAPORT: EQU 01H IMODEPORT: EQU 02H ;(3P + S DOESNT HAVE THESE) IMASKPORT: EQU 03H PARLPORT: EQU 04H ;NOT USED ; STATPORT: EQU 30H ;4FDC OR CCS 2422 BOARD TRAKPORT: EQU 31H SECTPORT: EQU 32H DATAPORT: EQU 33H FLAGPORT: EQU 34H ; BANKPORT: EQU 40H ;MEMORY BANKING PORT ; ; 1771/179X EQUATES ; HEADLOAD: EQU 08H VERIFY: EQU 04H ; IF BIGIOSW = 1 ; ; OTHER DEFINITIONS (OPTIONAL) ; BAUDRATE: EQU 01H ;110 BAUD FOR READER ? RSTATPORT: EQU 10H RBAUDPORT: EQU RSTATPORT ;TU-UART BOARD FOR PAPER TAPE RDATAPORT: EQU RSTATPORT+1 PSTATPORT: EQU 10H PBAUDPORT: EQU PSTATPORT ;PAPER TAPE PUNCH ALSO PDATAPORT: EQU PSTATPORT+1 LSTATPORT: EQU 54H ;PARALLEL PRINTER POARD LDATAPORT: EQU LSTATPORT 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 ; ; CPM ENTRY POINTS CPMB: EQU 00H+BEGINADR BDOS: EQU 806H+BEGINADR ; ; JUMP VECTOR JP CBOOT EBOOT: JP WBOOT JP CONSTAT JP CONIN JP CONOUT JP LIST JP PUNCH JP READER JP HOME JP SELDSK JP SETTRK JP SETSEC JP SETDMA JP READ JP WRITE JP LISTAT JP SECTRAN ; ; CP/M 2.2 DISK CONTROL BLOCKS ; DPBASE: DW XLT0,0000 DW 0000,0000 DW DIRBUF,DPB0 DW CSV0,ALV0 ; IF NUMDRIVES > 1 DW XLT0,0000 DW 0000,0000 DW DIRBUF,DPB0 DW CSV1,ALV1 ENDIF ; IF NUMDRIVES > 2 DW XLT0,0000 DW 0000,0000 DW DIRBUF,DPB0 DW CSV2,ALV2 ENDIF ; IF NUMDRIVES > 3 DW XLT0,0000 DW 0000,0000 DW DIRBUF,DPB0 DW CSV3,ALV3 ENDIF ; DPB0: ; ; disk parameter block, common to all disks ; this implements a "standard" CP/M directory ; with 64 entries, and 1K blocks. ; DW 18+LARGESW*8 ;sectors per track DB 3 ;block shift factor DB 7 ;block mask DB 0 ;null mask DW 81+LARGESW*161 ;disk size-1 DW 63 ;directory max DB 192 ;alloc 0 DB 0 ;alloc 1 DW 16 ;check size DW 3-LARGESW ;track offset ; XLT0: ; ; sector translate vector ; IF LARGESW EQ 0 DB 1,6,11,16 ;sectors 1,2,3,4 DB 3,8,13,18 ;sectors 5,6,7,8 DB 5,10,15,2 ;sectors 9,10,11,12 DB 7,12,17,4 ;sectors 13,14,15,16 DB 9,14 ;sectors 17,18 ENDIF ; ; IF LARGESW EQ 1 DB 1,7,13,19 ;sectors 1,2,3,4 DB 25,5,11,17 ;sectors 5,6,7,8 DB 23,3,9,15 ;sectors 9,10,11,12 DB 21,2,8,14 ;sectors 13,14,15,16 DB 20,26,6,12 ;sectors 17,18,19,20 DB 18,24,4,10 ;sectors 21,22,23,24 DB 16,22 ;sectors 25,26 ENDIF ; ; SIGNON: DEFB 26,13,10,10 DEFB MEMTENTH+30H DEFB MEMUNITS+30H DEFB 'k CP/M version 2.2' DEFB 13,10,0 ; SELDSK: ; LD HL,0 LD A,C ;C CONTAINS REQUESTED DRIVE NO. CP NUMDRIVES RET NC ;IGNORE IF TOO HIGH LD (DKNUMB),A LD L,C ;L=disk number 0,1,2,3 ADD HL,HL ;*2 ADD HL,HL ;*4 ADD HL,HL ;*8 ADD HL,HL ;*16 (size of each header) LD DE,DPBASE ADD HL,DE ;HL=.dpbase(diskno*16) RET ; SECTRAN: ;TRANSLATE SECTOR IN C USING TABLE AT DE LD B,0 EX DE,HL ;TABLE ADDR TO HL ADD HL,BC ;GET ADDRESS LD L,(HL) ;GET BYTE LD H,0 ;ANSWER IN HL RET ; SETSEC: LD A,C ;JUST SAVE SECTOR NUMBER LD (DKSECT),A RET ; SETDMA: LD (DKDMA),BC ;JUST SAVE I/O ADDRESS RET ; ; ; ERROR CHECKING READ AND WRITE RTNS FOR ; CROMEMCO CBIOS ; READ: CALL CLEAR RETRYREAD: CALL READ4FDC ;READ SECTOR RET Z ;SUCCESSFUL READ. RETURN CALL ERROR ;INCREMENT RETRYCOUNT JR NZ,RETRYREAD ;RETRY 20 TIMES OR 01H ;CP/M CONVENTION FOR PERMANENT ERROR RET ; CLEAR: XOR A LD (TRKERCNT),A ;ZERO OUT TRACK ERROR COUNTER LD (RETRYCOUNT),A ;ZERO OUT CRC ERROR COUNTER RET ; WRITE: CALL CLEAR RETRYWRITE: CALL WRIT4FDC ;WRITE SECTOR RET Z ;SUCCESSFUL WRITE. RETURN CALL ERROR ;INCREMENT RETRYCOUNT JR NZ,RETRYWRITE ;RETRY 20 TIMES OR 01H ;CP/M CONVENTION FOR PERMANENT ERROR RET ; ERROR: PUSH HL AND 10H ;CHECK FOR NRF JR NZ,TRACKERROR LD HL,RETRYCOUNT INC (HL) ;INCREMENT RETRYCOUNT LD A,(HL) POP HL SUB 20 ;20 TRIES? RET ; TRACKERROR: LD HL,TRKERCNT INC (HL) ;INCREMENT NO OF TRACK ERRORS LD A,(HL) SUB 10D ;ALLOW ONLY 10 TRACK ERRORS POP HL RET Z ;IF >10, RETURN A FAILURE PUSH BC CALL HOME ;HOME THE HEAD LD A,(DKTRACK) LD C,A ;GET TRACK IN C CALL SETTRK ;RESEEK TO CORRECT TRACK POP BC OR 0FFH ;RETRY RET ; ; RESTORE THE DISK TO TRACK ZERO ; HOME: SUB A,A ;ZERO OUT TRACK COUNTER LD (DKTRACK),A CALL DISKSELECT ;NOW SELECT THE DISK OUT FLAGPORT,A LD A,02H+HEADLOAD+VERIFY ;USE SLOW STEPPING SPEED FOR ALL DISKS OUT STATPORT,A RSTI: IN A,FLAGPORT ;NOW CHECK STATUS RRA JR NC,RSTI ;LOOP BACK UNTIL DONE JP SEEKTEST ; ; HERE, ACTUALLY DO THE READ OPERATION ; READ4FDC: PUSH BC PUSH HL ;FIRST, SAVE REGS PUSH DE LD E,88H ;READ COMMAND (VALID FOR 1771, 179X) CALL INIT4FDC RDI1: IN A,FLAGPORT ;NOW CHECK FLAGS RRA JR C,RDI3 ;IF PREMATURELY DONE, STOP INI JR NZ,RDI1 ;READ ANOTHER BYTE INTO CORE UNTIL DONE RDI2: IN A,FLAGPORT ;CHECK FLAGS RRA JR NC,RDI2 ;LOOP UNTIL READY RDI3: IN A,STATPORT ;NOW CHECK STATUS AND A,9CH RDWREND: EI POP DE ;RESTORE REGS POP HL POP BC RET ; ; ACTUALLY DO WRITE OPERATION ; WRIT4FDC: PUSH BC PUSH HL ;SAVE REGS PUSH DE LD E,0A8H ;WRITE COMMAND (VALID FOR 1771, 179X) CALL INIT4FDC WRI1: IN A,FLAGPORT ;CHECK FLAGS RRA JR C,WRI3 ;IF PREMATURELY DONE, STOP OUTI JR NZ,WRI1 ;WRITE DATA FROM MEMORY TIL DONE WRI2: IN A,FLAGPORT ;CHECK FLAGS RRA JR NC,WRI2 ;LOOP TIL DONE WITH WHOLE OPERATION WRI3: IN A,STATPORT ;NOW CHECK ERROR STATUS AND A,0FCH JR RDWREND ; ; SET THE TRACK, AND MOVE DISK ARM THERE ; SETTRK: LD A,C LD (DKTRACK),A ;STORE THE TRACK NUMBER SUB A,A CALL DISKSELECT ;NOW SELECT THE DISK OUT FLAGPORT,A LD A,C OUT DATAPORT,A ;TELL 1771 ABOUT TRACK WANTED LD A,(DKSECT) OUT SECTPORT,A ;TELL IT ABOUT SECTOR WANTED PUSH HL PUSH DE LD HL,LOGINTAB ;NOW, SEE WHERE DISK ARM IS NOW LD A,(DKNUMB) LD E,A ;LOOK UP IN TABLE LD D,0 ADD HL,DE ;GET BYTE LD A,(HL) OUT TRAKPORT,A ;TELL 1771 WHERE ARM IS NOW LD A,(HL) SUB A,C POP DE ;NOW CAN RESTORE THE REGS POP HL RET Z ;IF ALREADY AT THAT TRACK, QUIT LD A,12H-LARGESW*2+HEADLOAD+VERIFY OUT STATPORT,A ;PERFORM THE SEEK THAT IS NEEDED SKI: IN A,FLAGPORT ;CHECK FLAGS RRA JR NC,SKI ;LOOP UNITL OPERATION DONE SEEKTEST: IN A,STATPORT ;NOW CHECK ERROR STATUS AND A,98H RET NZ ;ZERO IS ALL OK PUSH DE LD D,0 ;UPDATE TRACK TABLE LD A,(DKNUMB) LD E,A ;WITH NEW POSITION ; IF PERSCISW = 1 CALL ADDSHIFT ;PERSCI DRIVES HAVE ONLY ONE ARM LD A,E XOR 1 LD E,A CALL ADDSHIFT POP DE RET ENDIF ; ADDSHIFT: ;IF NOT PERSCI, FALL THRU HERE PUSH HL LD HL,LOGINTAB ;GET CORRECT ADDRESS OF DISK ADD HL,DE LD A,(DKTRACK) ;AND GET CURRENT TRACK NUMBER LD (HL),A SUB A,A ;PUT IT IN POP HL IF PERSCISW = 0 POP DE ;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: LD A,80H ;FIRST, SELECT THE DISK CALL DISKSELECT LD HL,(DKDMA) ;INITIALIZE HL AND BC REGS FOR I/O LD BC,8000H+DATAPORT ;80H IS 128 BYTE SECTORS LD D,A ;SAVE THE A REG DI ;DISABLE INTERRUPTS LD A,(DKSECT) OUT SECTPORT,A ;SET THE SECTOR WANTED IN A,FLAGPORT CPL ;SEE IF HEAD IS LOADED AND A,20H JR Z,IN0 LD A,04 ;THIS IS THE HEAD LOAD BIT IN0: ADD A,E ;ADD HEAD LOAD BIT TO COMMAND LD E,A LD A,D ;RESTORE THE A REG OUT FLAGPORT,A LD A,E OUT STATPORT,A ;OUTPUT THE COMMAND RET ; ; FIGURE OUT WHICH DISK TO SELECT AND PUT BIT THERE ; DISKSELECT: PUSH BC ;FIRST, SAVE THIS, ITS NEEDED LD C,A LD A,(DKNUMB) ;GET DISK NUMBER 0-3 LD B,A INC B ;ADD ONE SUB A,A SCF ;SET A BIT IN TO BE SHIFTED SHIFTBIT: RLA ;NOW, ROTATE BITS OVER DJNZ SHIFTBIT LD B,A ;SAVE THE NEW VALUE XOR A OR A,20H+LARGESW*10H ;CONDITION 4FDC FOR MOTOR ON AND MAXI OR A,B OR A,C ;OR IN DRIVE SELECT AND COMMAND WANTED POP BC RET ; ; GETS CONTROL ON A WARM START ; WBOOT: LD A,01H ;FIRST, RESTORE DEFAULT BANKING OUT BANKPORT,A ; LD SP,80H LD A,(DKNUMB) LD (CURRDRIVE),A ;STORE SELECTED DRIVE ; STARTBOOT: LD C,0 CALL SELDSK ;SELECT DRIVE A TO REBOOT CALL HOME LD HL,CPMB-128 ;WILL INCREMENT BY 128 LATER LD (DKDMA),HL LD BC,44*256+1 ;SECTOR COUNT, FIRST SECTOR-1 RDSEC: LD A,C ;C IS SECTOR NUMBER CP 18+LARGESW*8 JR Z,NXTTRK LD DE,128 ;INCREMENT DMA ADDRESS LD HL,(DKDMA) ADD HL,DE LD (DKDMA),HL ;STORE DMA ADDRESS INC C ;INCREMENT SECTOR NUMBER CALL SETSEC CALL READ ;READ THE DATA JR NZ,WBOOT ;ON READ FAILURE TRY AGAIN DJNZ RDSEC JR BOOT ;READ CCP AND BDOS; NXTTRK: LD A,(DKTRACK) CP 2-LARGESW JR Z,BOOT ;STOP AT TRACK 2 LD C,A INC C ;SEEK NEXT TRACK IF NOT CALL SETTRK LD C,0 JR RDSEC ; ; CBOOT: ;AFTER COLD BOOT LD SP,80H ;INITIALIZE STACK FOR ROUTINE LD HL,SIGNON CALL PRMSG ;PRINT MESSAGE BOOT: ;GETS CONTROL AFTER COLD OR WARM BOOT DI LD A,0C3H ;SET UP PARAMETERS ON PAGE 0 LD (0),A LD HL,EBOOT LD (1),HL LD (7*8+1),HL LD (5),A LD HL,BDOS LD (6),HL ; ; CROMEMCO INITIALIZATION HERE (READER, LIST NOT NEEDED) ; NOTE .. THE CONSOLE TUART IS INITIALIZED BY RDOS ; IF BIGIOSW = 1 LD A,BAUDRATE OUT PBAUDPORT,A ENDIF ; IF SERIAL = 1 CALL L2INIT ENDIF ; LD BC,80H ;SET DEFAULT DMA ADDR CALL SETDMA ; LD A,(CURRDRIVE) ;RESELECT THE DRIVE THAT WAS ACTIVE LD C,A ; BEFORE THE WARM START EI JP CPMB ; ; ERRMSG: DEFB 13,10,'?? ERROR ??',13,10,0 ; PRMSG: ;PRINT MESSAGE AT H,L UNTIL 0 LD A,(HL) OR A ;ZERO? RET Z LD C,A ;GO PRINT CHAR CALL CONOUT INC HL ;GET NEXT CHAR JR PRMSG ; ; ;HARDWARE UART CONSOLE ROUTINES ; CONSTAT: IN A,CSTATPORT ;CHECK CONSOLE STATUS AND 40H RET Z ;ZERO MEANS NO INPUT BYTE READY LD A,0FFH RET ;FF MEANS INPUT ; CONIN: CALL CONSTAT ;CHECK STATUS TIL GOT A BYTE JR Z,CONIN IN A,CDATAPORT ;READ THE BYTE AND KILL OFF 80H AND 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. ; ; CP 127 ; DEL ; RET NZ ; SUB 127-21 ; CTRL-U RET ; ; CONSOLE OUTPUT ROUTINE ; CONOUT: IN A,CSTATPORT ;FIRST, LOOP UNITL TRANSMITTER BUFFER AND 80H ;IS EMPTY JR Z,CONOUT LD A,C ;NOW, OUTPUT CHARACTER TO CONSOLE OUT CDATAPORT,A RET ; ; LIST STATUS CHECK FOR CP/M 2.2 ; LISTAT: IF BIGIOSW = 1 IN A,LSTATPORT ;IF LIST TRANSMITTER IS BUSY, CPL ;THEN RETURN WITH ZERO AND 20H RET Z OR A,0FFH ;LIST TRANSMITTER BUFFER IS EMPTY RET ENDIF ; LIST: IF BIGIOSW = 1 AND SERIAL = 0 CALL LISTAT ;CHECK IF PRINTER BUSY JR Z,LIST LD A,C ;NOW OUTPUT CHARACTER SET 7,A OUT LDATAPORT,A ;WITH HIGH STROBE RES 7,A OUT LDATAPORT,A ;NOW LOW STROBE SET 7,A OUT LDATAPORT,A ;NOW HIGH STROBE AGAIN RET ENDIF ; IF SERIAL = 1 LD A,C JP L2OUT ENDIF ; PUNCH: IF BIGIOSW = 1 IN A,PSTATPORT ;CHECK IF PUNCH BUFFER EMPTY AND 80H JR Z,PUNCH ;LOOP UNTIL READY LD A,C OUT PDATAPORT,A ;OUTPUT CHARACTER RET ENDIF ; READER: IF BIGIOSW = 1 IN A,RSTATPORT ;SEE IF READER BUFFER FULL AND 40H JR Z,READER ;LOOP UNTIL FULL IN A,RDATAPORT ;AND READ IT RET ENDIF ; IF BIGIOSW NE 1 XOR A,A ;DUMMY ROUTINE FOR LIST,PUNCH,READER RET ENDIF ; Serial Printer Initialization Routine L2INIT: LD A,0FH ; SETUP THE 8250 OUT SMDMCT,A LD A,83H ; SET DIVISOR REGISTER ACCESS OUT SLCTRL,A LD A,80H ; SET THE DIVISOR TO 384=300 BAUD OUT SDATA,A LD A,01H ; OUT SINTEN,A LD A,03H ; SET DATA REGISTER ACCESS OUT SLCTRL,A LD A,0H ; DISABLE INTERRUPT OUT SINTEN,A OUT SLSTAT,A ; 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 A,SSTATP ; GET LIST-OUT STATUS AND STBE ; CHECK PRINTER TBE FLAG RET Z ; PRINTER NOT READY FOR CHARACTER LD A,-1 ; PRINTER READY FOR CHARACTER RET ; Serial Printer Output Routine ; Upon Entry: A contains the character to be output L2OUT: PUSH AF ; SAVE CHARACTER FOR A MOMENT L2OT30: CALL L2RDY ; GET LIST-OUT STATUS JR Z,L2OT30 ; ZERO MEANS PRINTER BUSY POP AF ; RESTORE CHARACTER OUT SDATA,A ; OUTPUT THE CHARACTER CP LF ; CHECK FOR END OF LINE RET NZ ; RETURN IF NOT LINE FEED CHARACTER LD A,NULLS+1 ; IF LF, GET NUMBER OF NULLS L2RTN: DEC A ; CHECK FOR 0 NULLS AT TOP OF LOOP RET Z ; RETURN IF ALL NULLS OUTPUT PUSH AF ; SAVE NULLS COUNTER SUB A ; PRINT A SINGLE NULL CALL L2OUT ; CHARACTER (RECURSIVE) POP AF ; RESTORE NULLS COUNTER JR L2RTN ; LOOP TO PRINT NEXT NULL ; DKNUMB: EQU 4 ;THIS IS WHERE THE DISK NUMBER IS ; ;KEPT IN NORMAL CP/M CURRDRIVE: DB 0 ;THE SAVE AREA FOR THE DEFAULT DRIVE LOGINTAB: DB 2-LARGESW,0,0,0 ;FOUR DRIVES MAX DKSECT: DB 0 ;SECTOR NUMBER DKTRACK: DB 0 ;TRACK NUMBER DKDMA: DW 0 ;DMA ADDRESS TRKERCNT: DB 0 ;NUMBER OF TRACK ERRORS RETRYCOUNT: 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 NUMDRIVES > 1 ALV1: DS 32 ENDIF ; IF NUMDRIVES > 2 ALV2: DS 32 ENDIF ; IF NUMDRIVES > 3 ALV3: DS 32 ENDIF ; CSV0: DS 16 ; IF NUMDRIVES > 1 CSV1: DS 16 ENDIF ; IF NUMDRIVES > 2 CSV2: DS 16 ENDIF ; IF NUMDRIVES > 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