home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
PROGRAMS
/
LIST
/
LIST.ASM
< prev
next >
Wrap
Assembly Source File
|
2000-06-30
|
13KB
|
596 lines
* 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 <CR> <LF> (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 <SP>; follow by <SP>
*
* 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 ; <CR>
LF equ 10 ; <LF>
BS equ 8 ; <BS>
TAB equ 9 ; <TAB>
BEL equ 7 ; <BEL>
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 <LF>
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 <CR> <LF>; DON'T CHANGE A
*
CRLF:
PUSH PSW ; SAVE A
MVI A,CR ; <CR>
CALL CHAR$OUT
MVI A,LF ; <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 <BS>
JZ PRINT$LINE$BS
CPI TAB ; PRINT <TAB>
JZ PRINT$LINE$TAB
CPI CR ; PROCESS EOL
JZ PRINT$LINE$CR
CPI ' ' ; DON'T OUTPUT LESS THAN <SP>
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 <BS>?
ORA A ; 0=NO
JZ PRINT$LINE$LOOP
MVI A,BS ; PRINT <BS>
CALL CHAR$OUT
DCR C ; COUNT DOWN
JMP PRINT$LINE$LOOP
PRINT$LINE$TAB:
MVI A,' ' ; PRINT <SP>
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 <LF>
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 <CR>
MOV A,M ; NEXT CHAR A <CR>?
ANI 7FH ; MASK OUT MSB
CPI CR
RZ
INX H ; NEXT CHAR A <CR>?
MOV A,M
ANI 7FH
DCX H
CPI CR
RZ
INX H ; 3RD CHAR A <CR>?
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 <LF>
DCX H ; BACK UP IN PREPARATION FOR <LF> ADVANCE
POP B ; RESTORE LINE COUNT
RET ; RETURN TO MAIN LOOP
*
* PRINT NUMBER IN HL AS UP TO 5 DECIMAL DIGITS FOLLOWED BY A <SP>
* AFFECT NO REGISTERS
*
PRINT$NUMBER:
PUSH H ! PUSH D ! PUSH B ! PUSH PSW
MVI A,0FFH
STA LDSP ; TURN OFF LEADING <SP> 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 <SP>
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 <SP>;
* 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 <SP>?
ORA A ; 0=NO
JZ PNUM3
MVI A,' ' ; PRINT <SP>
JMP PNUM4
PNUM3:
XRA A ; NO LEADING <SP>
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 <SP> 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