;**************************************************************************
;Written 1/27/88-2/6/88 by Jim Griebel
;Built as a Turbo external OBJ file, this routine replaces READCODE,
;ADDTOPIXEL and all the high-level LZW decoding done by GIFSLOW.
;**************************************************************************

data        segment word public
            assume ds:data

;definitions for Turbo variables used in this module

            extrn clear:byte,interlace:byte,pass:byte
            extrn freecode:word,clearcode:word,bitmask:word
            extrn width:word,maxcode:word,codesize:word
            extrn readmask:word,eofcode:word,height:word
            extrn bitsperpixel:byte
            extrn linestart:word
            extrn nextraster:byte
            extrn raster:dword
            extrn palette:byte

;And for variables declared locally

            bitoffset  dw 0
            bitoffset2 dw 0
            yc      dw ?
            oldcolor db ?
            yoffset dw ?
            xoffset dw ?
            curcode dw ?
            incode  dw ?
            oldcode dw ?
            finchar db ?
            rasterseg dw ?
            rasterofs dw ?
            prefix  dw 4096 dup (?)
            suffix  db 4096 dup (?)
            outcode db 1024 dup (?)
            scanline db 640 dup (?)

data        ends

cseg        segment byte public

            assume cs:cseg

;We retain some Turbo calls just to keep things simple

            extrn doclear:near
            extrn moveup:near

            public nlzw

nlzw        proc   near

;Here's where the fun starts. Do the necessary preliminaries, including
;setting constants and variable starting points since Turbo won't let
;us declare them

            mov    byte ptr oldcolor,255 ;Set oldcolor to a null value
            xor    ax,ax            ;Set bitoffset to start of file
            mov    bitoffset,ax
            mov    bitoffset2,ax
            mov    yoffset,ax       ;YOFFSET to origin of screen RAM
            mov    xoffset,ax       ;Ditto for XOFFSET
            mov    yc,ax            ;Zero out Y-coord

;Here's the main loop where the file is decompressed and displayed

top:        call   readcode         ;Extract GIF compression code
            push   ds
            pop    es
            lea    di,outcode       ;Aim DI at the output queue
            mov    bx,bitmask       ;Get the bitmask in BX
            xor    dx,dx            ;Zero out DX, used as output counter
            cmp    ax,eofcode       ;Are we at the end of image?
            jnz    ckclear          ;Nope, press on
            jmp    finis            ;Yes, done, exit
ckclear:    cmp    ax,clearcode     ;Did we read a CLEAR code?
            jnz    noclear          ;Nope
            inc    byte ptr clear   ;Yes, turn on the CLEAR flag
            push   es
            push   di
            push   dx
            push   bx
            call   doclear          ;Reset values to initial
            call   readcode         ;Read raw data
            pop    bx
            pop    dx
            pop    di
            pop    es
            mov    oldcode,ax       ;Set OLDCODE to value just read
            jmp    done             ;and go put that out
noclear:    mov    curcode,ax       ;Otherwise, check for new data
            mov    incode,ax
            cmp    ax,freecode      ;See if >= freecode, if so
            jl     notnew           ;Not new, already in table
            mov    ax,oldcode       ;Otherwise curcode=oldcode
            mov    curcode,ax
            mov    al,finchar       ;stack last char decoded for output
            stosb
            inc    dx               ;increment the count
notnew:     mov    ax,curcode       ;Run the hash table for this value
            cmp    ax,bx            ;Current code less than BITMASK?
            jle    done             ;Yes, bail out
loop:       mov    si,ax
            mov    al,[si+suffix]   ;otherwise stack suffix [curcode]
            stosb                   ;for output
            inc    dx
            shl    si,1
            mov    ax,[si+prefix]   ;and set curcode=prefix [curcode]
            mov    curcode,ax
            cmp    ax,bx            ;If not done, continue
            jg     loop
done:       and    ax,bx            ;Mask off actual data
            mov    finchar,al       ;Save as FINCHAR
            stosb                   ;And put it on output queue
            mov    si,di            ;Outqueue pointer to SI
            dec    si               ;Back up to valid data
            mov    cx,dx            ;Output count to CX
            inc    cx               ;Make good count for LOOP
            push   es
            std
            push   ds               ;Point ES:DI to SCANLINE
            pop    es
            lea    di,scanline
            add    di,xoffset       ;Point into current pos in SCANLINE
            mov    dx,xoffset       ;DX now serves as pos counter
scanloop:   lodsb                   ;get byte from output queue. Stacked
            stosb                   ;LIFO, so deal with it that way
            inc    di               ;overcome auto-decrement
            inc    di
            inc    dx               ;At end of current scan line?
            cmp    dx,width
            jnz    scanloop1        ;Nope, press on
            call   putout           ;Yup, put out the scan line
scanloop1:  loop   scanloop         ;Continue for length of outqueue
            mov    xoffset,dx       ;Save new pos
            cld                     ;Dir flag back to normal
            pop    es               ;ES back to DS
noout:      cmp    byte ptr clear,0 ;Are we doing a CLEAR?
            jz     goon             ;No, proceed
            dec    byte ptr clear   ;Yes, turn off CLEAR flag
            jmp    top              ;and take the short way out
