Metropoli BBS
VIEWER: view.asm MODE: TEXT (ASCII)
; 128 line wide file viewer, requires 80186, EGA
; Written by Tylisha C. Andersen

o           equ <offset>
b           equ <byte ptr>
w           equ <word ptr>

.model tiny
.186

;---------------------------------------------------------------------
.data

fontcomp    equ   this byte
    dw 0,0,35584,47779,53757,49147,50525,20703,-3521,56,15904,14562
    dw 29440,-2113,16,7537,4343,0,8,16,-2049,-4097,255,-6380,40,-5121
    dw 55064,6655,23256,2128,40331,4119,19204,-2864,4128,53835,9461
    dw 21520,17782,4368,15812,28876,12304,-7236,568,2417,31991,20996
    dw 41108,2600,-9301,37970,18693,18836,3088,0,14343,28942,-2295,-7041
    dw 2417,4338,8452,18184,1148,15904,2082,8192,33342,32,8192,16399
    dw 0,41022,40,7200,47183,-2017,-3555,1080,0,0,16896,32784,2080,421
    dw 64,20992,-2667,2684,6513,2062,8,1060,4624,11426,20618,8458,32768
    dw 0,8513,16388,17412,17416,4112,15504,12489,8192,16958,16,0,256
    dw 4,60,0,0,0,4104,17416,4128,11667,26665,49676,33808,7200,2195
    dw 8233,37662,10520,3080,48977,2081,34562,51589,3080,14723,18441
    dw 5900,9352,2064,6547,18473,37644,8477,3080,64,4,8192,512,2048
    dw 40992,8322,2,-8191,120,1092,4164,37648,8200,1040,11667,22568
    dw 37644,10557,4680,14743,18473,37660,2337,3136,9623,18473,34588
    dw 2233,7744,47495,16392,37648,2349,3144,48532,18473,18194,33808
    dw 7200,33809,18473,37900,18865,4688,8580,16392,-3042,10661,4680
    dw 42452,18537,37650,10533,3144,14743,16424,37648,9765,1624,14743
    dw 18473,37650,2329,3080,51239,4162,37892,10661,3144,42388,18474
    dw 37896,12197,4680,39316,18473,37906,8857,1040,34839,8232,34590
    dw 2081,7232,4224,4226,9986,16904,7184,147,0,0,0,7680,66,64,0,51460
    dw 3640,9604,18633,28,51492,3136,42000,18665,14,51236,3192,14417
    dw 8324,8,-6364,33353,9604,18633,530,33809,7200,33792,2401,33938
    dw 10793,4704,4166,8324,28,10557,4680,9472,18633,18,51492,3144,9472
    dw 19150,16,-6364,16968,9472,16584,16,-7904,7216,4418,8388,4,10533
    dw 3656,9472,18474,8,12069,4680,9472,12329,18,10021,33353,1280,4324
    dw 16670,33824,1056,66,8324,17416,33800,4128,16563,0,0,18708,7752
    dw 41347,16647,1156,10661,3656,41985,30920,1804,51588,3640,33796
    dw 14537,1550,51460,3640,1027,14537,14,-6368,33857,41991,30920,1036
    dw 51364,3192,9222,30920,524,49800,3600,34823,4290,1550,49672,3600
    dw 41988,30921,25362,51492,4728,41217,28904,30,-1527,8060,48547
    dw 20554,1814,51620,3144,41988,18633,1548,51492,3144,42247,18473
    dw 1550,10533,3656,42244,19239,1154,-5723,7752,42244,18473,8478
    dw -6368,33856,14417,41092,21534,18268,1040,9623,23753,10257,17052
    dw 5137,33793,14537,270,49800,3600,41985,18633,268,10661,3656,47266
    dw 18441,41490,2980,4712,40017,96,20736,16412,0,4098,16521,12,2108
    dw 64,15360,2049,37888,16661,1860,5012,52291,257,16904,1040,10280
    dw 43170,40960,43530,40,658,4233,27684,9371,14041,30395,47067,8557
    dw 16904,33808,2081,28738,8580,49673,33904,37970,27045,74,1280,19065
    dw 2304,28866,21124,42373,19049,37970,10661,74,-6907,19049,34130
    dw 30880,20992,41108,120,2337,28864,0,512,33904,2081,7232,8448,16392
    dw 124,0,31746,8580,16904,33820,0,31744,8448,16904,33916,2081,7282
    dw 21124,42388,18989,36946,15536,0,-2800,18989,33106,31920,0,-2815
    dw 19053,36946,11701,74,-4095,124,33106,28085,8522,-4095,124,37970
    dw 31904,0,-3583,33916,0,32005,21066,41108,60,2081,7280,0,29192
    dw 33820,0,15621,21066,42388,19069,2337,31986,8580,16392,112,0,7170
    dw -124,-1,-1,0,-241,-6145,52793,40051,52793,40051,-25,-3841,0,9544
    dw 55396,-8192,10553,5194,8688,16424,16,-2795,2600,34967,8329,30
    dw -2524,72,4608,9879,8,41512,2064,9456,18630,24606,10557,3144,9072
    dw 10261,12315,35080,3176,10752,21735,4096,-6358,2132,14704,16392
    dw 24590,10533,4680,15600,15,8448,-4087,7952,6244,16416,24606,153
    dw 7688,34856,4178,8580,18952,2128,15904,2,20480,18945,40,2129,160
    dw 0,8,16,0,4096,8448,18152,3152,5216,160,4864,16412,0,7168,14567
    dw 0,0,0

