;******************************************************************** ;* * ;* PROT - A 386 protected mode DOS extender * ;* Copyright (C) 1989, by Al Williams * ;* All rights reserved. * ;* * ;* Permission is granted for non-commercial use of this software * ;* subject to certain conditions (see PROT.ASM). * ;* * ;* This file is: FBROWSE.PM, a file browser. * ;* To assemble: PMASM FBROWSE * ;* or: MASM /DPROGRAM=FBROWSE PROT.ASM,FBROWSE.OBJ,FBROWSE.LST; * ;* * ;******************************************************************** ; If this equate is 0, use direct screen output else use DOS DOSOUT EQU 0 ; This program displays a file. The file is stored as strings ; starting at the top of memory (the heap) and growing downward. ; At the start of memory is an array of pointers to lines in the ; heap. PROT_DATA BASE DD 0 ; top screen line PTRPTR DD 0 ; pointer to end of line pointers HEAPPTR DD ? ; pointer to empty heap entry FHANDLE DW ? ; file handle LBUF DB 81 DUP(?) ; line buffer FBUFP DD 0 ; pointer to character in fbuf FBUFL DW 0 ; # of char's in fbuf FBUF DB 512 DUP(?) ; file buffer ; help message UMSG DB 'Usage: FBROWSE filename',13,10,'$' ; error messages NFMSG DB 'Can''t open file',13,10,'$' FEMSG DB 'File error!',13,10,'$' OMMSG DB 'Out of memory',13,10,'$' ; copyright message CPYMSG DB 'FBROWSE - Protected Mode File Browser (C) 1989' DB ' by Al Williams' IF DOSOUT DB '$' ; end of string for DOS function 9 ELSE DB 0 ; end of string for PROT ENDIF ; bios call frame BINTFRAME VM86BLK <> PROT_DATA_END PROT_CODE ; This routine catches ^BREAK's and forces program termination ONBREAK PROC FAR MOV AX,SEL_UDATA MOV DS,AX JMP32S USTOP0 ONBREAK ENDP ; Main program USER PROC NEAR ; Enable Break handling MOV AX,SEL_UCODE MOV BRK_SEG,AX MOV EAX,OFFSET ONBREAK MOV BRK_OFF,EAX ; clear the screen CALL32F SEL_CODE32,CLS ; Set up CALL86 parameter blocks (pintframe & bintframe) MOV AX,SEG SSINT1 MOV PINTFRAME.VMSS,EAX MOV BINTFRAME.VMSS,EAX MOV AX,SEG USERDATA MOV PINTFRAME.VMDS,EAX MOV EAX, OFFSET SSINT1 MOV PINTFRAME.VMESP,EAX MOV BINTFRAME.VMESP,EAX ; Pintframe will be used for INT 21 MOV EAX,21H MOV PINTFRAME.VMINT,EAX ; get command line MOV CX,SEL_PSP MOV FS,CX ASSUME FS:NOTHING XOR EBX,EBX MOV BL,FS:[80H] OR BL,BL JNZ SHORT OPENFILE ; No command line - Print usage message MOV AH,9 MOV EDX,OFFSET UMSG MOV EBX,OFFSET PINTFRAME VM86CALL MOV AL,1 JMP32S USTOP ; back to DOS ; Open file for reading OPENFILE: XOR AX,AX ; null terminate file name MOV FS:[81H+BX],AX MOV AX,_PSP MOV PINTFRAME.VMDS,EAX MOV EDX,80H ; skip spaces SPACSKIP: INC DX CMP BYTE PTR FS:[EDX],' ' JZ SHORT SPACSKIP MOV AX,3D00H MOV EBX,OFFSET PINTFRAME VM86CALL ; open file MOV BX,SEG USERDATA MOV PINTFRAME.VMDS,EBX JNC SHORT FOPENED ; Couldn't open file -- print error MOV EDX,OFFSET NFMSG MOV AH,9 MOV EBX,OFFSET PINTFRAME VM86CALL ; can't open file MOV AL,1 JMP32S USTOP ; back to DOS ; File opened OK FOPENED: MOV FHANDLE,AX ; save file handle ; See if extended memory present MOV AX,SEL_EXT LSL EBX,EAX OR EBX,EBX JNZ SHORT USE_EXTMEM ; No extended memory present -- use DOS memory MOV AX,SEL_FREE USE_EXTMEM: MOV GS,AX ; memory segment --> GS ; Set heap pointer to top of availble memory LSL EAX,EAX MOV HEAPPTR,EAX MOV EDX,EAX XOR EBX,EBX REREAD: MOV EAX,EBX ; check for heap overflow SHL EAX,2 CMP EAX,EDX JB SHORT HEAPOK ; No more memory -- Print error message MOV AH,9 MOV EDX,OFFSET OMMSG MOV EBX,OFFSET PINTFRAME VM86CALL ; out of memory MOV AL,1 JMP32S USTOP ; back to DOS HEAPOK: ; Put heap pointer in lines array MOV GS:[EBX*4],EDX ; read line to heap in reverse order. Each line ends in CR CALL SHORT READLINE PUSHFD ; point to next line in lines array INC EBX POPFD JNZ SHORT REREAD ; not end of file? ; here at end of file MOV PTRPTR,EBX ; end of lines array MOV BX,FHANDLE MOV PINTFRAME.VMEBX,EBX MOV AH,3EH MOV EBX,OFFSET PINTFRAME VM86CALL ; close file MOV DX,1800H CALL SHORT SETCP ; set cursor position ; write copyright message IF DOSOUT MOV AH,9 MOV EDX,OFFSET CPYMSG MOV EBX,OFFSET PINTFRAME VM86CALL ; print copyright ELSE ; If not BIOS output then get rid of BIOS cursor MOV DX,0FFFFH CALL32S DOSSETCP ; hide cursor MOV EBX,OFFSET CPYMSG CALL32F SEL_CODE32,MESSOUT ENDIF ; inverse video the bottom line PUSH DS MOV AX,SEL_VIDEO MOV DS,AX MOV CL,80 MOV EDX,24*160+1 ; bottom line INVLOOP: MOV BYTE PTR [EDX],70H INC EDX INC EDX DEC CL JNZ SHORT INVLOOP POP DS JMP SHORT HK0 ; skip over home key processing ; Home key processing HOMEKEY: OR BASE,0 ; don't do home if already there JZ SHORT KEYLOOP HK0: XOR EAX,EAX ; Set screen start to 0 MOV BASE,EAX ; Come here to display a page starting at base PAGEDISP: CALL SHORT DISPLAY ; read a key and act on it KEYLOOP: MOV AX,16H MOV BINTFRAME.VMINT,EAX XOR AH,AH PUSH ES MOV BX,SEL_UDATA MOV ES,BX MOV EBX,OFFSET BINTFRAME VM86CALL ; read a key POP ES CMP AL,27 ; escape key? JZ NEAR PTR USTOP0 CMP AL,3 ; break key JZ NEAR PTR USTOP0 OR AL,AL JNZ SHORT KEYERR ; special key? ; test for function keys CMP AH,48H ; up arrow JCC32 Z, UPARROW CMP AH,47H ; home key JZ SHORT HOMEKEY CMP AH,4FH ; end key JCC32 Z,ENDKEY CMP AH,49H ; page up JCC32 Z,PGUP CMP AH,51H ; page down JCC32 Z,PGDN CMP AH,50H ; down arrow JZ SHORT DNARROW ; Come here if you don't know what that key was! KEYERR: PUSH EDX MOV DL,7 ; bell character MOV AH,2 MOV EBX,OFFSET PINTFRAME VM86CALL POP EDX JMP SHORT KEYLOOP ; down arrow processing DNARROW: MOV EAX,BASE ; don't go down if at end ADD EAX,24 CMP EAX,PTRPTR JCC32 AE,KEYLOOP INC BASE ; top of screen + 1 MOV AX,0601H ; scroll screen CALL SHORT SCROLL MOV DX,1700H ; position cursor CALL SHORT SETCP MOV EDX,BASE ; print bottom line ADD EDX,23 CALL SHORT PLINE JMP32S KEYLOOP ; up arrow processing UPARROW: OR BASE,0 ; don't go up if at top JCC32 Z,KEYLOOP DEC BASE ; adjust top of screen MOV AX,0701H ; scroll screen CALL SHORT SCROLL XOR DX,DX ; position cursor CALL SHORT SETCP MOV EDX,BASE ; print top line CALL SHORT PLINE JMP32S KEYLOOP ; End key processing ENDKEY: MOV EAX,BASE ; Don't do end if already there ADD EAX,24 CMP EAX,PTRPTR JCC32 AE,KEYLOOP MOV EAX,PTRPTR ; get last line SUB EAX,24 ; find top of screen MOV BASE,EAX JMP32S PAGEDISP ; display page ; Page Up processing PGUP: MOV EAX,BASE ; Don't do pgup if at top OR EAX,EAX JCC32 Z,KEYLOOP CMP EAX,24 JBE SHORT PGUP0 ; if near the top.. go pgup0 SUB EAX,24 ; find new line JMP SHORT PGUP1 PGUP0: XOR EAX,EAX ; 1st line PGUP1: MOV BASE,EAX JMP32S PAGEDISP ; Page down processing PGDN: MOV EAX,BASE ; If at bottom, skip ADD EAX,24 CMP EAX,PTRPTR JCC32 AE,KEYLOOP MOV BASE,EAX ; find new line JMP32S PAGEDISP ; Exit FBROWSE USTOP0: ; clear screen CALL32F SEL_CODE32,CLS XOR AL,AL ; zero return code PUSH EAX XOR DX,DX ; home cursor CALL SHORT DOSSETCP POP EAX USTOP: BACK2DOS USER ENDP ; Read a line from the file ; lines end in 0AH unless they are long in which case they ; end in 0DH READLINE PROC NEAR PUSH ECX MOV CX,80 RLINE0: CALL SHORT GFCHAR ; get character MOV GS:[EDX],AL ; store it PUSHFD DEC EDX POPFD ; end of line? JZ SHORT RLINEXIT CMP AL,0AH ; end of line? JZ SHORT RLINEEOL DEC CX JNZ SHORT RLINE0 ; 80 char line? ; compare last character with 0ah -- if true pass it CALL SHORT GFCHAR CMP AL,0AH ; if so, line exactly 80 chars JZ SHORT LONGLINE DEC FBUFP ; unget character LONGLINE: MOV AL,0DH ; mark long line MOV GS:[EDX],AL ; save end marker DEC EDX RLINEEOL: CMP AL,0FFH ; set NZ flag RLINEXIT: POP ECX RET READLINE ENDP ; scroll screen using BIOS SCROLL PROC NEAR PUSH ES MOV BX,SEL_UDATA MOV ES,BX MOV BX,0700H MOV BINTFRAME.VMEBX,EBX MOV BX,10H MOV BINTFRAME.VMINT,EBX XOR ECX,ECX MOV DX,174FH MOV EBX,OFFSET BINTFRAME VM86CALL POP ES RET SCROLL ENDP ; position the cursor DOSSETCP PROC NEAR PUSH EBX PUSH ES MOV AX,SEL_UDATA MOV ES,AX MOV BX,10H MOV BINTFRAME.VMINT,EBX MOV BINTFRAME.VMEBX,EBX MOV AH,2 MOV EBX,OFFSET BINTFRAME VM86CALL POP ES POP EBX RET DOSSETCP ENDP SETCP PROC NEAR IF DOSOUT CALL32S DOSSETCP ELSE PUSH ES MOV AX,SEL_DATA MOV ES,AX MOV EAX,80 MUL DH XOR DH,DH ADD AX,DX MOV ES:CURSOR,EAX POP ES RET ENDIF SETCP ENDP ; display a screen full DISPLAY PROC NEAR MOV AX,0600H CALL32S SCROLL ; clear screen XOR DX,DX CALL32S SETCP ; home cursor MOV CX,24 MOV EDX,BASE DISP0: PUSH EDX CALL SHORT PLINE ; print lines POP EDX INC EDX CMP EDX,PTRPTR JZ DISP00 DEC CX JNZ SHORT DISP0 DISP00: RET DISPLAY ENDP ; print a line pointed to by [edx] going backwards to \n PLINE PROC NEAR ; get line ptr MOV EDX,GS:[EDX*4] PLINE0: MOV AL,GS:[EDX] ; get char fm str DEC DX CMP AL,0DH ; Long line? JZ SHORT PLINE2 CALL SHORT OOUCH ; Output character CMP AL,0AH ; End of string JNZ SHORT PLINE0 PLINE1: MOV AL,0DH ; Output CR CALL SHORT OOUCH PLINE2: RET PLINE ENDP ; get a character from the file GFCHAR PROC NEAR PUSH EBX PUSH EDX PUSH ECX GFSKIP: OR FBUFL,0 ; buffer empty? JZ SHORT FILLBUFF MOV EAX,FBUFP ; buffer used up? CMP AX,FBUFL JNZ SHORT FMBUFF FILLBUFF: MOV AX,FHANDLE ; read from file MOV PINTFRAME.VMEBX,EAX MOV ECX,512 MOV EDX,OFFSET FBUF MOV AH,3FH MOV EBX,OFFSET PINTFRAME VM86CALL ; read buffer JNC SHORT NOFERR ; file error here MOV AH,9 MOV EDX,OFFSET FEMSG VM86CALL JMP32S USTOP ; back to DOS NOFERR: OR AX,AX MOV FBUFL,AX ; store buffer length MOV AL,0AH JZ SHORT GFCEOF ; End of file? XOR EAX,EAX ; clear buffer pointer MOV FBUFP,EAX ; This is where the character is read from a good buffer FMBUFF: MOV EBX,FBUFP MOV AL,FBUF[EBX] INC EBX MOV FBUFP,EBX CMP AL,0DH ; skip CR JCC32 Z,GFSKIP GFCEOF: POP ECX POP EDX POP EBX RET GFCHAR ENDP ; output character in al OOUCH PROC NEAR IF DOSOUT PUSHAD PUSH ES ASSUME ES:DAT32 MOV BX,SEL_DATA MOV ES,BX MOV AH,2 MOV DL,AL MOV EBX,OFFSET PINTFRAME VM86CALL POP ES POPAD RET ELSE CALL32F SEL_CODE32,OUCH RET ENDIF OOUCH ENDP PROT_CODE_END