TITLE 'Word Count for CP/M 86' ; by Charlie Godet-Ceraolo ; 2610 Glenwood Road ; Brooklyn, NY 11210 ; ; based on MuchText.A86 with input file buffering ; and count accurate to about 8 digits. ; 5 March 1985 ; FALSE EQU 0 TRUE EQU NOT FALSE Release EQU 1 Version EQU 05 ; ; User definable equates ; Buf_Sectors EQU 256 ; 32k buffer Precision EQU 8 ; maximum bcd digits ; ; CP/M Equates ; Sector_Size EQU 128 Console EQU 2 OPEN EQU 15 ;OPEN FUNCTION CODE READ EQU 20 ;READ FUNCTION CODE STDMA EQU 26 ; set buffer address CPM_EOF EQU 1AH ; ; Ascii Equates ; TAB EQU 09H LF EQU 0AH ; Line Feed CR EQU 0DH ; Carriage Return SPACE EQU 20H ; Blank Ascii_Zero EQU '0' EOS EQU 0 ; CSEG ; WC: MOV DX,DS MOV SS,DX ; set up our stack MOV SP,OFFSET LOCAL_STACK MOV ES,DX ; make sure ES -> our data area CLD ; and we're going the right way ; MOV SI,Offset SignOn CALL P_String MOV AL,.CMD_TAIL ; test cmdline OR AL,AL ; anything? JNZ got_cmd ; yes, continue ; MOV SI,OFFSET MsgUse ; no args, send help CALL P_String JMP MAIN_EXIT ; and leave ; ; try to OPEN FILE ; got_cmd: MOV DX,TFCB MOV CL,OPEN CALL BDOS CMP AL,0FFH ; found? JNE MAIN ; yes, proceed ; MOV SI,OFFSET MsgNoFile ; nope, tell 'em CALL P_String JMP MAIN_EXIT ; and leave ; MAIN: MOV AL,Space ; initialize variables MOV CX,Precision * 3 MOV DI,Offset Counters REP STOSB MOV InWord,FALSE MOV BUF_PTR,Offset BUF_END ; force first read CALL P_Name ; ; MAIN_LOOP: CALL GET_CHAR ;GET A BYTE CMP AL,CPM_EOF ; eof? JE send_report ; leave AND AL,7FH ; strip hi-bit for WS files MOV SI,Offset Char_Count CALL Inc_Ascii ; bump char count CMP AL,LF ; eoln? JNE not_lf ; nope MOV SI,Offset Line_Count CALL Inc_Ascii ; yes, bump line count not_lf: MOV DI,Offset WhiteSpace MOV CX,Length WhiteSpace REPNE SCASB JNE not_white ; nope MOV InWord,FALSE ; yes, end of a word JMPS Main_Loop ; and back for more not_white: CMP InWord,TRUE ; are we in a word? JE Main_Loop ; yes, back for more MOV InWord,TRUE ; we are now, MOV SI,Offset Word_Count CALL Inc_Ascii ; bump word count JMPS MAIN_LOOP ; and back for more ; ; FINAL PROCESSING ; send_report: MOV SI,Offset MsgDoc CALL P_String MOV SI,Offset MsgChar CALL P_String MOV SI,Offset Char_Count CALL P_Number MOV SI,Offset MsgLine CALL P_String MOV SI,Offset Line_Count CALL P_Number MOV SI,Offset MsgWords CALL P_String MOV SI,Offset Word_Count CALL P_Number CALL P_CrLf MAIN_EXIT: MOV CL,0 ;EXIT TO CP/M MOV DL,0 INT 224 ; ; SUBROUTINES ; P_CrLf: PUSH AX MOV AL,CR CALL P_Char MOV AL,LF CALL P_Char POP AX RET ; P_Char: PUSH AX PUSH BX PUSH CX PUSH DX MOV DL,AL MOV CL,Console CALL BDOS POP DX POP CX POP BX POP AX RET ; ; Print null-terminated string pointed to by SI. Preserves all. ; P_String: PUSH SI ps_loop: LODSB ; get char OR AL,AL ; string end? JZ ps_exit ; yes CALL P_Char ; no, print it JMPS ps_loop ; get another ps_exit: POP SI RET ; ; Increment an unpacked BCD number, up to Precision places ; Inc_Ascii: PUSH SI PUSH DI PUSH AX PUSH CX STD ; set to decrement ADD SI,Precision-1 ; start from LSB MOV DI,SI ; point di there too MOV CX,Precision ; max bytes to do incloop: LODSB ; get byte INC AL ; bump it AAA ; ascii adjust STOSB ; save it JNC incexit ; no carry, we're done LOOP incloop ; else go to next byte ; if we get here, ; we have overflow incexit: POP CX POP AX POP DI POP SI CLD ; restore regs and direction RET ; ; Display an unpacked BCD number with leading zeros replaced ; by leading blanks. Up to Precision digits ; P_Number: MOV CX,Precision - 1 ; all but last digit ploop: LODSB ; get a byte CMP AL,Space ; if it's a space JE p_it ; print it ADD AL,Ascii_Zero ; else make it ascii p_it: CALL P_Char LOOP ploop ; get another LODSB ; get last digit OR AL,30H ; make ascii, no matter CALL P_Char ; and print RET ; ; Gets char from buffer, reads file, if necessary. ; Updates buffer pointer and returns char in AL. ; Preserves all. ; GET_CHAR: PUSH SI MOV SI,BUF_PTR ; get buffer position CMP SI,Offset BUF_END ; are we at end? JNE get_byte ; no, get char ; CALL INIT_BUFFER ; yes, do a read CALL FILL_BUFFER MOV SI,OFFSET BUF_START ; reset the pointer get_byte: LODSB MOV BUF_PTR,SI POP SI RET ; ; Fill buffer with CPM_EOF, so getchar will find one. ; Preserves all ; INIT_BUFFER: PUSH CX PUSH DI PUSH AX MOV CX,Buf_Sectors*Sector_Size ; bytes to fill MOV DI,Offset BUF_START MOV AL,CPM_EOF REP STOSB POP AX POP DI POP CX RET ; ; Fills the buffer from the start to either EOF or Buf_Sectors. ; preserves all. BDOS return code in AL. ; FILL_BUFFER: PUSH BX PUSH CX PUSH DX MOV DX,(Offset BUF_START) ;load start of disk buffer MOV CX,Buf_Sectors ;number of sectors to read fil_buf1: CALL READ_SECTOR OR AL,AL ;read OK? JNZ fill_exit ;no, end of file, exit ADD DX,Sector_Size ;else, add 128 to dma address LOOP fil_buf1 fill_exit: POP DX POP CX POP BX RET ; ; Reads a sector into the address in DX. Preserves all, ; bdos return code in al ; READ_SECTOR: PUSH BX PUSH CX PUSH DX MOV CL,STDMA CALL BDOS MOV DX,TFCB MOV CL,READ CALL BDOS POP DX POP CX POP BX RET ; ; Calls bdos, saves ES from bdos clobber. Don't use for ; system calls that return values in ES!!! (eg GetDMABase etc.) ; BDOS: PUSH ES INT 224 POP ES RET ; ; Move filename from fcb to heading, put in standard form ; P_Name: MOV SI,TFCB+1 ; SOURCE: name in fcb MOV DI,Offset FName MOV CX,8 name1: LODSB CMP AL,SPACE ; blank? JE ext1 ; yes, get the extension STOSB LOOP name1 ; get another, if less than 8 ext1: MOV AL,'.' ; place a dot STOSB MOV SI,TFCB+9 ; point to extension MOV CX,3 REP MOVSB RET IF ((Offset $ - Offset WC) MOD 10H) EQ 0 NOP ENDIF ; DSEG Data_Start EQU $ ORG 100H ; for the base page ; TFCB EQU 005CH ;TRANSIENT PROGRAM FCB CMD_TAIL EQU 0080H ; CP/M command line image ; SignOn DB CR,LF,' WORDCOUNT Version ' DB (Release MOD 10) + Ascii_Zero,'.' DB (Version / 10) + Ascii_Zero DB (Version MOD 10) + Ascii_Zero DB CR,LF,' by Charlie Godet-Ceraolo (3-5-85)' DB CR,LF,' (based on the program MUCHTEXT by Kelly Smith)' DB CR,LF,EOS MsgNoFile DB ' File not found.',07,CR,LF,EOS MsgDoc DB CR,LF,TAB,'File: ' FName DB ' ' DB CR,LF,EOS MsgChar DB CR,LF,TAB,'Characters: ',EOS MsgLine DB CR,LF,TAB,'Lines: ',EOS MsgWords DB CR,LF,TAB,'Words: ',EOS MsgUse DB CR,LF,' Syntax: WC filespec' DB CR,LF,EOS ; WhiteSpace DB CR,LF,TAB,Space,0CH,08H IF (Offset $ - Offset Data_Start ) MOD 2 NE 0 ; make even RB 1 ENDIF RS 128 LOCAL_STACK EQU $ InWord RW 1 ; word flag Counters EQU $ Line_Count RS Precision Word_Count RS Precision Char_Count RS Precision BUF_PTR RW 1 BUF_START RS (Buf_Sectors*Sector_Size) BUF_END EQU $ Min_Data EQU (Offset $ - Offset Data_Start) / 16 ; for gencmd ; END ; SingleQuote EQU 27H DoubleQuote EQU 22H