;---------------------------------------------------------------------

nomem$      db    'not enough memory to load',          13, 10, '$'
syntax$     db    'syntax:  VIEW <file>',               13, 10, '$'
notfound$   db    'file not found',                     13, 10, '$'
null_str    db    0

;---------------------------------------------------------------------
.data?

font        db    2048 dup(?)       ; the font buffer

max_lines   dw    ?                 ; maximum number of lines to load
num_lines   dw    ?                 ; number of lines in file
first_line  dw    ?                 ; pointer to first line

buf_size    equ   8192              ; buffer size

            db    2 dup(?)          ; spaces to precede filename
filename    db    128 dup(?)        ; file name
buffer      db    buf_size dup(?)   ; disk buffer

handle      dw    ?                 ; file handle
buffer_pos  dw    ?                 ; position in buffer
buffer_end  dw    ?                 ; end of data in buffer

;---------------------------------------------------------------------
.code
org 100h

start:      jmp   main

;---------------------------------------------------------------------
; exit conditions from main
;---------------------------------------------------------------------

m_error:    mov   ah, 9             ; print out string
            int   21h
m_exit:     mov   ax, 4C00h         ; exit to DOS
            int   21h

m_nomem:    mov   dx, o nomem$      ; not enough memory
            jmp   m_error

m_syntax:   mov   dx, o syntax$     ; syntax error
            jmp   m_error

m_notfound: mov   dx, o notfound$   ; file not found
            jmp   m_error

;---------------------------------------------------------------------
; main - main procedure
;---------------------------------------------------------------------

main:       mov   sp, o stack_top   ; set new stack position

            mov   ah, 4Ah           ; shrink memory to minimum
            mov   bx, o stack_top+15
            shr   bx, 4
            int   21h
            jc    m_nomem           ; out of memory?

            mov   ah, 48h           ; get total memory free
            mov   bx, -1
            int   21h
            mov   ah, 48h           ; allocate all free memory
            int   21h

            mov   first_line, ax    ; set first line segment
            shr   bx, 3             ; get maximum line count
            sub   bx, 40
            jle   m_nomem           ; out of memory?
            mov   max_lines, bx     ; set maximum line count

            lea   cx, [bx+39]       ; cx = total line count

m_cloop:    mov   es, ax            ; set all lines to a null string
            mov   b es:[0], 0
            add   ax, 8
            loop  m_cloop

