'===========================================================================
' Subject: SUPER HEX BROWSER                  Date: 07/95 (00:00)          
'  Author: Victor Yiu                         Code: ASM, QB, PDS           
'  Origin: SUPER,HEX.BROWSER                Packet: BINARY.ABC
'===========================================================================
'QB users must change all occurences of SSEG to VARSEG.
'
'Enjoy!  And I want to know if it feels SNAPPY on your machine!

' ==================== Browser 1.0 =======================
' by Victor Yiu, July 1993.  Released into Public Domain.
'
'              *** LOAD WITH /AH SWITCH ***
' This program is a binary file viewer, in hex and ascii,
' made to look like Calvin French's HEXVIEW, PCTools' View,
' Norton's DiskEdit, and numerous others.  It uses my public-
' domain SuperHex library, written in optimized assembly -- so
' it is FAST!
'                      (to make EXE:)
'       BC BROWSER /O;
'       LINK /FARC /PACKC:64000 BROWSER+C:\QB\NOCOM,,,C:\QB\BCOM45+SUPERHEX;
'            ^ (include "/EX" if you won't use LZEXE or PKLITE)
'
' Compared to Clavin's HEXVIEW:
'   o  Mine can open files up to 128k
'   o  Mine is 5 million times faster (faster than PCTools or Norton)
'   o  Has a built-in text filter
'   o  Acceptable on an 4.77 MHz XT!
'   o  Compiles to only 25K with LZEXE or PKLITE!
'
' Next version will include search and editing features.
'
' Speed comparison (on a 10MHz XT)*:
'       Calvin French's HEXVIEW:   .55 K/sec
'             PCTool's 4.x Edit:     3 K/sec
'         Victor's SuperHEX (!):    20 K/sec   --->  WOW!
'
'  * I didn't do timing on my 486 because it was too darn fast!
'
' By the way -- try loading a file from the floppy -- it's fun!
' ============================================================

DEFINT A-Z      ' $DYNAMIC

CONST Block = 8192, PageSize = 16 * 21, LastDataLine = 23
CONST DataFG = 7, DataBG = 1, Attrib = DataFG + DataBG * 16
CONST False = 0, True = NOT False

DECLARE SUB AdjustLastBytes (row%)
DECLARE SUB CheckPointer (Num&)
DECLARE SUB DrawInterface ()
DECLARE SUB DrawScreen ()
DECLARE SUB LoadFile (LOFile&)
DECLARE SUB PrintHex (Num&)
DECLARE SUB ShowHex (Posit&)
DECLARE FUNCTION GetAdapterSeg% ()
DECLARE FUNCTION Signed% (Num&)

DECLARE SUB Scroll (BYVAL GoUp, BYVAL Attrib)
DECLARE SUB MemCopy (BYVAL SegFrom, BYVAL OffFrom, BYVAL SegTo, BYVAL OffTo, BYVAL Leng)
DECLARE SUB SuperHex (BYVAL VidSeg, BYVAL row, BYVAL OffsetHex, BYVAL OffsetASCii, BYVAL SEGMENT, BYVAL offset, BYVAL BackColor, BYVAL FilterOn)
' VidSeg     = Video segment of adapter (B800 for color, B000 for mono)
' Row        = Row to display data (1-25)
' OffsetHex  = Column to display the hex digits (1-)
' OffsetASCii= Column to display the characters themselves (1-)
' Segment    = Segment of source data
' Offset     = Offset of source data
' BackColor  = Background color in a packed byte:
'                (BackGround * 16) + ForeGround

IF LEN(COMMAND$) = 0 THEN
    PRINT "Syntax:"
    PRINT "   BROWSER <filename>"
    END
END IF

DIM SHARED VidSegment, Pointer&, LastBox, LOFile&, FilterOn

OPEN COMMAND$ FOR BINARY AS #1
LOFile& = LOF(1)
IF (LOFile& + 3000 > FRE(-1)) OR (LOFile& > 131000) THEN PRINT "File too big.": END
IF LOFile& = 0 THEN CLOSE : PRINT COMMAND$; " does not exist.": KILL COMMAND$: END

DIM SHARED Array&(LOFile& \ 4& + 4)
Pointer& = 0: LastBox = 4: Null$ = CHR$(0)

CLS
VidSegment = GetAdapterSeg
DrawInterface
LOCATE 12, 33: COLOR 31, 3
PRINT " Loading file ... "; : COLOR 7

LoadFile LOFile&

IF LOFile& - 16 < PageSize THEN
    LOCATE 12, 33: COLOR , 1
    PRINT SPACE$(40);
