.286
  w equ word ptr
  b equ byte ptr
data segment public
  extrn vscreen:dword
  extrn vpage:word
  extrn palette:dataptr
data ends


code segment public
assume cs:code,ds:data
public init_modex, p13_2_modex, squeeze, copyscreen, double
public clrx, split, setpal, getpal, switch, setstart, enter400
public waitretrace, fade_out, fade_to, copy_block, pal_rot

switch proc far                 ;schaltet zw. beiden Bildschirmseiten um
  mov bx,vpage                  ;Startadresse laden
  mov dx,3d4h
  mov al,0ch                    ;auf CRTC-Register 0dh/0ch aufteilen
  mov ah,bh
  out dx,ax                     ;Highbyte setzen (Register 0dh)
  inc al
  mov ah,bl
  out dx,ax                     ;Lowbyte setzen (Register 0ch)
  cmp bx,16000d                 ;Startadresse umschalten (0/16000)
  je setze0
  mov vpage,16000
  ret
setze0:
  mov vpage,0
  ret
switch endp

Init_ModeX proc pascal far      ;schaltet Mode X ein
  mov ax,0013h                  ;Mode 13h setzen
  int 10h

  mov dx,3c4h                   ;Timing Sequenzer
  mov al,4                      ;Register 4 (Memory Mode):
  out dx,al                     ;Bit 3 l”schen -> Chain4 aus
  inc dx
  in al,dx
  and al,0f7h
  or al,4h                      ;Bit 2 setzen -> Odd/Even Mode aus
  out dx,al
  dec dx

  mov ax,0f02h                  ;Register 2 (Write Plane Mask):
  out dx,ax                     ;0fh: alle Planes beim Schreiben ein
  mov ax,0a000h                 ;Bildschirmspeicher l”schen
  mov es,ax
  xor di,di
  xor ax,ax
  mov cx,0ffffh
  cld
  rep stosw

  mov dx,3d4h                   ;CRTC
  mov al,14h                    ;Register 14h  (Underline Row Adress):
  out dx,al
  inc dx
  in al,dx                      ;Bit 6 l”schen -> Doubleword adress. aus
  and al,0bfh
  out dx,al
  dec dx
  mov al,17h                    ;Register 17h (CRTC Mode):
  out dx,al                     ;Bit 6 setzen -> Byte Mode ein
  inc dx
  in al,dx
  or al,40h
  out dx,al
  ret
Endp

Enter400 proc pascal far        ;schaltet von Mode X (200 Zeilen)
  mov dx,3d4h                   ;in erweieterten 400-Zeilen-Modus
  mov al,9                      ;CRTC Register 9 (Maximum Row Adress)
  out dx,al                     ;selektieren
  inc dx                        ;Wert auslesen
  in al,dx
  and al,01110000b              ;Bit 7 und 3:0 l”schen
  out dx,al                     ;und zurckschreiben
  ret
Enter400 endp

plane_l: db 0
plane_pos: dw 0

p13_2_modex proc pascal far start,pic_size:word
    mov dx,03ceh                ;Write Mode 0 setzen
    mov ax,4005h                ;ber GDC Register 5 (GDC Mode)
    out dx,ax

    mov b plane_l,1             ;Plane-Maske speichern
    push ds
    lds si,dword ptr ds:vscreen ;Quelladresse laden
    mov w plane_pos,si          ;und sichern
    mov ax,0a000h               ;Zieladresse setzen
    mov es,ax
    mov di,start
    mov cx,pic_size             ;Anzahl holen
@lpplane:
    mov al,02h                  ;TS Register 2 (Write Plane Mask)
    mov ah,b plane_l            ;entsprechende Plane maskieren
    mov dx,3c4h
    out dx,ax

  @lp1:
    movsb                       ;Byte kopieren
    add si,3                    ;auf n„chstes Quellbyte positionieren
    loop @lp1

    mov di,start                ;Zieladresse neu holen
    inc w plane_pos             ;Quelladresse auf neuen Start
    mov si,w plane_pos
    mov cx,pic_size             ;Gr”áe holen
    shl b plane_l,1             ;n„chste Plane maskieren
    cmp b plane_l,10h           ;alle 4 Planes kopiert ?
    jne @lpplane

    pop ds
    ret
Endp
Split proc pascal far row:byte  ;Screen-Splitting in Zeile row
  mov bl,row
  xor bh,bh
  shl bx,1                      ;*2 wg. Zeilenverdopplung
  mov cx,bx

  mov dx,3d4h                   ;CRTC
  mov al,07h                    ;Register 7 (Overflow low)
  out dx,al
  inc dx
  in al,dx
  and al,11101111b              ;Bit 4 mit Bit 8 der Zeile laden
  shr cx,4
  and cl,16
  or al,cl
  out dx,al                     ;und setzen

  dec dx
  mov al,09h                    ;Register 9 (Maximum Row Adress)
  out dx,al
  inc dx
  in al,dx
  and al,10111111b              ;Bit 6 mit Bit 9 der Zeile laden
  shr bl,3
  and bl,64
  or al,bl
  out dx,al                     ;und setzen

  dec dx
  mov al,18h                    ;Register 18h (Line Compare/Split Screen)
  mov ah,row                    ;restlichen 8 Bit setzen
  shl ah,1
  out dx,ax
  ret
