.286

clr=256                         ;Code fr "Alphabet l”schen"
eof=257                         ;Code fr "Datei-Ende"
w equ word ptr
b equ byte ptr

data segment public
  extrn gifname:dataptr         ;Name der Gif-Datei, incl. ".gif" + db 0
  extrn vscreen:dword           ;Zeiger auf Zielspeicherbereich
  extrn palette:dataptr         ;Zielpalette
  extrn vram_pos:word           ;Position innerhalb des Bildschirmspeichers
  extrn rest:word               ;Rest, der noch kopiert werden muá
  extrn errornr:word;           ;Flag fr Fehler

  handle     dw 0               ;DOS-Handle fr Gif-Datei
  Puf        db 768 dup (0)     ;Puffer der eingelesenen Daten
  PufInd     dw 0               ;Zeiger innerhalb dieses Puffers
  abStack    db 1281 dup (0)    ;Stack, zum Entschlsseln eines Bytes
  ab_prfx    dw 4096 dup (0)    ;Alphabet, Pr„fix-Teil
  ab_tail    dw 4096 dup (0)    ;Alphabet, Postfix-Teil
  free       dw 0               ;n„chste freie Position im Alphabet
  breite     dw 0               ;Anzahl Bit eines Bytes
  max        dw 0               ;Maximale Alphabet-L„nge bei akt. Breite
  stackp     dw 0               ;Zeiger innerhalb des Alphabet-Stacks
  restbits   dw 0               ;Anzahl noch zu lesender Bit
  restbyte   dw 0               ;Anzahl noch vorhandener Byte im Puffer
  sonderfall dw 0               ;Zwischenspeicher fr den Sonderfall
  akt_code   dw 0               ;gerade bearbeiteter Code
  old_code   dw 0               ;vorhergehender Code
  readbyt    dw 0               ;gerade gelesenes Byte
  lbyte      dw 0               ;zuletzt gelesenes physikalisches Byte
data ends

extrn p13_2_modex:far           ;wird beim šberlauf ben”tigt

code segment public
assume cs:code,ds:data

public readgif
GifRead proc pascal n:word
;liest n physikalische Bytes aus Datei
  mov ax,03f00h                 ;Funktion 3fh von Interrupt 21h: Lesen
  mov bx,handle                 ;Handle laden
  mov cx,n                      ;Anzahl zu lesender Bytes laden
  lea dx,puf                    ;Zeiger auf Zielpuffer
  int 21h                       ;Interrupt ausfhren
  ret
gifread endp

GifOpen proc pascal
;”ffnet die Gif-Datei zum Lese-Zugriff
  mov ax,03d00h                 ;Funktion 3dh: ™ffnen
  lea dx,gifname + 1            ;Zeiger auf Namen (L„ngenbyte berspringen)
  int 21h                       ;ausfhren
  mov handle,ax                 ;Handle sichern
  ret
gifopen endp

GifClose proc pascal
;schlieát Gif-Datei
  mov ax,03e00h                 ;Funktion 3eh: Schlieáen
  mov bx,handle                 ;Handle laden
  int 21h                       ;ausfhren
  ret
gifclose endp

GifSeek proc pascal Ofs:dword
;Positionierung innerhalb der Datei
  mov ax,04200h                 ;Funktion 42h,
  mov bx,w handle               ;Unterfunktion 0: Seek rel. zu Dateianfang
  mov cx,word ptr Ofs + 2       ;Offset laden
  mov dx,word ptr Ofs
  int 21h                       ;ausfhren
  ret
Endp
ShiftPal proc pascal
;gleicht das 24-Bit Palettenformat an das 18-Bit VGA-Format an
  mov ax,ds                     ;Quell- und Zielarrays im Datensegment
  mov es,ax
  mov si,offset Puf             ;Lesen aus Datenpuffer
  lea di,palette                ;Schreiben in Palette
  mov cx,768d                   ;786 Byte kopieren
@l1:
  lodsb                         ;Byte holen
  shr al,2                      ;konvertieren
  stosb                         ;und schreiben
  loop @l1
  ret
Endp
FillPuf proc pascal
;liest einen Block aus der Datei in Puf
  call gifread pascal,1         ;ein Byte lesen
  mov al,b puf[0]               ;L„nge nach al laden
  xor ah,ah
  mov w restbyte,ax             ;und in RestByte sichern
  call gifread pascal, ax       ;Bytes lesen
  ret
Endp

GetPhysByte proc pascal
;holt ein physikalisches Byte aus dem Puffer
  push bx                       ;bx wird vom Aufrufer ben”tigt
  cmp w restbyte,0              ;keine Daten mehr im Puffer ?
  ja @restda
  pusha                         ;dann Puffer neu fllen
  call fillpuf
  popa
  mov w pufind,0                ;und Zeiger zurck