END IF

DrawScreen
DO
    DO: I$ = INKEY$
    LOOP UNTIL LEN(I$)
    IF LEN(I$) = 1 THEN I$ = UCASE$(I$)

    SELECT CASE I$
        CASE Null$ + "Q", CHR$(13), " "   ' PageDown
            Pointer& = Pointer& + PageSize
            CheckPointer Pointer&
        CASE Null$ + "I"        ' PageUp
            Pointer& = Pointer& - PageSize
            CheckPointer Pointer&
        CASE Null$ + "P"        ' Down
            IF Pointer& + PageSize < LOFile& THEN
                Scroll 0, LastDataLine
                Pointer& = Pointer& + 16
                ShowHex Pointer&

                Temp& = Pointer& + PageSize - 16
                LOCATE LastDataLine, 3: COLOR 14
                PrintHex Temp&

                SuperHex VidSegment, LastDataLine, 10, 62, Signed(sseg(Array&(0)) + Temp& \ 16), VARPTR(Array&(0)), Attrib, FilterOn
                IF LOFile& - Temp& < 15 THEN AdjustLastBytes LastDataLine
            END IF
        CASE Null$ + "H"        ' Up
            IF Pointer& >= 16 THEN
                Scroll -1, LastDataLine
                Pointer& = Pointer& - 16
                ShowHex Pointer&

                LOCATE 3, 3: COLOR 14
                PrintHex Pointer&
                SuperHex VidSegment, 3, 10, 62, Signed(sseg(Array&(0)) + Pointer& \ 16), VARPTR(Array&(0)), Attrib, FilterOn
            END IF
        CASE Null$ + "G"        ' Home
            Pointer& = 0
            DrawScreen
        CASE Null$ + "O"        ' End
            Pointer& = LOFile&
            CheckPointer Pointer&
        CASE "F"                ' toggle filter
            FilterOn = NOT FilterOn
            DrawScreen

            LOCATE 25, 58: COLOR 4, 3
            IF FilterOn THEN PRINT CHR$(251);  ELSE PRINT " ";
        CASE ELSE
    END SELECT
LOOP UNTIL I$ = CHR$(27)

COLOR 7, 0: CLS
PRINT "Thanks for trying Browser 1.0!"
PRINT
END

REM $STATIC
SUB AdjustLastBytes (row)

    Remov = 16 - (LOFile& AND 15)
    LOCATE row, 58 - Remov * 3
    PRINT SPACE$(Remov * 3 + 1);
    LOCATE , 78 - Remov
    PRINT SPACE$(Remov);

END SUB

SUB CheckPointer (Num&)

IF Num& + PageSize - 15 > LOFile& THEN
    Num& = LOFile& + 15 - PageSize
END IF

IF Num& < 0 THEN Num& = 0
DrawScreen

END SUB

SUB DrawInterface

COLOR 14, 12
PRINT " Browser 1.0  þ  by Victor Yiu, July 1993  þ  Idea from" + " Calvin French's HEXVIEW "
COLOR 15, 1
PRINT CHR$(218); CHR$(196); CHR$(180);
COLOR 15, 3: PRINT " "; COMMAND$; " "; : COLOR 15, 1
PRINT CHR$(195); STRING$(80 - POS(0), 196); CHR$(191)

FOR Lin = 3 TO LastDataLine
    LOCATE Lin, 1: PRINT CHR$(179); SPACE$(78);

    IF Lin = 3 THEN
        PRINT CHR$(24);
    ELSEIF Lin = LastDataLine THEN
        PRINT CHR$(25);
    ELSE
        PRINT CHR$(176);
    END IF
NEXT

LOCATE 4, 80, 0: PRINT CHR$(219);
LOCATE 24, 1: PRINT CHR$(192); CHR$(196); CHR$(180);
COLOR 13: PRINT "       (     )/"; LTRIM$(STR$(LOF(1)));
COLOR 15: PRINT " "; CHR$(195); STRING$(80 - POS(1), 196); CHR$(217);

LOCATE 25, 1: COLOR 14, 3
PRINT " Adjust Viewport "; : COLOR 15
PRINT "[PgUp/PgDn] [Up/Down] [Home/End]         [F]=Filter" + " [Esc]=Quit ";

END SUB

SUB DrawScreen
STATIC NotOnePage, L    ' 1=True, 2=False, 0=first call

IF NotOnePage = 0 THEN
    IF LOFile& - 16 < PageSize THEN
        NotOnePage = 1
        L = (LOFile& - 1) \ 16 + 3
    ELSE
        NotOnePage = 2
        L = LastDataLine
    END IF