Endp
SetStart proc pascal far t:word ;setzt Bildschirmstart auf angegebene Adr.
  mov dx,3d4h                   ;CRTC
  mov al,0ch                    ;Register 0ch(Linear Starting Adress Middle)
  mov ah,byte ptr t + 1         ;Bits 15:8 setzen
  out dx,ax                     ;Register 0dh(LSA Low)
  mov al,0dh                    ;Bits 7:0 setzen
  mov ah,byte ptr t
  out dx,ax
  ret
Endp
WaitRetrace proc pascal far
  mov dx,3dah                   ;Input Status Register 1
@wait1:
  in al,dx                      ;Bit 3 wird 0 wenn Strahl beim Bildaufbau
  test al,08h
  jnz @wait1
@wait2:
  in al,dx                      ;Bit 3 wird 1 wenn Retrace
  test al,08h
  jz @wait2
  ret                           ;jetzt ist Strahl ganz unten am Bildschirm
Endp
public squeeze
squeeze proc pascal far         ;f„hrt Bildschirm zusammen
  mov si,200*80                 ;Start-Wert fr Startadresse
  mov di,199                    ;Start-Wert fr Split-Zeile
sqlp:                           ;Hauptschleife
  call waitretrace              ;auf Retrace warten
  call split pascal, di         ;Setzen der unteren H„lfte durch Splitting
  call setstart pascal, si      ;Setzen der oberen H„lfte durch Scrolling
  sub si,80                     ;eine Zeile weiter, also nach unten fahren
  dec di                        ;Split eine Zeile runter, also untere
  cmp di,99d                    ;H„lfte rauffahren
  jae sqlp                      ;fertig ?
  ret
squeeze endp
clrx proc pascal far pmask:byte ;L”scht Mode X - Seiten
  mov al,02h
  mov ah,pmask
  mov dx,3c4h
  out dx,ax
  mov ax,0a000h                 ;Startadresse und L„nge holen
  mov es,ax
  mov di,vpage
  xor ax,ax
  mov cx,8000
  rep  stosw                    ;und l”schen
  ret
clrx endp
copyscreen proc pascal far ziel,quelle:word
  mov dx,3c4h										;alle Planes selektieren
  mov ax,0f02h
  out dx,ax
  mov dx,3ceh                   ;Write-Mode 1 (kopieren)
  mov ax,4105h
  out dx,ax

  push ds
  mov ax,0a000h									;Quell- und Zielsegment im VGA
  mov es,ax
  mov ds,ax
  mov si,quelle                 ;Quell- und Zieloffset laden
  mov di,ziel
  mov cx,16000d                 ;16000 Byte (=64000 Pixel) kopieren
  rep movsb
  pop ds
  mov dx,3ceh                   ;Write-Mode 0
  mov ax,4005h
  out dx,ax
  ret
copyscreen endp
SetPal proc pascal far
  push si
  mov si,offset palette         ;Adresse holen
  mov cx,256*3                  ;Anzahl Farben holen
  xor al,al
  mov dx,03c8h                  ;External Palette RAM, Pixel Write Adress
  out dx,al                     ;ab Farbe 0 setzen
  inc dx                        ;Pixel Color Value

  rep outsb                     ;alle Farben an VGA schicken
  pop si
  ret
Endp

getpal proc pascal far
  push di
  mov di,offset palette         ;Adresse holen
  mov cx,256*3                  ;Anzahl Farben holen
  xor al,al
  mov dx,03c7h                  ;External Palette RAM, Pixel Read Adress
  out dx,al                     ;ab Farbe 0 lesen
  mov dx,3c9h                   ;Pixel Color Value

  rep insb                      ;alle Farben an VGA schicken
  pop di
  ret
Endp

double proc pascal far
  mov dx,3d4h                   ;CRTC Register 13h (Row Offset)
  mov ax,5013h                  ;auf 80 setzen (doppelte Breite)
  out dx,ax                     ;und schreiben
  ret
double endp


fade_out proc pascal far        ;Fadet Bild raus, Video-Modus unabh„ngig
local groesste:word             ;beinhaltet maximal m”glichen Farb-Wert
  mov groesste,63
  mov ax,ds                     ;Ziel-Segment laden
  mov es,ax
main_loop:                      ;Hauptschleife, wird einmal pro Bild durchl.
  lea si,palette                ;Quell- und Zieloffset auf Palette
  mov di,si
  mov cx,768                    ;768 Byte modifizieren
lp:
  lodsb                         ;Wert holen
  dec al                        ;herunterz„hlen
  jns setzen                    ;wenn noch nicht negativ -> setzen
  xor al,al                     ;sonst 0
setzen:
  stosb                         ;Zielwert in "Palette" schreiben

  dec cx                        ;Schleifenz„hler
  jne lp

  call waitretrace              ;auf Retrace synchronisieren
  call setpal                   ;berechnete Palette setzen
  dec groesste                  ;„uáere Schleife herunter z„hlen
  jne main_loop                 ;noch nicht fertig ? dann weiter

  ret