@restda:                        ;Daten im Puffer
  mov bx,w PufInd               ;Puffer-Zeiger laden
  mov al,b Puf[bx]              ;Byte holen
  inc w pufind                  ;Zeiger weiter
  pop bx                        ;und fertig
  ret
Endp

GetLogByte proc pascal
;holt ein logisches Byte aus dem Puffer, benutzt GetPhysByte
  push si                       ;si wird vom Aufrufer ben”tigt
  mov ax,w breite               ;Byte-Breite holen
  mov si,ax                     ;und sichern
  mov dx,w restbits             ;lbyte um 8-Restbits nach rechts schieben
  mov cx,8
  sub cx,dx                     ;dazu Differenz bilden
  mov ax,w lByte
  shr ax,cl                     ;und shiften
  mov w akt_code,ax             ;Code sichern
  sub si,dx                     ;Restbits bereits geholt -> abziehen
@nextbyte:
  call getphysbyte              ;neues Byte holen
  xor ah,ah
  mov w lByte,ax                ;in lByte fr n„chstes logische Byte sichern
  dec w restbyte                ;Byte als geholt markieren

  mov bx,1                      ;restliche Bits in geholtem Byte maskieren
  mov cx,si                     ;dazu Anzahl Bits setzen
  shl bx,cl                     ;1 um Anzahl shiften
  dec bx                        ;und dekrementieren
  and ax,bx                     ;Byte maskieren

  mov cx,dx                     ;auf die richtige Position shiften
  shl ax,cl                     ;also um Restbits nach links
  add w akt_code,ax             ;und zum Ergebnis addieren

  sbb dx,w breite               ;Restbits vermindern
  add dx,8                      ;um das, was ber 8 Bit hinausgeht
  jns @positiv
  add dx,8
@positiv:
  sub si,8                      ;bis zu 8 Bit geholt -> abziehen
  jle @fertig                   ;<= 0 -> alles fertig, Ende
  add dx,w breite               ;ansonsten Restbits um fehlende Bits erh”hen
  sub dx,8
  jmp @nextbyte                 ;und weitermachen
@fertig:
  mov w restbits,dx             ;Restbits fr n„chsten Aufruf sichern
  mov ax,w akt_code             ;und ax laden
  pop si
  ret
Endp

ReadGif proc pascal
;L„dt ein Gif-Bild namens gifname in vscreen, šberlauf wird auf Bildschirm
;ausgelagert
  push ds                       ;ds sichern
  call GifOpen                  ;Datei ”ffnen
  jnc ok                        ;Fehler ?
  mov errornr,1                 ;dann melden und beenden
  pop ds
  ret

ok:
  call gifseek pascal, 0,13d    ;ersten 13 Byte berspringen
  push 768d                     ;768 Byte der Palette laden
  call gifread
  call shiftpal                 ;und nach "Palette" konvertieren
  call gifread pascal,1         ;ein Byte berspringen

@extloop:                       ;Extension-Blocks berlesen
  cmp w puf[0],21h              ;noch ein Extension-Block vorhanden ?
  jne @noext                    ;nein, dann weiter
    call gifread pascal,2       ;ersten beiden Bytes lesen
    mov al,b puf[1]             ;L„nge des Datenblocks
    inc al                      ;um eins erh”hen
    xor ah,ah
    call gifread pascal, ax     ;und berlesen
  jmp @extloop

@noext:
  call gifread pascal, 10d      ;Rest des IDBs lesen
  test b puf[8],128             ;lokale Palette ?
  je @nolok                     ;nein, dann weiter
    push 768                    ;ansonsten lesen
    call gifread
    call shiftpal               ;und setzen

@nolok:
  les di,dword ptr vscreen      ;Zieladresse laden

  mov w lbyte,0                 ;Letztes gelesenes Byte 0
  mov w free,258                ;erster freier Eintrag 258
  mov w breite,9                ;Byte-Breite 9 Bit
  mov w max,511                 ;damit maximaler Eintrag bei 511
  mov w stackp,0                ;Stack-Zeiger auf Beginn
  mov w restbits,0              ;keine Restbits
  mov w restbyte,0              ;oder Restbytes zu holen
@mainloop:                      ;fr jedes logische Byte durchlaufen
  call getlogByte               ;logisches Byte holen
  cmp ax,eof                    ;End of File - Kennung
  jne @no_abbruch
  jmp @abbruch                  ;ja, dann Ende
@no_abbruch:
  cmp ax,clr                    ;Clr-Code ?
  jne @no_clear
  jmp @clear                    ;ja, dann Alphabet l”schen
@no_clear:
  mov w readbyt,ax              ;aktuelles Byte sichern
  cmp ax,w free                 ;ist Code bereits im Alphabet (<free)
  jb @code_in_ab                ;ja, dann ausgeben
  mov ax,w old_code             ;nein, dann Sonderfall, also letzen String
  mov w akt_code,ax             ;zur Bearbeitung geben
  mov bx,w stackp
  mov cx,w sonderfall           ;und erstes Zeichen anh„ngen (immer konkret)
  mov w abstack[bx],cx          ;dieses auf Stack eintragen
  inc w stackp                  ;Stack-Pointer weiter
