clr=256                         ;code for "clear alphabet"
eof=257                         ;code for "end of file"
w equ word ptr
b equ byte ptr

data segment public
  extrn buffer:dataptr					;pointer to destination buffer
  extrn gifname:dataptr         ;name of Gif, incl. ".gif" + db 0
  extrn errorno:word;           ;flag for error

  palette db 768 dup (?)        ;destination palette

  handle     dw 0               ;DOS handle for Gif file
  Buf        db 768 dup (0)     ;buffer of read/input data
  BufInd     dw 0               ;pointer within this buffer
  abStack    db 1281 dup (0)    ;stack for decoding a byte
  ab_prfx    dw 4096 dup (0)    ;alphabet, prefix portion
  ab_tail    dw 4096 dup (0)    ;alphabet, tail portion
  free       dw 0               ;next free position in alphabet
  width1     dw 0               ;number of bits in a byte
  max        dw 0               ;maximum alphabet length for current width
  stackp     dw 0               ;pointer within alphabet stack 
  restbits   dw 0               ;number of bits remaining to be read 
  restbyte   dw 0               ;number of bytes still in buffer
  specialcase dw 0               ;buffer for special case
  cur_code   dw 0               ;code just processed
  old_code   dw 0               ;previous code
  readbyt    dw 0               ;byte just read
  lbyte      dw 0               ;last physical byte read
data ends


code segment public
assume cs:code,ds:data
.286

public readgif
GifRead proc pascal n:word
;reads n physical bytes from file
  mov ax,03f00h                 ;function 3fh of Interrupt 21h: read
  mov bx,handle                 ;load handle
  mov cx,n                      ;load number of bytes to read
  lea dx,buf                    ;pointer to destination buffer
  int 21h                       ;execute interrupt
  ret
gifread endp

GifOpen proc pascal
;opens the Gif file for read access
  mov ax,03d00h                 ;function 3dh: open
  lea dx,gifname + 1            ;pointer to name (skip length byte)
  int 21h                       ;execute
  mov handle,ax                 ;save handle
  ret
gifopen endp

GifClose proc pascal
;closes Gif file
  mov ax,03e00h                 ;function 3eh: close
  mov bx,handle                 ;load handle
  int 21h                       ;execute
  ret
gifclose endp

GifSeek proc pascal Ofs:dword
;positioning within the file
  mov ax,04200h                 ;function 42h,
  mov bx,w handle               ;subfunction 0: seek relative to start of file
  mov cx,word ptr Ofs + 2       ;load offset
  mov dx,word ptr Ofs
  int 21h                       ;execute
  ret
Endp
ShiftPal proc pascal
;aligns 24-bit palette format to 18-bit VGA format
  mov ax,ds                     ;source and destination arrays in the data segment
  mov es,ax
  mov si,offset Buf             ;read from data buffer
  lea di,palette                ;write to palette
  mov cx,768d                   ;copy 786 bytes
@l1:
  lodsb                         ;get bytes
  shr al,2                      ;convert
  stosb                         ;and write
  loop @l1
  ret
Endp
FillBuf proc pascal
;reads a block from the file in buf
  call gifread pascal,1         ;read one byte
  mov al,b buf[0]               ;load length after al
  xor ah,ah
  mov w restbyte,ax             ;and store in restbyte
  call gifread pascal, ax       ;read bytes
  ret
Endp

GetPhysByte proc pascal
;gets a physical byte from the buffer
  push bx                       ;caller needs bx
  cmp w restbyte,0              ;no more data in buffer ?
  ja @restthere
  pusha                         ;then refill buffer
  call fillbuf
  popa
  mov w bufind,0                ;and reset pointer
@restthere:                        ;data in buffer
  mov bx,w BufInd               ;load buffer pointer
  mov al,b Buf[bx]              ;get byte
  inc w bufind                  ;move pointer
  pop bx                        ;finished
  ret
Endp

GetLogByte proc pascal
;gets a logical byte from the buffer, uses GetPhysByte
  push si                       ;caller needs si 
  mov ax,w width1               ;get byte width
  mov si,ax                     ;and store
  mov dx,w restbits             ;shift lbyte 8 remaining bits to the right
  mov cx,8
  sub cx,dx                     ;obtain difference
  mov ax,w lByte
  shr ax,cl                     ;and shift
  mov w cur_code,ax             ;store code
  sub si,dx                     ;remaining bits already obtained -> subtract
@nextbyte:
  call getphysbyte              ;get new byte
  xor ah,ah
  mov w lByte,ax                ;store in lbyte for next logical byte
  dec w restbyte                ;mark byte as obtained 

  mov bx,1                      ;mask remaining bits in obtained byte 
  mov cx,si                     ;add number of bits 
  shl bx,cl                     ;shift 1 by number 
  dec bx                        ;and decrement
  and ax,bx                     ;mask byte

  mov cx,dx                     ;shift to correct position
  shl ax,cl                     ;(by remaining bytes to the left) 
  add w cur_code,ax             ;and add to result 

  sbb dx,w width1               ;decrement remaining bits 
  add dx,8                      ;by amount exceeding 8 bits
  jns @positive
  add dx,8
@positive:
  sub si,8                      ;up to 8 bits obtained -> subtract
  jle @finished                   ;<= 0 -> finished, end
  add dx,w width1               ;otherwise increment remaining bits by missing bits 
  sub dx,8
  jmp @nextbyte                 ;and continue
@finished:
  mov w restbits,dx             ;store remaining bits for next call 
  mov ax,w cur_code             ;and load ax
  pop si
  ret
Endp

ReadGif proc pascal
;loads a Gif image called gifname in buffer
  push ds                       ;store ds
  call GifOpen                  ;open file
  jnc ok                        ;error ?
  mov errorno,1                 ;then issue message and end
  pop ds
  ret

