; THIS PROGRAM PRINTS THE LAST FEW LINES OF A TEXT FILE. ; ; USAGE: TAIL -n filename ; -n is optional, limits output to n lines (20 default) ; ; BY JON DART, DEPT. OF ANTHROPOLOGY, UCSD C-001, LA JOLLA, CA 92093 ; ; VERSION 1.1: 15-MAR-85 ; VERSION 1.0: 15-JAN-85 ; BUFSIZ EQU 64 ;SIZE OF BUFFER IN RECORDS ; CP/M SYSTEM DEFINITIONS: BOOT EQU 0 BDOS EQU 0005H DEFFCB EQU 005CH DEFBUF EQU 0080H FCBSIZ EQU 36 SECSIZ EQU 128 R0 EQU 33 R2 EQU 35 CONOUT EQU 2 PRTSTR EQU 09H VERS EQU 0CH FOPEN EQU 0FH FCLOSE EQU 10H SETDMA EQU 1AH RDRAND EQU 21H CFS EQU 23H ; ASCII CHARACTERS: LF EQU 0AH CR EQU 0DH TAB EQU 09H SPACE EQU 20H CTRL$Z EQU 26 ORG 100H TAIL EQU $ LXI H,0 DAD SP SHLD OLDSTK ;SAVE OLD STACK POINTER LXI SP,STACK CALL CRLF MVI C,VERS CALL BDOS MOV A,L CPI 20H JNC VERSOK ;IF NOT CP/M 2.2 LXI D,MSG0 MVI C,PRTSTR CALL BDOS JMP EXIT2 VERSOK: LXI H,FCB MVI B,FCBSIZ CALL ZERO ;ZERO FCB LXI H,DEFBUF ;GET BYTE COUNT FOR COMMAND LINE MOV A,M ANA A JZ NONAME ;IF NO NAME PUSH H MOV E,M MVI D,0 DAD D INX H MVI M,0 ;PUT 0 BYTE AT END OF COMMAND LINE POP H INX H CALL SKIPS ;SKIP SPACES JC NONAME ;IF END OF LINE CPI '-' JNZ NONUM ;IF NO NUMBER SPECIFIED INX H MVI C,0 PUSH H CD: MOV A,M ;HAVE NUMBER, COUNT NUMBER OF DIGITS CPI '0' JC NODGT CPI '9'+1 JNC NODGT INX H INR C JMP CD NODGT: SHLD NUMEND ;SAVE ADDRESS AFTER NUMBER POP H CALL DTOBIN ;CONVERT NUMBER TO BINARY JC NONAME ;IF ERROR XCHG INX H SHLD NUMLIN ;STORE AS # LINES LHLD NUMEND ;POINT TO 1 PAST NUMBER CALL SKIPS JC NONAME NONUM: LXI D,FCB CALL PACK ;MOVE NAME INTO FCB JMP NAMEOK NONAME: LXI D,MSG1 MVI C,PRTSTR CALL BDOS JMP EXIT2 NAMEOK: LXI D,FCB MVI C,FOPEN PUSH D CALL BDOS ;TRY TO OPEN FILE POP D CPI 0FFH JNZ OPENOK ;IF OK BADOPN: XCHG LXI D,FILNAM CALL FCOPY ;COPY FILE NAME INTO MESSAGE LXI D,MSG2 MVI C,PRTSTR CALL BDOS ;PRINT MESSAGE JMP EXIT SKIPS1: INX H SKIPS: MOV A,M ANA A STC RZ CPI SPACE JZ SKIPS1 CPI TAB JZ SKIPS1 ANA A RET ; COPY FILE NAME FROM COMMAND LINE TO FCB ; ENTRY: (HL) POINTS TO FILE NAME ; (DE) POINTS TO FCB ; PACK: INX H MOV A,M CPI ':' DCX H JZ HAVED ;IF DRIVE SPECIFIED XRA A STAX D ;ELSE PUT 0 IN 1ST BYTE JMP PACK2 HAVED: MOV A,M SUI 'A'-1 STAX D INX H INX H PACK2: INX D PUSH H PUSH D XCHG MVI B,11 MVI A,SPACE CALL FILL ;FILL NAME FIELD WITH SPACES POP D POP H MVI C,8 PACK3: MOV A,M ;COPY NAME ANA A RZ CPI SPACE RZ INX H CPI '.' JZ PACK4 STAX D INX D DCR C JNZ PACK3 MOV A,M ;15-MAR-85 ANA A ;15-MAR-85 RZ ;15-MAR-85 INX H ;FIXES BUG ;15-MAR-85 JMP PACK5 PACK4: XCHG MOV B,C MVI A,SPACE CALL FILL XCHG PACK5: MVI C,3 ;COPY EXTENSION PACK6: MOV A,M ANA A RZ CPI SPACE RZ STAX D INX H INX D DCR C JNZ PACK6 RET ZERO: XRA A FILL: MOV M,A INX H DCR B JNZ FILL RET OPENOK EQU $ LXI D,FCB MVI C,CFS CALL BDOS ;COMPUTE FILE SIZE LHLD FCB+R0 ;GET SIZE MOV A,L ORA H JZ EXIT ;IF 0 SHLD FILSIZ ;SAVE SIZE LXI D,BUFSIZ CALL SUBHLDE ;SUBTRACT BUFFER SIZE JC SMALL ;IF FILE SIZE < BUFFER SIZE PUSH H LXI H,BUFSIZ ;SET BUFFER SIZE SHLD NREAD ;AS NUMBER OF RECORDS TO READ POP H JMP GOTSIZ SMALL: LHLD FILSIZ ;SET FILE SIZE SHLD NREAD ;AS NUMBER OF RECORDS TO READ LXI H,0 ;START FROM RECORD 0 GOTSIZ: SHLD FCB+R0 ;SET RANDOM RECORD BYTES CALL FILBUF ;FILL BUFFER FROM DISK LHLD NREAD DCX H PUSH H POP B LXI D,SECSIZ LXI H,BUFFER MLOOP: MOV A,B ;COMPUTE ADDRESS OF START OF LAST SECTOR ORA C JZ MDONE DAD D DCX B JMP MLOOP MDONE: MVI C,SECSIZ FCZ: MOV A,M ;SEARCH FOR ^Z CPI CTRL$Z JZ FOUND INX H DCR C JNZ FCZ FOUND: SHLD ENDTXT ;SAVE END OF TEXT ADDRESS LXI D,BUFFER DCX H ;POINT TO CHAR. BEFORE ^Z PUSH H LHLD NUMLIN PUSH H POP B ;LINE COUNT IN (BC) POP H SRC: CALL COMPRP JZ BEGIN ;IF REACHED START OF BUFFER MOV A,M ;GET BYTE DCX H CPI LF JZ GOTLF ;IF LF CPI CR JNZ SRC ;IF NOT CR GOTCR: DCX B ;FOUND CR, COUNT A LINE MOV A,M ;GET PREVIOUS CHAR. CPI LF ;IF LF, JNZ NOSKIP DCX H ;SKIP IT NOSKIP: MOV A,B ;GET LINE COUNT ORA C ;MORE TO GO? JNZ SRC ;YES, LOOP INX H ;START OF RIGHT LINE FOUND JMP BEGIN ;GO PRINT GOTLF: DCX B ;COME HERE IF FOUND LF MOV A,M ;IF PREVIOUS CHAR. = CR CPI CR JNZ NOSKIP DCX H ;SKIP IT JMP NOSKIP BEGIN: PUSH H LHLD ENDTXT XCHG ;PUT END OF BUFFER ADDR. IN (DE) POP H SHOW: CALL COMPRP ;DISPLAY TEXT JZ EXIT ;UNTIL END OF BUFFER MOV A,M CALL COUT INX H JMP SHOW ; FILBUF - FILL BUFFER FROM FILE ; ; ENTRY: NONE ; ; EXIT: 'C'=1 IF ERROR ; FILBUF EQU $ LXI D,BUFFER ;BUFFER ADDRESS IN (DE) LHLD NREAD PUSH H POP B ;(BC) = COUNT OF SECTORS TO READ READ11: PUSH D PUSH B MVI C,SETDMA CALL BDOS ;SET DMA ADDRESS LXI D,FCB MVI C,RDRAND CALL BDOS ;READ A SECTOR ANA A POP B POP D JNZ READ12 ;QUIT IF END OF FILE DCX B ;COUNT # SECTORS READ XCHG LXI D,SECSIZ DAD D ;UPDATE BUFFER ADDRESS XCHG LHLD FCB+R0 INX H ;ADVANCE FCB SHLD FCB+R0 ;TO NEXT SECTOR MOV A,C ;GET # SECTORS TO READ ORA B JNZ READ11 ;LOOP TILL DONE READ12: ANA A ;CLEAR CARRY RET EXIT: LXI D,FCB ;CLOSE FILE MVI C,FCLOSE CALL BDOS EXIT2: LHLD OLDSTK SPHL RET ;RETURN TO CCP FCOPY: MOV A,M INX H ANA A JZ NODRV ADI 'A'-1 STAX D INX D MVI A,':' STAX D INX D NODRV: MVI B,11 FCOPY1: MOV A,M CPI SPACE JZ NOCOPY STAX D INX D NOCOPY: INX H DCR B RZ MOV A,B CPI 3 JNZ FCOPY1 MVI A,'.' STAX D INX D JMP FCOPY1 ; UTILITY SUBROUTINES: ; DTOBIN - CONVERT A 5-DIGIT (MAX.) UNSIGNED DECIMAL NUMBER INTO A ; BINARY VALUE. ; ; ENTRY: (C) = NO. OF DIGITS ; (HL) POINTS TO CHAR. STRING (MSD FIRST) ; ; EXIT: (DE) = CONVERTED NUMBER ; 'C' FLAG SET IF OVERFLOW (>64384) OR ERROR ; ; USES: ALL 8080 REGS. ; DGTPTR: DS 2 DTOBIN: XRA A MOV D,A MOV E,A ;CLEAR (DE) - SERVES AS ACCUMULATOR CMP C ;MAKE SURE (C) NOT ZERO RZ PUSH H DTOB: SHLD DGTPTR ;SAVE (HL) AS DIGIT POINTER MOV A,M ;GET DIGIT POP H CALL VALDGT ;CHECK FOR VALID DECIMAL DIGIT RC ;RETURN WITH ERROR IF NOT ANI 0FH ;CONVERT ASCII DIGIT TO BINARY XCHG ;SAVE SUM IN (HL) MOV E,A ;MOVE DIGIT TO (DE) MVI D,0 MOV B,C DCR B ;PUT COUNT IN (B) = DIGIT COUNT -1 JZ DTOB$1 ;IF =0, SKIP X10 ROUTINE DEX10: PUSH H CALL MU10 ;MULTIPLY (DE) BY 10 XCHG POP H RC ;(B) TIMES - RETURN ON OVERFLOW DCR B JNZ DEX10 DTOB$1: DAD D ;ADD PRODUCT TO RESULT RC ;RETURN ON OVERFLOW XCHG ;SAVE NEW SUM DCR C RZ ;FINISHED IF COUNT=0 PUSH H LHLD DGTPTR INX H ;ELSE BUMP DIGIT POINTER JMP DTOB ;& CONVERT NEXT DIGIT MU10: XCHG DAD H RC MOV D,H MOV E,L DAD H RC DAD H RC DAD D RET ; SUBROUTINE - RETURNS WITH 'C' FLAG SET IF CHAR. IN (A) IS NOT ; IN THE RANGE 0-9 VALDGT: CPI '0' RC CPI ':' CMC RET ; CHARACTER OUTPUT COUT: DS 0 PUSH H PUSH D PUSH B PUSH PSW ANI 07FH MOV E,A MVI C,CONOUT CNZ BDOS POP PSW POP B POP D POP H RET ; WRITE CR + LF CRLF: MVI A,CR CALL COUT MVI A,LF JMP COUT ; COMPRP = COMPARE REGISTER PAIR ; ; ENTRY: (HL), (DE) SET ; ; EXIT: 'Z'=1 IF (HL)=(DE) ; 'C'=1 IF (HL)>(DE) ; COMPRP: MOV A,D CMP H RNZ MOV A,E CMP L RET ; SUBHLDE = SUBTRACT (DE) FROM (HL) ; ; ENTRY: (HL), (DE) SET ; ; EXIT: (HL) = (HL) - (DE) ; 'C' AND 'Z' FLAGS REFLECT RESULTS OF SUBTRACTION ; ; USES: F,H,L ONLY ; SUBHLDE: DS 0 PUSH B MOV B,A ;SAVE (A) XCHG CALL COMPRP ;SET FLAGS XCHG MOV A,B ;RESTORE (A) POP B PUSH PSW ;SAVE (A) AND FLAGS MOV A,L SUB E MOV L,A ;DO SUBTRACTION MOV A,H SBB D MOV H,A POP PSW ;RESTORE (A) AND FLAGS RET ; MEMORY DEFINITIONS: ; FCB: DS FCBSIZ OLDSTK: DS 2 ;STACK POINTER ON ENTRY ENDTXT: DS 2 ;END OF TEXT IN BUFFER FILSIZ: DS 2 ;FILE SIZE IN RECORDS NREAD: DS 2 ;NUMBER OF RECORDS TO READ FROM FILE NUMEND: DS 2 ;END OF NUMBER DS 40 ;STACK AREA STACK: DS 0 ;TOP OF STACK NUMLIN: DW 20 ;NUMBER OF LINES TO LIST MSG0: DB CR,LF,'CP/M 2.X REQUIRED.',CR,LF,'$' MSG1: DB 'TAIL V. 1.1 BY JON DART' DB CR,LF,CR,LF DB 'USAGE: TAIL -n filename',CR,LF DB SPACE,SPACE,SPACE,SPACE,SPACE,SPACE,SPACE DB '-n is optional, shows last n lines of file (default 20)','$' MSG2: DB CR,LF,'CAN''T OPEN: ' FILNAM: DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,CR,LF,'$' ENDDATA EQU $ BUFFER EQU ENDDATA+10; END