goon:       mov    di,freecode      ;suffix[freecode]=finchar
            mov    al,finchar
            mov    [di+suffix],al
            shl    di,1             ;Adjust for word-sized array
            mov    ax,oldcode       ;Prefix [freecode]=oldcode
            mov    [di+prefix],ax
            mov    ax,incode        ;Set Oldcode=Incode
            mov    oldcode,ax
            mov    ax,freecode      ;Kick to next free code
            inc    ax
            mov    freecode,ax
            cmp    ax,maxcode       ;Increase code size if necessary
            jge    incmax           ;(and possible, 12 is limit)
            jmp    top
incmax:     mov    ax,maxcode       ;maxcode = maxcode * 2
            shl    ax,1
            mov    maxcode,ax
            mov    ax,codesize      ;If codesize < 12, increment it
            cmp    ax,12
            jnz    inccode
            jmp    top
inccode:    inc    ax
            mov    codesize,ax
            mov    cl,al            ;Compute new READMASK value
            xor    ch,ch
            mov    ax,1
            shl    ax,cl
            dec    ax
            mov    readmask,ax
            jmp    top
finis:      ret                      ;Whole image done, exit

nlzw        ENDP

;Put out the array of pixels stored in SCANLINE

putout      proc   near
            push   es                ;Push sensitive regs
            push   cx
            push   ds
            push   si
            cld                      ;Because caller does STD
            mov    ax,0a000h         ;EGA video segment address
            mov    es,ax
            mov    bl,80h            ;Current pos in byte
            mov    bh,oldcolor
            mov    di,yoffset        ;Current Y pos in screen RAM
            lea    si,scanline
            mov    dx,3CEH           ;DX gets EGA register address
            mov    cx,width
outloop:    lodsb                    ;Get byte from output queue
            cmp    al,15
            jle    outgo
            push   si
            xor    ah,ah
            mov    si,ax
            mov    al,[si+palette]
            pop    si
outgo:      cmp    al,bh             ;Same as old color?
            jz     skipit            ;Yes, no need to tell EGA new one
            mov    bh,al             ;Set new color as old color
            xchg   ah,al
            xor    al,al             ;Index 0
            out    dx,ax             ;Put out both values at once
skipit:     mov    al,8              ;Now point to mask reg
            mov    ah,bl
            out    dx,ax
            mov    al,es:[di]        ;Latch data by read/write memory
            stosb
            dec    di                ;compensate for auto-increment
            shr    bl,1              ;Shift mask to next bit pos
            or     bl,bl             ;Out of mask?
            jnz    loop1             ;No, skip
            mov    bl,80H            ;Yes, reset mask to top bit
            inc    di                ;DI to next screen byte
loop1:      loop   outloop           ;And keep on
            call   incyc             ;Kick YC and Yoffset to next line
            call   computey
            lea    di,scanline       ;Point DI back to start of scanline
            xor    dx,dx             ;Pos pointer back to 0
            std                      ;set this back the right (wrong) way
            mov    oldcolor,bh       ;save oldcolor for next time
            pop    si
            pop    ds
            pop    cx
            pop    es
            ret

putout      endp

;Compute starting position of this screen line in EGA memory. Just gets
;appropriate value from LINESTART

computey    proc   near
            push   ax
            push   si
            mov    ax,yc
            shl    ax,1
            lea    si,linestart
            add    si,ax
            lodsw
            mov    yoffset,ax
            pop    si
            pop    ax
            ret
computey    endp

;Increment YC if the picture is non-interlaced, otherwise cope with
;interlace

incyc       proc   near
            mov    ax,yc
            cmp    ax,479     ;Quit if we'll overflow EGA bounds
            jnz    incit      ;otherwise increment
            ret
incit:      cmp    byte ptr interlace,0
            jz     simple
            cmp    byte ptr pass,0
            jnz    in2
            add    ax,8
            mov    yc,ax
            cmp    ax,word ptr height
            jl     fin
            inc    byte ptr pass
            mov    word ptr yc,4
            ret
in2:        cmp    byte ptr pass,1
            jnz    in3
            add    ax,8
            mov    yc,ax
            cmp    ax,word ptr height
            jl     fin
            inc    byte ptr pass
            mov    word ptr yc,2
            ret
in3:        cmp    byte ptr pass,2
            jnz    in4
            add    ax,4
            mov    yc,ax
            cmp    ax,word ptr height
            jl     fin
            inc    byte ptr pass
            mov    word ptr yc,1
            ret
in4:        add    ax,2
            mov    yc,ax
            ret
simple:     inc    ax
            mov    yc,ax
fin:        ret
incyc       endp

;This code replaces READCODE. Pick up a 24-bit chunk from the RASTER
;array and make a code of the necessary size out of it. Cope with files
;larger than 64000 bytes here also

readcode    proc   near
            mov    ax,bitoffset
            mov    dx,bitoffset2
            add    ax,codesize
            adc    dx,0
            xchg   ax,bitoffset
            xchg   dx,bitoffset2
            mov    cx,8
            div    cx
            push   ax
            push   ds
            lds    si,raster
            add    si,ax
            les    bx,[si]
            mov    ax,es
            pop    ds
            mov    cx,dx
            jcxz   noshift
shift:      shr    al,1
            rcr    bx,1
            loop   shift
noshift:    and    bx,readmask
            pop    ax
            cmp    byte ptr nextraster,1
            jnz    rdone
            cmp    ah,0f6h
            jnz    rdone
            push   bx
            mov    ax,ds
            push   ax
            lea    ax,bitoffset
            push   ax
            call   moveup
            pop    bx
rdone:      mov    ax,bx
            ret
readcode    endp

nlzw        endp
            cseg   ends
            end    nlzw
            end