* PROGRAM NAME: LIST * AUTHOR: RICHARD CONN * VERSION: 1.0 * DATE: 10 FEB 81 * PREVIOUS VERSIONS: -None- VERS equ 10 ; Version Number * * * Section 0: Introduction to the LIST Program * * * LIST is a CP/M utility which displays a file on the user console in * paged mode. It prints 24 lines of text and pauses; typing a ^C at this point * returns to CP/M, while any other character continues. If a line is more than * LINE$LENGTH character long, it is broken and the line count is incremented * accordingly. * * * The structure of the LIST Program is as follows -- * * Section Functions/Routines * ------- ------------------ * * 0 Introduction and Documentation * * 1 Initialization of the LIST Program * Significant Labels are: * OPTION: Command Line option processing * OPTION$NUMBER: Set the Line Numbering Flag (/N) * DRIVE: Extract the disk drive letter from the command line * * 2 Mainline of the LIST Program * Significant Labels are: * LIST: Open files, perform tests, etc * LIST1: Start processing loop * LIST$LOOP: Main program loop * * 3 Support Utilties * Significant Routines are: * PRINT$ID: Print Program ID * TYP$COMP: Compare the 3-byte file type pted to by HL to * the FCB type; return w/Zero Set if match * PRINT$MESSAGE: Print string pted to by return address; * string ends in 0 * LOADER: Copy top of buffer down to front of buffer and * load up to 16K into buffer * LOADER1: Load the buffer pted to by HL for B blocks (max) * CRLF: Print (no regs affected) * CHAR$OUT: Print char in A; no regs affected * CHAR$IN: Input char in A; only PSW affected * PRINT$LINE: Print line pted to by HL on console; process * line numbering, tab expansion, line overflow fcts * PRINT$NUMBER: Print HL as up to 5 decimal digits with * leading ; follow by * * 4 Buffers * NUMBER$LINES equ 24 ; Number of lines/screen LINE$LENGTH equ 77 ; Maximum number of characters/line NUMBER$LENGTH equ 7 ; Number of characters in line number * * DEFINE MISCELLANEOUS CONSTANTS USED IN PROGRAM * OPT$CHAR equ '/' ; Option character CR equ 13 ; LF equ 10 ; BS equ 8 ; TAB equ 9 ; BEL equ 7 ; CPM equ 0 ; Warm Boot Address BUFF equ 80H ; CP/M Buffer FCB equ 5CH ; CP/M FCB WBADR equ 1 ; CP/M Warm Boot Address BDOS equ 5 ; CP/M BDOS Entry Point CTRLC equ 'C'-'@' ; ^C CTRLZ equ 'Z'-'@' ; ^Z * * * Section 1: Initialization of LIST Program * ORG 100H LXI H,BUFF ; SCAN FOR OPTION MOV A,M ; GET CHAR COUNT ADD L ; ADD TO HL MOV L,A MOV A,H ACI 0 MOV H,A INX H ; HL PTS TO CHAR AFTER LAST CHAR IN COMMAND LINE MVI M,0 ; STORE ENDING 0 LXI H,BUFF+1 ; PT TO 1ST CHAR PUSH H ; SAVE PTR FOR LATER XRA A ; A=0 STA NFLG ; TURN OFF LINE NUMBERING FLAG STA OVFL ; TURN OFF LINE LENGTH OVERFLOW FLAG OPTION: MOV A,M ; SCAN FOR OPTION ORA A ; DONE? JZ OPTION$DONE INX H ; PT TO NEXT CPI OPT$CHAR ; OPTION? JNZ OPTION MOV A,M ; GET OPTION LETTER CPI 'N' ; NUMBER LINES? JZ OPTION$NUMBER CALL PRINT$ID ; PRINT PROGRAM ID CALL PRINT$MESSAGE DB CR,LF,' The LIST command takes the following format --' DB CR,LF,' LIST d:filename.typ [/N]' DB CR,LF,' Only the "/N" option is available; this option prints' DB CR,LF,'line numbers in front of each line.' DB CR,LF,' The drive specification "d:" is optional.',0 JMP CPM * THIS OPTION TURNS ON THE LINE NUMBERING FLAG OPTION$NUMBER: MVI A,0FFH ; TURN ON FLAG STA NFLG JMP OPTION * DONE PROCESSING OPTIONS OPTION$DONE: POP H ; GET PTR TO COMMAND LINE DRIVE: MOV A,M ; SCAN FOR DRIVE NAME ORA A ; DONE? JZ LIST INX H ; PT TO NEXT CHAR CPI ' ' ; NON-SPACE? JZ DRIVE ; CONTINUE SCAN IF SO MOV A,M ; CHECK FOR COLON CPI ':' ; COLON MEANS DRIVE NAME PRECEEDS JNZ LIST DCX H ; PT TO DRIVE NAME MOV A,M ; GET IT SUI 'A' ; ADJUST FROM LETTER TO NUMBER (A=0,B=1,ETC) JC DRIVE$ERROR CPI 16 ; IN RANGE? JNC DRIVE$ERROR MOV E,A ; PLACE NUMBER IN E MVI C,14 ; SELECT DISK CALL BDOS JMP LIST DRIVE$ERROR: CALL PRINT$MESSAGE DB CR,LF,'ERROR: Invalid Drive Specification',0 JMP CPM * * * Section 2: Mainline of LIST Program * LIST: CALL PRINT$ID ; PRINT PROGRAM ID LXI H,COM$TYPE ; DON'T PRINT *.COM OR *.OBJ FILES CALL TYP$COMP ; COMPARE TYPES JZ TYPE$ERROR LXI H,OBJ$TYPE CALL TYP$COMP JNZ LIST0 TYPE$ERROR: CALL PRINT$MESSAGE DB CR,LF,'ERROR: Attempt to List COM or OBJ File',0 JMP CPM LIST0: LXI D,FCB ; TRY TO OPEN FILE MVI C,15 ; OPEN FILE CALL BDOS CPI 0FFH ; ERROR? JNZ LIST1 CALL PRINT$MESSAGE DB CR,LF,'ERROR: File Not Found',0 JMP CPM LIST1: CALL CRLF ; NEW LINE LXI H,BUFFER ; FILE BUFFER (16K) MVI B,8*16 ; LOAD 16K OF FILE (MAX) CALL LOADER1 ; LOAD BUFFER PTED TO BY HL FOR 16K LXI H,BUFFER ; PT TO FIRST CHARACTER LDA NFLG ; NUMBER LINES? ORA A ; SET FLAGS JZ LIST$LOOP PUSH H ; SAVE HL LXI H,0 ; SET FOR 1ST LINE NUMBER SHLD LINE$NUMBER POP H ; RESTORE HL * * MAIN LOOP FOR PRINTING LINES * LIST$LOOP: MVI C,NUMBER$LINES ; NUMBER OF LINES/SCREEN LIST$LOOP1: CALL PRINT$LINE ; PRINT ONE LINE CPI CTRLZ JZ CPM CPI LF ; LINE FEED? JNZ LIST$LOOP2 INX H ; PT TO CHAR AFTER MOV A,M ; GET POSSIBLE ^Z CPI CTRLZ ; ^Z IF SO JZ CPM LIST$LOOP2: DCR C ; COUNT DOWN JNZ LIST$LOOP3 CALL CHAR$IN ; WAIT FOR CHAR CALL CRLF ; NEW LINE CPI CTRLC ; ^C? JZ CPM ; ABORT IF SO JMP LIST$LOOP ; CONTINUE LIST$LOOP3: CALL CRLF ; NEW LINE JMP LIST$LOOP1 * * * Section 3: Support Utilities * * * THIS ROUTINE PRINTS THE PROGRAM ID * PRINT$ID: CALL PRINT$MESSAGE DB 'LIST Version ',VERS/10+'0','.',(VERS MOD 10)+'0',0 RET * * THIS ROUTINE COMPARES THE THREE BYTES PTED TO BY HL AGAINST THE TYPE * IN THE FCB; RETURNS W/ZERO SET IF MATCH * TYP$COMP: LXI D,FCB+9 ; PT TO TYPE IN FCB MVI B,3 ; 3 BYTES TYP$COMP$LOOP: LDAX D ; GET BYTE CMP M ; COMPARE RNZ INX H ; PT TO NEXT INX D DCR B JNZ TYP$COMP$LOOP RET * * PRINT MESSAGE PTED TO BY RETURN ADDRESS ENDING IN 0 * PRINT$MESSAGE: XTHL ; SAVE HL AND GET PTR PRINT$MESSAGE$LOOP: MOV A,M ; GET CHAR INX H ; PT TO NEXT ORA A ; DONE? JZ PRINT$MESSAGE$DONE CALL CHAR$OUT ; PRINT IT JMP PRINT$MESSAGE$LOOP PRINT$MESSAGE$DONE: XTHL ; RESTORE HL AND RETURN ADDRESS RET * * THIS ROUTINE LOADS THE FILE BUFFER * ENTRY POINT 'LOADER' COPIES THE END OF THE FILE BUFFER DOWN TO THE * BEGINNING AND LOADS THE REST OF THE BUFFER (UP TO 16K). * ENTRY POINT 'LOADER1' LOADS THE BUFFER POINTED TO BY HL FOR B-BLOCKS * (1 BLOCK = 128 BYTES). * LOADER: PUSH B ; SAVE BC LXI H,BUFFER$LAST ; PT TO LAST BLOCK LXI D,BUFFER ; PT TO 1ST BLOCK CALL MOVE$BLOCK ; MOVE BLOCK FROM HL TO DE CALL MOVE$BLOCK ; MOVE PAGE (2 BLOCKS) XCHG ; PT TO 2ND BLOCK MVI B,8*16-2 ; 2 BLOCKS LESS THAN 16K CALL LOADER1 ; LOAD BLOCKS POP B ; RESTORE BC RET LOADER1: CALL LOADER2 ; LOAD NEXT BLOCK RNZ ; DONE IF PAST EOF LXI D,BUFF ; PT TO BUFFER XCHG ; EXCHANGE PTRS CALL MOVE$BLOCK ; MOVE BLOCK LOADED INTO FILE BUFFER XCHG ; RESTORE PTRS DCR B ; COUNT DOWN JNZ LOADER1 RET * * LOAD BUFFER FROM DISK * LOADER2: PUSH H ! PUSH D ! PUSH B LXI D,FCB ; PT TO FILE NAME MVI C,20 ; READ BLOCK CALL BDOS ORA A ; SET FLAG POP B ! POP D ! POP H RET * * MOVE BLOCK (128 BYTES) FROM HL TO DE * MOVE$BLOCK: PUSH B ; SAVE BC MVI B,128 ; 128 BYTES MOVE$BLOCK$LOOP: MOV A,M ; GET BYTE STAX D ; PUT BYTE INX H ; PT TO NEXT INX D DCR B ; COUNT DOWN JNZ MOVE$BLOCK$LOOP POP B ; RESTORE BC RET * * OUTPUT ; DON'T CHANGE A * CRLF: PUSH PSW ; SAVE A MVI A,CR ; CALL CHAR$OUT MVI A,LF ; CALL CHAR$OUT POP PSW ; GET A RET * * CHARACTER OUTPUT ROUTINE * OUTPUT CHARACTER IN REG A TO CONSOLE * CHAR$OUT: PUSH H ! PUSH D ! PUSH B ! PUSH PSW MOV E,A ; CHAR IN E MVI C,2 ; OUTPUT TO CON: CALL BDOS POP PSW ! POP B ! POP D ! POP H RET * * CHARACTER INPUT ROUTINE * CHARACTER IS RETURNED IN REG A * CHAR$IN: PUSH H ! PUSH D ! PUSH B LXI H,CHAR$IN$RET ; PLACE RETURN ADDRESS ON STACK PUSH H LHLD WBADR ; INDEX INTO BIOS FOR NO ECHO MOV A,L ; ADD 6 FOR CONSOLE INPUT ROUTINE ADI 6 MOV L,A MOV A,H ACI 0 MOV H,A ; HL PTS TO ROUTINE PCHL ; "CALL" CONSOLE INPUT ROUTINE CHAR$IN$RET: POP B ! POP D ! POP H RET * * PRINT LINE PTED TO BY HL ON CONSOLE * PRINT$LINE: PUSH B ; SAVE LINE COUNT MVI C,0 ; SET CHAR COUNT LDA NFLG ; NUMBER LINE? ORA A ; 0=NO JZ PRINT$LINE1 LDA OVFL ; OVERFLOW FROM PREVIOUS LINE? ORA A ; 0=NO JNZ PRINT$LINE1 PUSH H ; SAVE PTR TO LINE LHLD LINE$NUMBER ; FETCH AND INCREMENT LINE NUMBER INX H SHLD LINE$NUMBER CALL PRINT$NUMBER ; PRINT LINE NUMBER POP H ; RESTORE PTR TO LINE PRINT$LINE1: XRA A ; TURN OFF OVERFLOW FLAG STA OVFL LXI D,BUFFER$LAST ; IN LAST BLOCK? MOV A,H ; CHECK AGAINST H CMP D JNZ PRINT$LINE$LOOP PUSH H ; SAVE PTR TO LINE (RELATIVE OFFSET IN L) CALL LOADER ; LOAD NEXT 16K LXI H,BUFFER ; PT TO 1ST BYTE OF BLOCK POP D ; GET RELATIVE OFFSET IN E MOV L,E ; RELATIVE OFFSET IN L -- CONTINUE PRINT$LINE$LOOP: MOV A,M ; GET CHAR FROM FILE INX H ; PT TO NEXT CHAR ANI 7FH ; MASK OUT MSB CPI CTRLZ ; PROCESS EOF JZ PRINT$LINE$CR CPI BS ; PROCESS JZ PRINT$LINE$BS CPI TAB ; PRINT JZ PRINT$LINE$TAB CPI CR ; PROCESS EOL JZ PRINT$LINE$CR CPI ' ' ; DON'T OUTPUT LESS THAN JC PRINT$LINE$LOOP CPI 7EH ; DON'T OUTPUT IF GREATER THAN OR EQUAL TO 7EH JNC PRINT$LINE$LOOP CALL CHAR$OUT ; PRINT CHAR INR C ; INCREMENT CHAR COUNT CALL OVFL$TEST ; CHECK FOR LINE OVERFLOW JMP PRINT$LINE$LOOP PRINT$LINE$BS: MOV A,C ; POSSIBLE TO ? ORA A ; 0=NO JZ PRINT$LINE$LOOP MVI A,BS ; PRINT CALL CHAR$OUT DCR C ; COUNT DOWN JMP PRINT$LINE$LOOP PRINT$LINE$TAB: MVI A,' ' ; PRINT CALL CHAR$OUT INR C ; INCREMENT COUNT CALL OVFL$TEST ; CHECK FOR LINE OVERFLOW MOV A,C ; MULTIPLE OF 8? ANI 7 ; MASK FOR 3 LSB JNZ PRINT$LINE$TAB JMP PRINT$LINE$LOOP PRINT$LINE$CR: MOV A,M ; GET POSSIBLE POP B ; RESTORE LINE COUNT RET * * TEST FOR LINE OVERFLOW AND PRINT OVERFLOW CHARS IF SO * OVFL$TEST: LDA NFLG ; NUMBERING LINES? ORA A ; 0=NO JZ OVFL$TEST1 LDA OVFL ; IN OVERFLOW? ORA A ; 0=NO JNZ OVFL$TEST1 * WE ARE ON A NUMBERED LINE MVI A,LINE$LENGTH ; GET LINE LENGTH SUI NUMBER$LENGTH ; SUBTRACT LENGTH OF LEADING NUMBER JMP OVFL$TEST2 * WE ARE NOT ON A NUMBERED LINE OVFL$TEST1: MVI A,LINE$LENGTH ; CHECK CHAR COUNT OVFL$TEST2: CMP C ; OK? RNZ * CHECK TO SEE IF ONE OF NEXT 3 CHARS IS A MOV A,M ; NEXT CHAR A ? ANI 7FH ; MASK OUT MSB CPI CR RZ INX H ; NEXT CHAR A ? MOV A,M ANI 7FH DCX H CPI CR RZ INX H ; 3RD CHAR A ? INX H MOV A,M ANI 7FH DCX H DCX H CPI CR RZ * NONE OF THEM ARE, SO OVERFLOW MVI A,0FFH ; SET OVERFLOW FLAG STA OVFL MVI A,' ' ; PRINT OVERFLOW CHARS CALL CHAR$OUT MVI A,'<' CALL CHAR$OUT POP D ; CLEAR STACK MVI A,LF ; FAKE A DCX H ; BACK UP IN PREPARATION FOR ADVANCE POP B ; RESTORE LINE COUNT RET ; RETURN TO MAIN LOOP * * PRINT NUMBER IN HL AS UP TO 5 DECIMAL DIGITS FOLLOWED BY A * AFFECT NO REGISTERS * PRINT$NUMBER: PUSH H ! PUSH D ! PUSH B ! PUSH PSW MVI A,0FFH STA LDSP ; TURN OFF LEADING FLAG LXI D,10000 ; DETERMINE 10,000'S COUNT CALL PNUM LXI D,1000 ; DETERMINE 1,000'S COUNT CALL PNUM LXI D,100 ; 100'S CALL PNUM LXI D,10 ; 10'S CALL PNUM MOV A,L ; 1'S ADI '0' ; CONVERT TO ASCII CALL CHAR$OUT ; PRINT IT MVI A,':' ; PRINT COLON CALL CHAR$OUT MVI A,' ' ; PRINT CALL CHAR$OUT POP PSW ! POP B ! POP D ! POP H RET * * PNUM IS A UTILITY TO SUBTRACT DE FROM HL UNTIL HL<0; PRINT NUMBER OF TIMES * SUBTRACTION WAS DONE; IF ZERO AND LEADING SPACE FLAG SET, PRINT ; * ELSE, PRINT DIGIT AND CLEAR LEADING SPACE FLAG * ON EXIT, HL=HL-DE * PNUM: MVI C,'0' ; SET DIGIT PNUM1: MOV A,L ; GET NUMBER SUB E MOV L,A MOV A,H SBB D MOV H,A ; HL=HL-DE JC PNUM2 INR C ; INCREMENT COUNT JMP PNUM1 PNUM2: MOV A,L ; ADD BACK IN SO HL IS AGAIN 0 OR POS ADD E MOV L,A MOV A,H ADC D MOV H,A ; HL RESTORED MOV A,C ; GET DIGIT CPI '0' ; ZERO? JNZ PNUM3 LDA LDSP ; LEADING ? ORA A ; 0=NO JZ PNUM3 MVI A,' ' ; PRINT JMP PNUM4 PNUM3: XRA A ; NO LEADING STA LDSP ; TURN OFF FLAG MOV A,C ; GET CHAR PNUM4: CALL CHAR$OUT RET * * * Section 4: Buffers * COM$TYPE: DB 'COM' ; FOR COMPARISON AGAINST COM FILE TYPE OBJ$TYPE: DB 'OBJ' ; FOR COMPARISON AGAINST OBJ FILE TYPE LDSP: DS 1 ; LEADING FLAG (0=NO) OVFL: DS 1 ; LINE LENGTH OVERFLOW FLAG (0=NO) NFLG: DS 1 ; LINE NUMBER FLAG (0=NO) LINE$NUMBER: DS 2 ; LINE NUMBER STORAGE BUFFER DS 60 ; STACK SPACE STACK EQU $ ORG $/256*256+256 BUFFER: DS 128*(16*8-2) ; FILE BUFFER SPACE BUFFER$LAST: DS 128*2 ; LAST BLOCK END