@code_in_ab:                    ;Code im Alphabet vorhanden:
  cmp ax,clr                    ;< Clr-Code ?
  jb @konkret                   ;dann konkretes Zeichen
@fillstack_loop:                ;ansonsten entschlsseln
  mov bx,w akt_code             ;dazu aktuellen Code als Zeiger im Alphabet
  shl bx,1                      ;Word-Array (!)
  push bx
  mov ax,w ab_tail[bx]          ;Tail holen, der ist konkret
  mov bx,w stackp               ;also auf Stack schieben
  shl bx,1                      ;ebenfalls Word-Array
  mov w abstack[bx],ax          ;eintragen
  inc w stackp
  pop bx
  mov ax,w ab_prfx[bx]          ;Prefix holen
  mov w akt_code,ax             ;als aktuellen Code zum Entschlsseln geben
  cmp ax,clr                    ;> Clr-Code
  ja @fillstack_loop            ;dann weiter entschlsseln
@konkret:                       ;jetzt nur noch konkrete Werte auf dem Stack
  mov bx,w stackp               ;letzten Code auf den Stack schieben
  shl bx,1                      ;Word-Array
  mov w abstack[bx],ax
  mov w sonderfall,ax           ;auch fr den Sonderfall vermerken
  inc w stackp                  ;Zeiger weiter
  mov bx,w stackp               ;Lesen des Stack vorbereiten
  dec bx                        ;Zeiger vermindern und
  shl bx,1                      ;auf Word-Array ausrichten
@readstack_loop:                ;Stack abarbeiten
  mov ax,w abstack[bx]          ;Zeichen vom Stack holen
  stosb                         ;und in Ziel-Speicher schreiben

  cmp di,0                      ;Segment-šberlauf ?
  jne @noovl1
  call p13_2_modex pascal,vram_pos,16384d
  add vram_pos,16384d           ;dann Teil in Bildschirmspeicher auslagern
  les di,dword ptr vscreen      ;Position im VGA-Ram weiter und Zielzeiger neu

@noovl1:
  dec bx                        ;Stack-Pointer auf n„chstes Element
  dec bx
  jns @readstack_loop           ;abgearbeitet ? nein, dann weiter
  mov w stackp,0                ;Stackpointer-Variable auf 0
  mov bx,w free                 ;jetzt in Alphabet eintragen
  shl bx,1                      ;dazu auf Position "free" positionieren
  mov ax,w old_code             ;letzten Code in Pr„fix schreiben
  mov w ab_prfx[bx],ax
  mov ax,w akt_code             ;aktuellen Code in Tail
  mov w ab_tail[bx],ax
  mov ax,w readbyt              ;gelesenes Byte als letzten Code sichern
  mov w old_code,ax
  inc w free                    ;auf n„chste Position innerhalb d. Alphabets
  mov ax,w free
  cmp ax,w max                  ;bereits Maximum erreicht ?
  ja @no_mainloop
  jmp @mainloop                 ;nein, dann einfach weitermachen
@no_mainloop:
  cmp b breite,12               ;Breite bereits 12 Bit ?
  jb @no_mainloop2
  jmp @mainloop                 ;ja, dann einfach weitermachen
@no_mainloop2:
  inc w breite                  ;sonst erh”hen
  mov cl,b breite               ;neuen Maximalwert berechnen
  mov ax,1                      ;1 um neue Breite nach links schieben
  shl ax,cl
  dec ax                        ;und dekrementieren
  mov w max,ax                  ;eintragen
  jmp @mainloop                 ;und zurck zur Hauptschleife
@clear:                         ;Alphabet zurcksetzen:
  mov w breite,9                ;Breite wieder auf Ursprungswert
  mov w max,511                 ;Maximum wieder bei 511
  mov w free,258                ;erste freie Position bei 258
  call getlogbyte               ;n„chstes Byte holen
  mov w sonderfall,ax           ;als Sonderfall vermerken
  mov w old_code,ax             ;und auch als zuletzt gelesenen
  stosb                         ;diesen Wert direkt in Speicher, weil konkret

  cmp di,0                      ;Segment-šberlauf ?
  jne @noovl2
  call p13_2_modex pascal,vram_pos,16384d
  add vram_pos,16384d           ;dann in Bildschirmspeicher auslagern
  les di,dword ptr vscreen      ;VGA-Ram Zeiger weiter und Startadresse neu

@noovl2:
  jmp @mainloop                 ;zurck zur Hauptschleife
@abbruch:                       ;Abbruch durch Eof-Code
  call gifclose                 ;Datei schlieáen
  mov rest,di                   ;Anzahl noch zu kopierender Bytes sichern
  pop ds                        ;und beenden
  ret
Endp

code ends
end