;-----------------------------------------------------------

            push  ds                ; es = ds
            pop   es

            mov   cl, ds:[80h]      ; cx = command line length
            xor   ch, ch
            inc   cx

            mov   di, 81h           ; find first non-space
            mov   al, ' '
            repe  scasb
            lea   si, [di-1]        ; si = pointer to filename
            cmp   b [si], 0Dh       ; no file specified?
            je    m_syntax

            repne scasb             ; find next space
            mov   b [di-1], 0       ; add the null terminator

            mov   di, o filename    ; di = filename buffer
            mov   ah, 60h           ; convert to full path
            int   21h

            mov   ax, 2020h         ; set preceding bytes to spaces
            mov   w filename-2, ax

;-----------------------------------------------------------

            mov   dx, o filename    ; open the file
            mov   ax, 3D00h
            int   21h
            jc    m_notfound        ; jump if error
            mov   handle, ax        ; save handle

            mov   bp, first_line    ; bp = segment of first line

            mov   ax, buf_size      ; init buffer variables
            mov   buffer_pos, ax
            mov   buffer_end, ax

            mov   num_lines, 0      ; line counter = 0

m_lineloop: mov   es, bp            ; es:di = line
            xor   di, di

            mov   cx, 127           ; 127 max. chars

m_charloop: call  getchar           ; get character
            jc    m_chardone        ; check for EOF
            cmp   al, 13            ; cr = done with line
            je    m_chardone
            cmp   al, 10            ; ignore line feeds
            je    m_charloop
            jcxz  m_charloop        ; maxed out, don't store
            stosb                   ; store char
            dec   cx                ; decrement maximum
            jmp   m_charloop        ; loop back

