TITLE 'Bi-Directional File Dump Utility for CP/M86' ; use: ASM86 BIDUMP86 $PZSZ ; GENCMD BIDUMP86 DATA[M500] ; ; 10/04/84 Charlie Godet-Ceraolo ; 2610 Glenwood Rd. ; Brooklyn,NY 11210 ; false EQU 0 true EQU not false ; CR EQU 0DH LF EQU 0AH ESC EQU 1BH SPACE EQU ' ' EOS EQU 0 ; end of string marker ; PRINTER EQU 5 ; output to LST device dircon EQU 6 CONSOLE EQU 6 ; direct console io open EQU 15 ; open file stdma EQU 26 ; set dma address READRANDOM EQU 33 ; read random record getsize EQU 35 ; return file size ; maxsec EQU 128 ; 16K buffer ScreenWidth EQU 80 ; cols per screen ; DSEG ; ; Image of CP/M86's BASEPAGE. Simplifies the code ; Must be at the beginning of Data Segment ; BASEPG RB 5CH FCB EQU $ ; default fcb FCBDR RB 1 FCBFN RB 8 ; file name FCBFT RB 3 ; file type FCBEX RB 1 ; extent byte FCBS1 RB 1 FCBS2 RB 1 FCBRC RB 1 ; record count FCB2 EQU $ ; second fcb FCBD0 RB 16 ; data map FCBCR RB 1 ; current record FCBRNO RW 1 ; random record number FCBR3 RB 1 ; random record overflow TBUF RB 80H ; default dma buffer ; ;**************************************************************** ; ; USER DEFINABLE VALUES ; ; Change CLS_STR for the codes that will clear your screen ; and home your cursor. Don't forget the zero byte ; at the end. ; ; If you don't have or don't want Cursor On/Off, just replace ; the codes with a zero byte. ; CLS_STR DB ESC,'E',0 ;IBM PC, change as required CURSOR_OFF DB ESC,'n',0 ;IBM PC, change as required CURSOR_ON DB ESC,'m',0 ;IBM PC, change as required Marker DB '>' ; marks record start on screen ; ; You can choose any collection of single keys for any of the ; commands. Letters must be upper case. These commands ; DON'T need a zero byte. If you want to use your ; terminal's special function keys, the Do_Cmds routine ; ignores ESCAPE. ; Add your keys to the UseMsg in the data area ; COMMANDS EQU $ FWD_CMDS DB SPACE,'F' ; next record BACK_CMDS DB 'B' ; previous record BOF_CMDS DB '1' ; first record EOF_CMDS DB 'L' ; last record CONT_CMDS DB 'C' ; continuous forward PRINT_CMDS DB 'P' ; print current sector EXIT_CMDS DB 'QX' ; exit program ; ;**************************************************************** ; CSEG ; BIDUMP: MOV DX,DS MOV SS,DX MOV SP,OFFSET LOCAL_STACK MOV ES,DX CLD MOV SI,OFFSET SignOn CALL PSTRING ; CMP FCBFN,SPACE ; anything there? JNE gotaname ; yes, proceed ; MOV SI,OFFSET UseMsg ; no, give help message CALL PSTRING ; and leave JMP exit1 ; gotaname: CALL OpenFile JNC openok ; MOV SI,OFFSET NoFileMsg CALL PSTRING ; nope, send message JMP exit1 ; and leave ; openok: CALL Init_File JNC not_empty ; MOV SI,OFFSET EmptyMsg CALL PSTRING JMP exit1 not_empty: CALL Store_Name ; put filename on help screen MOV SI,OFFSET CURSOR_OFF CALL PSTRING JMPS first_entry ; Do_Cmds: CALL Key_Pressed ; get a keyboard char JNC Do_Cmds CALL MAKE_UPPER ; to upper case CMP AL,esc ; skip if esc JE Do_Cmds ; MOV DI,OFFSET COMMANDS MOV CX,LENGTH FWD_CMDS REPNE SCASB JNE try_back_cmds MOV BX,CUR_SECT INC BX CALL GetBufPos JC Do_Cmds CALL Display_Sector JMPS Do_Cmds try_back_cmds: MOV CX,LENGTH BACK_CMDS REPNE SCASB JNE try_bof_cmds MOV BX,CUR_SECT DEC BX CALL GetBufPos JC Do_Cmds CALL CLEAR_SCREEN CALL Display_Sector JMPS Do_Cmds try_bof_cmds: MOV CX,LENGTH BOF_CMDS REPNE SCASB JNE try_eof_cmds first_entry: MOV BX,0 CALL GetBufPos CALL CLEAR_SCREEN CALL Display_Sector JMPS Do_Cmds try_eof_cmds: MOV CX,LENGTH EOF_CMDS REPNE SCASB JNE try_cont_cmds MOV BX,File_Last CALL GetBufPos CALL CLEAR_SCREEN CALL Display_Sector JMPS Do_Cmds try_cont_cmds: MOV CX,LENGTH CONT_CMDS REPNE SCASB JNE try_print_cmds repeat: MOV BX,CUR_SECT INC BX CALL GetBufPos JC Do_Cmds CALL Display_Sector CALL Key_Pressed JNC repeat JMP Do_Cmds try_print_cmds: MOV CX,LENGTH PRINT_CMDS REPNE SCASB JNE try_exit_cmds MOV Out_Device,PRINTER MOV SI,Offset SectorBuffer MOV CX,LENGTH SectorBuffer p_loop: LODSB CALL CTYPE LOOP p_loop MOV AL,CR CALL CTYPE MOV AL,LF CALL CTYPE MOV Out_Device,CONSOLE JMP Do_Cmds try_exit_cmds: MOV CX,LENGTH EXIT_CMDS REPNE SCASB JNE send_help JMPS exit send_help: MOV SI,OFFSET HelpMsg CALL PSTRING JMP Do_Cmds ;try again for command ; ; MAIN PROGRAM EXIT ; exit: CALL CLEAR_SCREEN ;clear the screen ; ; error exit, doesn't clear screen ; exit1: CALL Key_Pressed MOV SI,OFFSET CURSOR_ON CALL PSTRING MOV CL,0 ; back to CP/M MOV DL,0 CALL BDOS ; ; SUBROUTINES ; GetBufPos: CMP BX,0 ; check range: beyond bof? JB Nil_Return ; yes, tell caller, and leave CMP BX,File_End ; beyond eof? JB in_range ; nope, proceed NIL_Return: STC ; no good, set carry RET ; back to caller ; in_range: MOV CUR_SECT,BX ; make desired sector current ; is desired record in buffer? CMP BX,FCBRNO ; beyond last read? JBE not_eob CMP BX,File_Last ; is eof wanted? JE Read_EOF ; yes, go get it JMPS Read_FWD not_eob: CMP BX,Buf_First ; beyond beginning of buffer? JAE not_bob ; nope, proceed CMP BX,0 ; bof wanted? JE Read_BOF JMPS Read_BACK not_bob: SUB BX,Buf_First MOV CL,7 ; desired sector SHL BX,CL ; times 128 ADD BX,(OFFSET dskbuf) ; plus buffer start addr OK_Return: CLC ; clear carry RET ; ; Logic here is to do backward reads into the middle of the buffer ; (maxsec DIV 2) this eliminates a certain amount of disk-thrashing ; at the expense of slower backward movement through long files. ; Read_BACK: MOV BX,maxsec/2 ;get the buffer size ADD BX,seccnt ; add number of sectors last read SUB FCBRNO,BX ; tell bdos where to start JC Read_BOF ;if beyond beginning of file, go get it CALL FILL_BUFFER ; else fill the buffer MOV BX,seccnt ; put it in middle of buffer SHR BX,1 ; DIV 2 MOV CL,7 ; times 128 SHL BX,CL ADD BX,OFFSET dskbuf ; add start of buffer JMPS OK_Return ; Read_BOF: XOR AX,AX ; if bof not in buffer MOV FCBR3,AL ; zero the overflow MOV FCBRNO,AX ; and current record field CALL FILL_BUFFER ; fill it MOV BX,Offset DSKBUF JMPS OK_Return ; and back to caller ; Read_FWD: INC FCBRNO ; tell bdos where to start CALL FILL_BUFFER ; fill it MOV BX,Offset DSKBUF JMPS OK_Return ; and back to caller ; Read_EOF: MOV BX,File_Last SUB BX,maxsec-1 ; subtract buffer size MOV FCBRNO,BX ; tell bdos where to start CALL FILL_BUFFER ; fill the buffer MOV BX,seccnt ; number of sects in buffer MOV CL,7 ; times 128 SHL BX,CL ADD BX,OFFSET dskbuf JMPS OK_Return ; and back to caller ; Display_Sector: PUSH CX PUSH DX PUSH SI PUSH DI MOV SI,BX ; put bufpos in SI MOV DI,Offset SectorBuffer MOV CX,8 ; 8 groups of 16 bytes to process XOR DL,DL ; initialize byte-offset to 0 MOV AL,Marker ; mark start of sector sectloop: STOSB CALL P_Space CALL P_Index ; print the record num & offset CALL DO16 ; print 16 hex bytes CALL P_Ascii ; print the ascii equivalent CALL P_CrLf ; move to next screen line ADD SI,16 ; bump buffer position by 16 ADD DL,16 ; bump byte-offset by 16 MOV AL,SPACE ; indent LOOP sectloop ; until we're done MOV AL,0 ; store terminator STOSB MOV SI,Offset SectorBuffer CALL P_Sector ; display the sector POP DI POP SI POP DX POP CX RET ; P_Index: PUSH AX MOV AX,CUR_SECT XCHG AL,AH CALL P_Hex XCHG AL,AH CALL P_Hex CALL P_Space MOV AL,DL ; get byte-offset CALL P_Hex ; display it MOV AL,':' STOSB CALL P_Space POP AX RET ; DO16: PUSH SI PUSH CX MOV CX,16 doem: CALL P_Space LODSB CALL P_Hex LOOP doem POP CX POP SI RET ; P_Space: PUSH AX MOV AL,SPACE STOSB POP AX RET ; P_CrLf: PUSH AX MOV AL,CR STOSB MOV AL,LF STOSB POP AX RET ; P_Hex: PUSH AX PUSH BX PUSH CX MOV BX,Offset HexTable MOV AH,AL ; save byte MOV CL,4 ; do high nibble SHR AL,CL XLAT BX STOSB MOV AL,AH ; get it back AND AL,0FH ; do low nibble XLAT BX STOSB POP CX POP BX POP AX RET ; DSEG $ HexTable DB '0123456789ABCDEF' CSEG $ ; P_Ascii: PUSH SI PUSH CX CALL P_Space CALL P_Space CALL P_Space MOV CX,16 ; 16 BYTES TO DISPLAY paloop: LODSB ; GET THE BUFFER BYTE CMP AL,SPACE ; IS IT ASCII? JB pdot ; NOPE, PRINT A DOT CMP AL,7FH ; IS IT HIGHER THAN DEL? JB pa5 ; no, print it pdot: MOV AL,'.' ; print a dot pa5: STOSB LOOP paloop ; DONE 16? NO, GO BACK FOR MORE POP CX POP SI RET ; P_Sector: LODSB ; get a character OR AL,AL ; machine zero? JZ psexit ; yes, exit MOV DL,AL ;set up character MOV CL,dircon ;..to send to console PUSH ES INT 224 POP ES JMPS P_Sector ; get another psexit: RET ; OpenFile: MOV DX,OFFSET FCB ;file name in default fcb MOV CL,open CALL BDOS ; open it CMP AL,0FFH ; ok? JE no_open CLC RET no_open: STC RET ; Init_File: MOV DX,OFFSET FCB ; get File_End in sectors MOV CL,getsize CALL BDOS MOV BX,FCBRNO OR BX,BX ; is file empty? JZ mt_exit MOV File_End,BX ; save as eof recnum DEC BX ; adjust MOV File_Last,BX ; save as last recnum XOR BX,BX MOV FCBRNO,BX ; zero rand rec number MOV Buf_First,1 ; force first read CLC RET mt_exit: STC RET ; FILL_BUFFER: PUSH BX PUSH CX PUSH DX MOV BX,FCBRNO MOV Buf_First,BX ; save starting rec number MOV DX,(Offset dskbuf) ;load start of disk buffer MOV CX,maxsec ;number of sectors to read XOR BX,BX ;zero out the MOV seccnt,BX ;..number of sectors in buffer fil_buf1: CALL READ_SECTOR OR AL,AL ;read OK? JNZ fill_exit ;no, end of file, exit INC FCBRNO INC SECCNT ADD DX,128 ;else, add 128 to dma address LOOP fil_buf1 DEC FCBRNO ; correct 'em for one too many DEC SECCNT 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,OFFSET FCB MOV CL,READRANDOM 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 helpmsg, put in standard form ; Store_Name: CLD ; make sure we're going right MOV SI,Offset FCBFN ; SOURCE: name in fcb MOV DI,OFFSET FName ; DEST: storage area MOV CX,8 name1: LODSB CMP AL,SPACE ; blank? JE ext1 ; yes, get the extension STOSB ; store it LOOP name1 ; get another, if less than 8 ext1: MOV AL,'.' ; place a dot STOSB MOV SI,Offset FCBFT ; point to extension MOV CX,3 ; REP MOVSB ; move 3, no matter what RET ; CLEAR_SCREEN: PUSH SI MOV SI,OFFSET CLS_STR CALL PSTRING ; CALL P_CrLf ; CALL P_CrLf POP SI RET ; ; Convert char in AL to uppercase, if possible ; MAKE_UPPER: CMP AL,'a' ; is it lower? JB its_ok ; nope, return it CMP AL,'z' ; maybe JA its_ok ; nope, return it SUB AL,20H ; make it upper its_ok: RET ; ; Print null-terminated string pointed to by SI, saves all ; PSTRING: PUSH AX PUSH SI pstrloop: LODSB ; get a character OR AL,AL ; machine zero? JZ pstrexit ; yes, exit CALL CTYPE ; print it JMP pstrloop ; get another pstrexit: POP SI POP AX RET ; Key_Pressed: PUSH BX PUSH CX PUSH DX MOV CL,dircon ; see if a char is pending MOV DL,0FEH ; ask for status CALL BDOS OR AL,AL JZ no_char MOV CL,dircon ; yes, gobble it MOV DL,0FFH ; read funct CALL BDOS STC JMPS kp_exit no_char: CLC JMPS kp_exit kp_exit: POP DX POP CX POP BX RET ; ; Displays char in AL on the console, all regs saved ; ctype: PUSH AX PUSH CX PUSH DX PUSH BX MOV DL,AL ;set up character MOV CL,Out_Device CALL BDOS ;do it POP BX POP DX POP CX POP AX RET ; DSEG $ ; SignOn DB cr,lf,' BIDUMP Version 1.23' db cr,lf,' by Charlie Godet-Ceraolo (10/04/84)',cr,lf,EOS NoFileMSG DB CR,LF,' Input file not found.',07,cr,lf,EOS EmptyMsg DB cr,lf,' File is empty.',07,cr,lf,EOS ; UseMsg DB cr,lf,lf,' Syntax: BIDUMP filename ',07,cr,lf HelpMsg EQU $ Fname DB ' ' DB cr,lf,lf,' F or to go forward.',cr,lf DB ' B to go backward.',cr,lf DB ' 1 for beginning of file.',cr,lf DB ' E for end of file.',cr,lf DB ' C for continuous forward scroll.',cr,lf DB ' P to PRINT current sector.',cr,lf DB ' Q or X to exit.' DB cr,lf,EOS ; Out_Device DB CONSOLE seccnt RW 1 ;number of sectors read into buffer Buf_First RW 1 CUR_SECT RW 1 File_End RW 1 File_Last RW 1 ; RS 100 LOCAL_STACK EQU $ SectorBuffer RB 8*ScreenWidth ; for building line for display DSKBUF RB (MAXSEC*128) FULBUF EQU $ ; END