END IF

ShowHex Pointer&
COLOR 14

Temp& = Pointer&
FOR row = 0 TO L - 3
    SuperHex VidSegment, row + 3, 10, 62, Signed(sseg(Array&(0)) + Temp& \ 16), VARPTR(Array&(0)), Attrib, FilterOn

    LOCATE row + 3, 3, 0
    PrintHex Temp&

    Temp& = Temp& + 16
NEXT

IF Temp& > LOFile& THEN AdjustLastBytes L

END SUB

FUNCTION GetAdapterSeg
    DEF SEG = 0
    ColorM = (PEEK(&H410) AND 48) <> 48
    DEF SEG 'Monocrome--^                       ^
            'Color Graphics Adapter or better --|

    IF ColorM THEN GetAdapterSeg = &HB800 ELSE GetAdapterSeg = &HB000
END FUNCTION

SUB LoadFile (LOFile&)

Dummy& = FRE("")

TempStor$ = SPACE$(Block)
start& = sseg(Array&(0))

FOR LoadUp = 1 TO LOFile& \ Block
    GET #1, , TempStor$
    MemCopy sseg(TempStor$), SADD(TempStor$), Signed(start&), VARPTR(Array&(0)), Block
    start& = start& + Block \ 16
    ShowHex LoadUp * 1& * Block
NEXT

TempStor$ = SPACE$(LOFile& MOD Block)
GET #1, , TempStor$
MemCopy sseg(TempStor$), SADD(TempStor$), Signed(start&), 0, LEN(TempStor$)
    ' *** SSEG to sseg for QB/QBASIC users!
END SUB

SUB PrintHex (Num&)

    'PRINT MID$(HEX$(Num& + &H100000), 2);
    PRINT RIGHT$("0000" + HEX$(Num&), 5);

END SUB

SUB ShowHex (Posit&) STATIC

COLOR 15, 1
LOCATE 24, 5
PRINT USING "######"; Posit&;
LOCATE , 12
PrintHex Posit&

LOCATE LastBox, 80
PRINT CHR$(176);
LastBox = Posit& * 18 \ LOFile& + 4
LOCATE LastBox, 80
PRINT CHR$(219);

END SUB

FUNCTION Signed (Num&)

    IF Num& > 32767 THEN
        Signed = Num& - 65536
    ELSE
        Signed = Num&
    END IF

END FUNCTION


;---------8<-----[ Begin SUPERHEX.ASM ]----->8-----------
; All:
;
; This code is different from my original posting of SuperHex
; very slightly only.  I added a mem. copy and a scrolling
; procedure.
;              -- Victor

; =========== SuperHEX 1.1 ===============
; by Victor Yiu, July 1993
;
; Ultra-fast ASCII to HEX conversion...
; designed for calling up from a file
; or memory viewer to display hex/ascii
; codes like Norton's DiskEdit, or PCTool's
; View... except much faster!
;
; This code is released into public domain.
; =========================================

CODE SEGMENT PARA PUBLIC 'CODE'
    PUBLIC SuperHex, MemCopy, Scroll
    ASSUME CS:code, DS:nothing, ES:nothing, SS:nothing

CharsPerLine    = 80        ; set-up hard coded constant
FilterReplaceChar EQU   '.' ; replace bad chars. with what?

SuperHex PROC FAR
    PUSH BP             ; set up stack frame
    MOV BP, SP
    PUSH DS             ; save registers
    PUSH SI
    PUSH DI
; =============================================================
;VidSeg, Row, OffsetHex, OffsetASCii, Seg:Off memory (16 byte)
; BP+20  BP+18  BP+16       BP+14            BP+10 [DWORD]

; BG Color    Filter
;   BP+8       BP+6
; =============================================================

    LES AX, SS:[BP+18]  ; VidSeg --> ES
    DEC AX              ; Row --> AX  (adjust to 0-24 range)
    JZ NoMul            ; if 0, don't multiply to save time
    MOV BL, CharsPerLine; get # chars per line
    MUL BL              ; multiply
NoMul:
    SHL AX, 1           ; *2: vid.mem. alternates ASCii/color
    MOV DX, AX          ; save into DX

    LDS SI, SS:[BP+10]  ; get source memory into DS:SI
    PUSH SI             ; save it for later

; ====== Setup to write the 16 bytes of ASCii first
    MOV AH, SS:[BP+8]   ; get attribute into AH
    MOV DI, DX          ; move start of row offset into DI
    MOV BX, SS:[BP+14]  ; get offset of ASCii
    DEC BX
    SHL BX, 1           ; *2 because of vid. mem.
    ADD DI, BX          ; compute final offset
    CMP WORD PTR [BP+6], 0       ; filter on?
    JNE FilterOn