ok:
  call gifseek pascal, 0,13d    ;skip first 13 bytes
  push 768d                     ;load 768 bytes of palette
  call gifread
  call shiftpal                 ;and convert to "palette"
  call gifread pascal,1         ;skip one byte 

@extloop:                       ;skip extension blocks
  cmp w buf[0],21h              ;another extension block existing ?
  jne @noext                    ;no, then continue
    call gifread pascal,2       ;read first two bytes 
    mov al,b buf[1]             ;data block length 
    inc al                      ;increment by one 
    xor ah,ah
    call gifread pascal, ax     ;and skip
  jmp @extloop

@noext:
  call gifread pascal, 10d      ;read remainder of IDB
  test b buf[8],128             ;local palette ?
  je @noloc                     ;no, then continue
    push 768                    ;otherwise read 
    call gifread
    call shiftpal               ;and set

@noloc:
  les di,dword ptr buffer       ;load destination address

  mov w lbyte,0                 ;last read byte 0
  mov w free,258                ;first free entry 258
  mov w width1,9                ;byte width 9 bits
  mov w max,511                 ;so maximum entry is 511
  mov w stackp,0                ;stack pointer to beginning
  mov w restbits,0              ;no remaining bits 
  mov w restbyte,0              ;or remaining bytes to obtain 
@mainloop:                      ;run for each logical byte 
  call getlogByte               ;get logical byte
  cmp ax,eof                    ;end of file - ID
  jne @no_abort
  jmp @abort                  ;yes, then end
@no_abort:
  cmp ax,clr                    ;clr-code ?
  jne @no_clear
  jmp @clear                    ;yes, then clear alphabet
@no_clear:
  mov w readbyt,ax              ;store current byte 
  cmp ax,w free                 ;code already in alphabet (<free)
  jb @code_in_ab                ;yes, then output
  mov ax,w old_code             ;no, then special case, i.e., give last string
  mov w cur_code,ax             ;for processing
  mov bx,w stackp
  mov cx,w specialcase           ;and add first character (always concrete)
  mov w abstack[bx],cx          ;enter onto stack
  inc w stackp                  ;move stack pointer forward
@code_in_ab:                    ;code exists in alphabet:
  cmp ax,clr                    ;< clr code ?
  jb @concrete                   ;then concrete character
@fillstack_loop:                ;otherwise decode
  mov bx,w cur_code             ;current code as pointer in alphabet
  shl bx,1                      ;word-array (!)
  push bx
  mov ax,w ab_tail[bx]          ;get tail, (concrete)
  mov bx,w stackp               ;push onto stack 
  shl bx,1                      ;likewise word-array
  mov w abstack[bx],ax          ;enter
  inc w stackp
  pop bx
  mov ax,w ab_prfx[bx]          ;get prefix
  mov w cur_code,ax             ;give as current code for decoding
  cmp ax,clr                    ;> clr-code
  ja @fillstack_loop            ;then continue decoding
@concrete:                       ;now just the concrete values onto the stack
  mov bx,w stackp               ;push last code onto stack
  shl bx,1                      ;word-array
  mov w abstack[bx],ax
  mov w specialcase,ax           ;also keep for special case
  inc w stackp                  ;move pointer forward
  mov bx,w stackp               ;prepare to read stack
  dec bx                        ;move pointer backward and
  shl bx,1                      ;align with word-array
@readstack_loop:                ;process stack
  mov ax,w abstack[bx]          ;get character from stack
  stosb                         ;and write to destination memory

@noovl1:
  dec bx                        ;stack pointer to next element
  dec bx
  jns @readstack_loop           ;finished processing ? no, then continue
  mov w stackp,0                ;set stack pointer variable to 0
  mov bx,w free                 ;now enter in alphabet
  shl bx,1                      ;go to position "free" 
  mov ax,w old_code             ;write last code in prefix
  mov w ab_prfx[bx],ax
  mov ax,w cur_code             ;current code in tail
  mov w ab_tail[bx],ax
  mov ax,w readbyt              ;store byte read as last code
  mov w old_code,ax
  inc w free                    ;to next position in alphabet
  mov ax,w free
  cmp ax,w max                  ;maximum reached ?
  ja @no_mainloop
  jmp @mainloop                 ;no, then continue
@no_mainloop:
  cmp b width1,12               ;has width1 reached 12 bits ?
  jb @no_mainloop2
  jmp @mainloop                 ;yes, then continue
@no_mainloop2:
  inc w width1                  ;otherwise increment
  mov cl,b width1               ;calculate new maximum value
  mov ax,1                      ;shift 1 by new width1 to the left
  shl ax,cl
  dec ax                        ;and decrement
  mov w max,ax                  ;enter
  jmp @mainloop                 ;and go back to main loop
@clear:                         ;reset alphabet:
  mov w width1,9                ;width1 back to original value
  mov w max,511                 ;reset maximum to 511
  mov w free,258                ;first free position at 258
  call getlogbyte               ;get next byte 
  mov w specialcase,ax           ;record as special case
  mov w old_code,ax             ;and also as last byte read
  stosb                         ;this value directly to memory, because concrete

@noovl2:
  jmp @mainloop                 ;go back to main loop
@abort:                       ;terminate through eof-code
  call gifclose                 ;close file
  pop ds                        ;end
  ret
Endp

public setpal
SetPal proc pascal
;sends the palette to the VGA
  push si
  mov si,offset palette         ;get address
  mov cx,256*3                  ;get number of colors
  xor al,al
  mov dx,03c8h                  ;external palette RAM, pixel write address
  out dx,al                     ;set from color 0
  inc dx                        ;pixel color value

  rep outsb                     ;send all colors to VGA
  pop si
  ret
Endp
code ends
end