fade_out endp


fade_to proc pascal far zielpal:dword, laenge:word, schritt:byte
;fadet "Palette" auf "Zielpal", šbergabe von Pascal als Array of Byte !
local groesste:word
  mov ax,63                     ;Anzahl Durchl„ufe berechnen, die
  div schritt                   ;n”tig sind, um 63 zu erreichen
  xor ah,ah
  mov groesste,ax               ;Anzahl Schleifendurchl„ufe setzen
next_frame:
  les di,zielpal                ;Offset holen, Pascal bergibt Arrays far !
  lea si,palette                ;Offset der "Palette" holen
  mov cx,768                    ;768 Bytes bearbeiten

weiter:
  mov al,[si]                   ;Wert aus aktueller Palette holen
  mov ah,[di]                   ;Wert aus Zielpal holen

  mov bl,ah
  sub bl,al                     ;Differenz zum Zielwert
  cmp bl,schritt                ;mehr als ein Schritt drber ?
  jg rauf                       ;-> runterz„hlen
  neg bl                        ;Differenz
  cmp bl,schritt                ;gr”áer als negativer Schritt
  jg runter

  mov al,ah                     ;Ziel erreicht, endgltig setzen

schreiben:
  dec cx                        ;Farb-Schleife runter
  je fertig                     ;0 ? -> fertig
  mov [si],al                   ;Wert in Palette schreiben
  inc si                        ;n„chsten Wert selektieren
  inc di
  jmp weiter                    ;und weitermachen
runter:
  sub al,schritt                ;herunterz„hlen
  jmp schreiben
rauf:
  add al,schritt                ;heraufz„hlen
  jmp schreiben
fertig:                         ;Palette fertig berechnet
  call waitretrace              ;Synchronisation
  call setpal                   ;Palette setzen
  dec groesste                  ;alle 63 Durchl„ufe fertig ?
  jne next_frame                ;nein -> weiter

  ret
fade_to endp

copy_block proc pascal far ziel,quelle,breite,hoehe:word
local sprung:word
  mov dx,3ceh                   ;GDC
  mov ax,4105h                  ;ReadMode 0, WriteMode 1
  out dx,ax                     ;auf Register 5 : GDC Mode
  mov dx,3c4h                   ;TS
  mov ax,0f02h                  ;alle Planes einschalten
  out dx,ax                     ;auf Register 2 : Write Plane Mask

  push ds
  mov ax,0a000h                 ;Kopieren innerhalb VGA
  mov es,ax                     ;-> beide Segmente auf 0a000h
  mov ds,ax
  mov si,quelle                 ;Quelldaten von Quelle
  mov di,ziel                   ;nach Ziel kopieren
  mov dx,hoehe                  ;hoehe Zeilen kopieren

  mov ax,80                     ;Sprung zwischen zwei Zeilen berechnen
  sub ax,breite                 ;(= 80-Breite)
  mov sprung,ax

line_lp:
  mov cx,breite                 ;Breite laden
  rep movsb                     ;eine Zeile kopieren
  add si,sprung
  add di,sprung

  dec dx                        ;Zeilenz„hler weiter
  jne line_lp

  pop ds
  ret
copy_block endp

Pal_Rot proc pascal far Start,Ende:Word
;rotiert Palettenteil Start bis Ende um 1
;wenn Start < Ende : Rotation nach unten
;wenn Start > Ende : Rotation nach oben

  mov ax,ds                     ;es auf Datensegment
  mov es,ax

  lea si,palette                ;Palettenoffset laden
  mov di,si                     ;auch nach di

  mov ax,3                      ;"Start" in Palettenoffset umrechnen
  mul start
  add si,ax                     ;und auf si addieren
  mov ax,3                      ;das Gleiche fr Ziel
  mul ende
  add di,ax                     ;auf di addieren

  mov bx,[si]                   ;Bytes der Start-Farbe sichern
  mov dl,[si+2]

  mov cx,di                     ;Differenz zw. Start und Ende ist Anzahl
  sub cx,si                     ;zu kopierender Bytes

  mov di,si                     ;Start-Farbe als Ziel-Offset
  add si,3                      ;eine Farbe darber als Quell-Offset
                                ;fr vorw„rts kopieren bereits fertig
  cld                           ;Vorgabe: vorw„rts kopieren
  or cx,cx                      ;wenn cx negativ (Start > Ende)
  jns vorwaerts
  std                           ;dann rckw„rts kopieren
  neg cx                        ;cx korrigieren
  sub si,4                      ;si auf das 2. Byte der vorletzten Farbe
  add di,2                      ;di auf das 2. Byte der letzten Farbe
  add cx,2                      ;2 Byte mehr kopieren,
vorwaerts:                      ;damit Position nach Kopierschleife stimmt

  rep movsb                     ;Farben kopieren
  mov [di],bx                   ;Bytes der alten Start-Farbe
  mov [di+2],dl                 ;als letzte Farbe schreiben

  cld                           ;Direction-Flag wieder l”schen
  ret
Pal_Rot Endp

code ends
end