REPT 16                 ; repeat 16 times
    LODSB               ; get byte
    STOSW               ; store byte + attribute
ENDM
    JMP SHORT Continue
EVEN
FilterOn:
    MOV CX, 16
    MOV BX, '~ '        ; preload constants
EVEN
FilterTop:
    LODSB
    CMP AL, BL          ; below 32?
    JL NoShow
    CMP AL, BH          ; more than 127
    JG NoShow
    STOSW
    LOOP FilterTop
    JMP SHORT Continue
EVEN
NoShow:
    MOV AL, FilterReplaceChar
    STOSW
    LOOP FilterTop


; ======= Set up for HEX conversion to screen
Continue:
    MOV BX, SS:[BP+16]  ; get offset of HEX
    DEC BX
    SHL BX, 1           ; *2 for vid. mem
    ADD DX, BX          ; add to original row offset
    MOV DI, DX          ; put it into the index register
    POP SI              ; get previous SI
    MOV CX, 16          ; do sixteen characters

    MOV BL, AH          ; attribute into BL

    MOV DX, (256*9) + ('A'-'9'-1)
    MOV BP, '00'        ; preload stuff to make it scream

; BP = '00'
; BL = attribute
; BH = -- reserved for temporary digit
; DL = 'A'-'9'-1
; DH = 9
EVEN
LoopTop:
    MOV BH, 16          ; load divisor
    LODSB               ; get character
    MOV AH, 0           ; clear AH
    DIV BH              ; to get tens in AL, ones in AH.
    CMP AL, DH          ; > '9'?
    JLE NextDigit       ; no -- don't fix
EVEN
    ADD AL, DL          ; fix it
NextDigit:
    CMP AH, DH          ; > '9'?
    JLE WriteOut        ; no -- don't fix
    ADD AH, DL          ; fix it
WriteOut:
    ADD AX, BP          ; add '00' to digits to make them ASCii
    MOV BH, AH          ; save ones digit for next character
    MOV AH, BL          ; get attribute
    STOSW               ; write digit
    MOV AL, BH          ; get next
    STOSW               ; write
    MOV AL, ' '         ; write space
    STOSW
    CMP CX, 9           ; between the 8th and 9th HEX digits
    JE AddSpace
    LOOP LoopTop
    JMP SHORT OttaHere
EVEN
AddSpace: STOSW
    LOOP LoopTop

OttaHere:
    POP DI              ; restore registers
    POP SI
    POP DS
    POP BP
    RET 16       ; shave off 16 bytes of passed in parameters
SuperHex ENDP

MemCopy PROC FAR
    PUSH BP
    MOV BP, SP          ; set up stack frame
    PUSH DS
    PUSH SI
    PUSH DI

    CLD
    MOV CX, [BP+6]      ; # to copy in CX
    LES DI, [BP+8]      ; get dest.
    LDS SI, [BP+12]     ; get source

    SHR CX, 1           ; odd byte
    JNC CopyStart
    MOVSB
CopyStart: REP MOVSW    ; do copy

    POP DI
    POP SI
    POP DS
    POP BP
    RET 10
MemCopy ENDP

Scroll PROC FAR
    PUSH BP
    MOV BP, SP      ; set up stack frame

; BP+8 = MoveUp?   BP+6 = attribute

    MOV BH, [BP+6]  ; load up attribute
    MOV AX, 0601h   ; 1 line
    CMP BYTE PTR [BP+8], 0    ; zero?
    JE Down        ; yes; go up
    INC AH          ; go down (AX=0701)
Down:
    MOV CX, 0202h   ; (3,3) top left
    MOV DX, 0164Ch  ; (23,77) bottom right
    INT 010h        ; call vid. interrupt
    POP BP
    RET 4

Scroll ENDP
    CODE ENDS
END
;---------8<-----[ End SUPERHEX.ASM ]----->8-----------

' Compile SUPERHEX.ASM under TASM or MASM to make SUPERHEX.OBJ
'
' Then to make a LIB and QLB out of this file:
'
' LIB SUPERHEX +SUPERHEX;
' LINK /QUI SUPERHEX.LIB,,,c:\qb\bqlb45;
'                          qbxqlb        for PDS (I think)
'                          vbdosqlb      for VBDOS
' Load into QB:
'    QB BROWSER /L SUPERHEX /AH
'
' Instructions on how to make EXE file is in the BASIC source.
'