m_chardone: mov   al, 0             ; make string null-terminated
            stosb                   ;  (doesn't affect flags)

            inc   num_lines         ; increment line counter
            jc    m_cont            ; jump if EOF

            add   bp, 8             ; next line
            mov   ax, num_lines     ; too many lines?
            cmp   ax, max_lines
            jb    m_lineloop        ; loop if not

;-----------------------------------------------------------

m_cont:     mov   bx, handle        ; close the file
            mov   ah, 3Eh
            int   21h

            push  ds                ; es = ds
            pop   es

            mov   si, o fontcomp    ; si = compressed font
            mov   di, o font        ; di = font buffer
            mov   bp, 256           ; bp = size
            jmp   m_decloop         ; jump to decloop

m_decstore: shr   al, 3             ; shift al and mask to 5 bits
            and   ax, 1F1Fh
            stosw                   ; store 2 bytes
            ret                     ; return

m_decloop:  lodsw                   ; bl:bh:cl:ch:dl = 5 byte font char
            xchg  bx,ax             ; 8 x 5 bits per line
            lodsw
            xchg  cx,ax
            lodsb
            xchg  dx,ax

            mov   ah, bl            ; ax = bytes 0 and 1
            mov   al, bh
            shr   ax, 3
            call  m_decstore        ; store bytes 0 and 1

            mov   ah, bh            ; ax = bytes 2 and 3
            mov   al, cl
            shr   ax, 1
            call  m_decstore        ; store bytes 2 and 3

            mov   ah, cl            ; ax = bytes 4 and 5
            mov   al, ch
            shl   ax, 1
            call  m_decstore        ; store bytes 4 and 5

            mov   ah, ch            ; ax = bytes 6 and 7
            mov   al, dl
            shl   ax, 3
            call  m_decstore        ; store bytes 6 and 7

            dec  bp                 ; loop
            jnz  m_decloop

;---------------------------------------------------------------------
; main key loop handling starts here
;---------------------------------------------------------------------

            mov   ax, 10h           ; set video mode 0Fh (640x350 mono)
            int   10h

            mov   si, o null_str    ; si = null string
            mov   al, -1            ; al = -1 for reverse video
            mov   dx, 42            ; clear line 42 to white
            call  putline

            mov   si, o filename-2  ; es:si = filename
            mov   dx, -1            ; title line
            call  putline           ; put line to screen

            mov   bp, -1            ; impossible value
            jmp   k_home            ; jump to 'home' key

;-----------------------------------------------------------

m_done:     mov   ax, 3             ; restore video mode
            int   10h
            jmp   m_exit            ; exit to DOS

;-----------------------------------------------------------

k_keyloop:  xor   ah, ah            ; wait for a key
            int   16h
            cmp   ah, 01h           ; quit if ESC
            je    m_done

            cmp   ah, 48h           ; key UP?
            je    k_up
            cmp   ah, 50h           ; key DOWN?
            je    k_down
            cmp   ah, 49h           ; key PGUP?
            je    k_pgup
            cmp   ah, 51h           ; key PGDN?
            je    k_pgdn
            cmp   ah, 47h           ; key HOME?
            je    k_home
            cmp   ah, 4Fh           ; key END?
            je    k_end
            jmp   k_keyloop         ; invalid key, loop

;-----------------------------------------------------------

k_up:       test  bp, bp            ; already at top?
            jz    k_keyloop
            call  scrolldn          ; scroll screen down
            dec   bp                ; move current line up

            imul  ax, bp, 8         ; ax = first line of screen
            add   ax, first_line
            mov   es, ax            ; es:si = line
            xor   si, si

            mov   dx, 2             ; screen line 2
            xor   al, al            ; white on black
            call  putline           ; put line to screen
            jmp   k_keyloop         ; loop

;-----------------------------------------------------------

k_down:     mov   ax, num_lines     ; ax = max line position
            dec   ax
            dec   ax
            cmp   bp, ax            ; already at bottom?
            jge   k_keyloop

            call  scrollup          ; scroll screen up
            inc   bp                ; move current line down

            imul  ax, bp, 8         ; ax = first line of screen
            add   ax, first_line
            add   ax, 39*8          ; ax = last line of screen
            mov   es, ax            ; es:si = line
            xor   si, si

            mov   dx, 41            ; screen line 41
            xor   al, al            ; white on black
            call  putline           ; put line to screen
            jmp   k_keyloop         ; loop

;-----------------------------------------------------------

k_pgup:     test  bp, bp            ; already at top?
            jz    k_keyjmp
            sub   bp, 40            ; move up 40 lines and redraw
            jnc   k_redraw
            xor   bp, bp
            jmp   k_redraw

;-----------------------------------------------------------

k_pgdn:     mov   ax, num_lines     ; ax = max line position - 41
            sub   ax, 41
            cmp   bp, ax            ; already at bottom?
            jge   k_keyjmp

            add   bp, 40            ; move down 40 lines and redraw
            jmp   k_redraw

;-----------------------------------------------------------

k_home:     test  bp, bp            ; already at top?
            jz    k_keyjmp
            xor   bp, bp            ; move to top and redraw
            jmp   k_redraw

;-----------------------------------------------------------

k_end:      mov   ax, num_lines     ; ax = max line position - 40
            sub   ax, 40
            cmp   bp, ax            ; already at bottom?
            jge   k_keyjmp

            mov   bp, ax            ; move to end and redraw
            jmp   k_redraw

;-----------------------------------------------------------

k_redraw:   imul  di, bp, 8         ; di = first line of screen
            add   di, first_line

            mov   dx, 2             ; first line is line 2
            xor   si, si            ; si = 0
            xor   al, al            ; white on black

k_rloop:    mov   es, di            ; es:si = line
            call  putline           ; put line to screen
            add   di, 8             ; next line
            inc   dx
            cmp   dx, 42            ; done all 40 lines?
            jb    k_rloop           ; loop if not

k_keyjmp:   jmp   k_keyloop         ; jump to key loop

;---------------------------------------------------------------------
; getchar - read one character from the file, buffered
;---------------------------------------------------------------------
; in:  assumes ds = cs
; out: al = character, carry = EOF

getchar:    pusha                   ; save registers

            mov   si, buffer_pos    ; ax = position in buffer
            cmp   si, buffer_end    ; if not to end, then load char
            jb    g_load

            cmp   si, buf_size      ; buffer not full = EOF
            jb    g_done            ; carry flag set if we jump

            mov   ah, 3Fh           ; try to read in next buffer
            mov   bx, handle
            mov   cx, buf_size
            mov   dx, o buffer
            int   21h
            jc    g_done            ; carry = EOF

            mov   buffer_end, ax    ; set end position
            xor   si, si            ; set buffer position
            mov   buffer_pos, si

g_load:     mov   al, buffer[si]    ; load byte
            inc   buffer_pos        ; increment buffer position
            clc                     ; clear carry flag

g_done:     mov   bp, sp            ; set al value on return
            mov   [bp+14], al
            popa                    ; restore registers
            ret

;---------------------------------------------------------------------
; putline - put a line of text on the screen
;---------------------------------------------------------------------
; in:  es:si = string, dx = y-position, al = xor byte (0 or -1)
;      y-position -1 = draw the title line

putline:    pusha                   ; save all registers
            push  ds
            push  es

            push  es                ; ds:si = string
            pop   ds

            push  0A000h            ; es = video memory
            pop   es

            imul  dx, 640           ; set up to clear the line
            mov   di, dx            ; dx, di = offset
            mov   ah, al            ; ax = xor-value
            mov   cx, 640/2         ; 8 lines of the screen

            test  dx, dx            ; if dx was negative, then
            jge   p_clear
            mov   dx, 240           ; use title position & larger size
            xor   di, di            ; for a 3-line border on each side
            mov   cx, 1120/2

p_clear:    rep   stosw             ; clear the line
            mov   di, dx            ; di = offset

            mov   bp, ax            ; bp = xor value
            xor   bx, bx            ; zero bx

p_yloop:    push  si                ; save si, di
            push  di
            xor   dx, dx            ; clear bit buffer
            mov   cl, 11            ; shift = 11 (16 - 5)

p_xloop:    lodsb                   ; load byte
            test  al, al            ; check for null
            jz    p_xdone
            xor   ah, ah            ; bx = font byte pointer
            shl   ax, 3
            add   bx, ax
            mov   ch, cs:font[bx]   ; ch = font byte
            sub   bx, ax            ; fix bx

            mov   al, ch            ; ax = font byte
            xor   ah, ah
            shl   ax, cl            ; shift left
            or    dx, ax            ; or into bit buffer

            sub   cl, 5             ; adjust shift value
            jnc   p_xloop           ; if bit buffer full, then

            mov   al, dh            ; shift 8 bits out of the
            shl   dx, 8             ; bit buffer into al
            add   cl, 8             ; adjust shift value
            xor   ax, bp            ; xor with xor-value
            stosb                   ; write to video memory
            jmp   p_xloop           ; loop

p_xdone:    mov   al, dh            ; flush bit buffer:
            mov   ah, dl            ; ax = dx (bytes reversed)
            xor   ax, bp            ; xor with xor-value
            stosw                   ; write to video memory

            pop   di                ; restore si, di
            pop   si
            add   di, 80            ; next scanline on screen
            inc   bx                ; increment line counter
            cmp   bx, 8             ; done all 8 lines?
            jb    p_yloop           ; loop if not

            pop   es                ; restore registers
            pop   ds
            popa
            ret                     ; return

;---------------------------------------------------------------------
; scrolldn - scroll all but the top 2 & bottom 1 lines down by 1 line
;---------------------------------------------------------------------

scrolldn:   pusha                   ; save all registers
            push  ds
            push  es

            mov   ax, 0A000h        ; ds, es = video memory
            mov   ds, ax
            mov   es, ax

            std                     ; moves are backwards
            mov   si, 2*640+24960-2 ; scroll the screen down
            mov   di, 3*640+24960-2
            mov   cx, 24960/2
            rep   movsw

            cld                     ; fix direction flag
            pop   es                ; restore registers
            pop   ds
            popa
            ret                     ; return

;---------------------------------------------------------------------
; scrollup - scroll all but the top 2 & bottom 1 lines up by 1 line
;---------------------------------------------------------------------

scrollup:   pusha                   ; save all registers
            push  ds
            push  es

            mov   ax, 0A000h        ; ds, es = video memory
            mov   ds, ax
            mov   es, ax

            mov   si, 3*640         ; scroll the screen up
            mov   di, 2*640
            mov   cx, 24960/2
            rep   movsw

            pop   es                ; restore registers
            pop   ds
            popa
            ret                     ; return

;---------------------------------------------------------------------
.data?

stack_buf   db    1024 dup(?)       ; stack buffer
stack_top:

end start

[ RETURN TO DIRECTORY ]