(* ================================================================ *)
(*     Prozeduren f�r Spezialeffekte und Non-Standard-Videomodi     *)
(*                  (Kapitel 12, 14 und 15)                         *)
(*           (C) 1990 Matthias Uphoff & Addison Wesley              *)
(* ================================================================ *)

UNIT EVSpec;

INTERFACE

TYPE RegType = (Misc,Sequencer,CRT,Graphics,Attribute);
     (* Die EGA-Registergruppen als Aufz„hlungstyp *)

(* ----------------- Die Prozeduren aus Kapitel 12 ---------------- *)

FUNCTION RegDefault(Reg: RegType; Index: Byte): Byte;
   (* Gibt den Defaultwert eines EGA/VGA-Registers     *)
   (* im aktuellen Videomodus zur�ck. Reg spezifiziert *)
   (* die Registergruppe und Index die Registernummer. *)

(* ----------------- Die Prozeduren aus Kapitel 14 ---------------- *)

PROCEDURE SetStartAddr(SAddr: Word);
   (* Setzt den Adreáz„hler-Startwert SAddr *)
PROCEDURE FirstRow(r: Word);
   (* Stellt die Startadresse so ein, daá die virtuelle    *)
   (* Zeile r als oberste Zeile auf dem Monitor erscheint. *)
   (* Die Zeilennumerierung beginnt mit 0.                 *)
   (* F�r Textmodi 3,7 und Grafikmodi Eh,Fh,10h,11h,12h    *)
PROCEDURE SetCursorAddr(x,y: Word);
   (* Setzt den Cursor an die virtuelle Textposition x,y *)
PROCEDURE SetOffset(Width: Word);
   (* Stellt die virtuelle Bildschirmbreite Width ein   *)
   (* (Anzahl Textspalten bzw. Bytes in den Grafikmodi) *)
PROCEDURE ScreenPos(x,y: Word);
   (* Bestimmt die Position der linken oberen Ecke des *)
   (* sichtbaren Bildes im virtuellen Bildschirm.      *)
PROCEDURE WaitVRetrace;
   (* Wartet auf den Beginn der n„chsten vertikalen Retrace-Periode *)
PROCEDURE WaitDisplay;
   (* Wartet auf den Beginn der n„chsten Display-Periode *)
PROCEDURE GScrollVertical(n: Integer);
   (* Bewegt das Bild per Softscrolling �ber *)
   (* den virtuellen Grafikbildschirm:       *)
   (* wenn n < 0: n Rasterzeilen nach oben   *)
   (* wenn n > 0: n Rasterzeilen nach unten  *)
PROCEDURE PresetRowScan(Row: Byte);
   (* Bestimmt, mit welcher Zeichen-Punktreihe *)
   (* der Bildaufbau in den Textmodi beginnt   *)
PROCEDURE TScrollVertical(n: Integer);
   (* Bewegt das Bild per Softscrolling �ber *)
   (* den virtuellen Textbildschirm:         *)
   (* wenn n < 0: n Textzeilen nach oben     *)
   (* wenn n > 0: n Textzeilen nach unten    *)
PROCEDURE SetPelPan(p: Byte);
   (* L„dt das Horizontal Pixel Panning  *)
   (* Register mit dem Wert p            *)
PROCEDURE SetScrXY(x,y: Word);
   (* Legt die linke obere Ecke des sichtbaren   *)
   (* Bildes an die virtuelle Grafikposition x,y *)
FUNCTION GetScrX: Word;
   (* Gibt die virtuelle x-Koordinate der linken *)
   (* oberen Ecke des sichtbaren Bildes zur�ck   *)
FUNCTION GetScrY: Word;
   (* Gibt die virtuelle y-Koordinate der linken *)
   (* oberen Ecke des sichtbaren Bildes zur�ck   *)
PROCEDURE GSoftScroll(n: Word; h,v: Integer);
   (* Bewegt das Bild per Softscrolling �ber *)
   (* den virtuellen Grafikbildschirm.       *)
   (* n = Anzahl der Scrollvorg„nge          *)
   (* h = horizontale Schrittweite           *)
   (*     < 0: nach links, > 0: nach rechts  *)
   (* v = vertikale Schrittweite             *)
   (*     < 0: nach oben, > 0 nach unten     *)
PROCEDURE T8ScrollHorizontal(n: Integer);
   (* Bewegt das Bild per Softscrolling      *)
   (* �ber den virtuellen Textbildschirm:    *)
   (* wenn n < 0: n Textspalten nach links   *)
   (* wenn n > 0: n Textspalten nach rechts  *)
   (* Version f�r EGA-Textmodi 0..3          *)
PROCEDURE T9ScrollHorizontal(n: Integer);
   (* Wie T8ScrollHorizontal, aber f�r       *)
   (* VGA-Textmodi 0-3 und Monochrom-Modus 7 *)
PROCEDURE EGASetSplit(ScanLine: Word);
   (* Setzt den Line Compare Wert auf der EGA-Karte *)
PROCEDURE VGASetSplit(ScanLine: Word);
   (* Setzt den Line Compare Wert auf der VGA-Karte *)
PROCEDURE PelPanSplit;
   (* Begrenzt auf der VGA-Karte die Wirkung des Hor. Pixel *)
   (* Panning Registers auf den oberen Teil des Bildschirms *)
PROCEDURE ColorPlaneEnable(Planes: Byte);
   (* L„dt das Color Plane Enable Reg. mit dem Wert Planes *)

(* ----------------- Die Prozeduren aus Kapitel 15 ---------------- *)

PROCEDURE MemoryMap(mm: Byte);
   (* Stellt den Bildspeicher-Adreábereich ein: *)
   (* mm = 0: EGA  A0000 - BFFFF                *)
   (* mm = 1: EGA  A0000 - AFFFF                *)
   (* mm = 2: MDA  B0000 - B7FFF                *)
   (* mm = 3: CGA  B8000 - B8FFF                *)
   (* In dieser Form nur f�r VGA!               *)
PROCEDURE Init256N;
   (* Initialisiert den 256-Farben-   *)
   (* Modus ohne Chain 4-Adressierung *)
PROCEDURE Init256NXR;
   (* Initialisiert einen 256-Farben-Modus mit    *)
   (* 360x480-Aufl”sung ohne Chain 4-Adressierung *)
PROCEDURE PlotN(x,y: Word; Color: Byte);
   (* Setzt im 256N/NXR-Modus einen Punkt in *)
   (* der Farbe Color an die Position x,y    *)
FUNCTION DotColorN(x,y: Word): Byte;
   (* Liest im 256N/NXR-Modus die Punktfarbe an der Position x,y *)
PROCEDURE ClearScreenN(Color: Byte);
   (* L”scht im 256N/NXR-Modus den Bildschirm mit der Farbe Color *)
PROCEDURE  WriteCharN(c: Char; x,y: Word; ForeColor, BackColor: Byte);
   (* Schreibt im 256N/256NXR-Modus das Zeichen c an die    *)
   (* Position x,y (Grafikkoordinaten) mit der Vordergrund- *)
   (* farbe ForeColor und der Hintergrundfarbe BackColor    *)
   (* x,y markiert die linke obere Ecke des Zeichens.       *)
PROCEDURE Init16XR;
   (* Initialisiert einen VGA-Grafikmodus *)
   (* mit 720 x 480 Punkten bei 16 Farben *)
PROCEDURE Init90S;
   (* Initialisiert den 90x30-Textmodus *)

(* ================================================================ *)

IMPLEMENTATION

USES Dos;

VAR CRTC: Word ABSOLUTE $0000:$0463;
      (* BIOS: Portadresse des CRTC-Indexregisters *)
    ScrAddr: Word ABSOLUTE $0000:$044E;
      (* BIOS: Startadresse der aktiven Bildseite *)
    ScrWidth: Word ABSOLUTE $0000:$044A;
      (* BIOS: Anzahl Textspalten *)

    PelPos: Byte; (* Globale Variable f�r aktuelle Pixelposition *)

(* ----------------- Die Prozeduren aus Kapitel 12 ---------------- *)

FUNCTION RegDefault(Reg: RegType; Index: Byte): Byte;
(* ------------------------------------------------ *)
(* Gibt den Defaultwert eines EGA/VGA-Registers     *)
(* im aktuellen Videomodus zur�ck. Reg spezifiziert *)
(* die Registergruppe und Index die Registernummer. *)
(* ------------------------------------------------ *)
   CONST    (* Offset der Registergruppen in der Untertabelle *)
         RegOfs: ARRAY[RegType] OF Word =
                 ($09,$04,$0A,$37,$23);
            (* Offset der EGA-Untertabellen f�r die Modi 0..10h *)
         EGAOfs: ARRAY[0..$10] OF Word =
                 ($4C0,$500,$540,$580,$100,$140,$180,$1C0,$200,$240,
                  $280,$2C0,$300,$340,$380,$440,$480);
            (* Offset der VGA-Untertabellen f�r die Modi 0..13h *)
         VGAOfs: ARRAY[0..$13] OF Word =
                 ($5C0,$5C0,$600,$600,$100,$140,$180,$640,$200,$240,
                  $280,$2C0,$300,$340,$380,$440,$480,$680,$6C0,$700);
   TYPE PtrTableType = ARRAY[0..6] OF Pointer;
          (* BIOS-Zeigertabelle (Save Table) *)
   VAR R: Registers;      (* Prozessorregister! *)
       VGA: Boolean;      (* TRUE wenn VGA vorhanden *)
       PSeg, POfs: Word;
       SaveTablePtr: ^PtrTableType ABSOLUTE $0000:$04A8;
          (* Save Table Pointer, zeigt auf ROM-Zeigertabelle *)
       VideoMode: Byte ABSOLUTE $0000:$0449;
          (* Aktueller Videomodus im BIOS-Variablenbereich *)
BEGIN
  R.AX := $1A00;       (* BIOS-Call Combination Code lesen *)
  Intr($10,R);         (* um festzustellen, ob EGA oder VGA *)
  VGA := (R.AL = $1A);
  IF (Reg = Sequencer) AND (Index = 0) THEN
    (* Sequencerreg. 0 ist nicht in der Parametertabelle erfaát *)
    RegDefault := 3
  ELSE BEGIN
    PSeg := Seg(SaveTablePtr^[0]^);   (* Segmentadr. der Tabelle *)
    POfs := Ofs(SaveTablePtr^[0]^);   (* Offsetadr. der Tabelle *)
    (* Offset f�r Untertabelle addieren: *)
    IF VGA THEN
      POfs := POfs + VGAOfs[VideoMode AND $7F]
    ELSE
      POfs := POfs + EGAOfs[VideoMode AND $7F];
    (* Offset f�r Registergruppe und Index addieren: *)
    POfs := POfs + RegOfs[Reg] + Index;
    RegDefault := Mem[PSeg:POfs];     (* Tabellenbyte lesen *)
  END;
END;

(* ----------------- Die Prozeduren aus Kapitel 14 ---------------- *)

PROCEDURE SetStartAddr(SAddr: Word);
(* ------------------------------------  *)
(* Setzt den Adreáz„hler-Startwert SAddr *)
(* ------------------------------------  *)
BEGIN
  Port[CRTC] := $0C;          (* Index f�r Start Address High *)
  Port[CRTC+1] := Hi(SAddr);  (* mit Highbyte laden *)
  Port[CRTC] := $0D;          (* Index f�r Start Address Low *)
  Port[CRTC+1] := Lo(SAddr);  (* mit Lowbyte laden *)
  ScrAddr := SAddr;           (* Update der BIOS-Variablen *)
END;

PROCEDURE FirstRow(r: Word);
(* ---------------------------------------------------- *)
(* Stellt die Startadresse so ein, daá die virtuelle    *)
(* Zeile r als oberste Zeile auf dem Monitor erscheint. *)
(* Die Zeilennumerierung beginnt mit 0.                 *)
(* F�r Textmodi 3,7 und Grafikmodi Eh,Fh,10h,11h,12h    *)
(* ---------------------------------------------------- *)
BEGIN
  SetStartAddr(r * 80);
END;

PROCEDURE SetCursorAddr(x,y: Word);
(* -------------------------------------------------- *)
(* Setzt den Cursor an die virtuelle Textposition x,y *)
(* -------------------------------------------------- *)
   VAR a: Word;
BEGIN
  a := y * ScrWidth + x;    (* Cursoradresse berechnen *)
  Port[CRTC] := $0E;        (* Index f�r Cursor Location High *)
  Port[CRTC+1] := Hi(a);    (* mit Highbyte laden *)
  Port[CRTC] := $0F;        (* Index f�r Cursor Location Low *)
  Port[CRTC+1] := Lo(a);    (* mit Lowbyte laden *)
END;

PROCEDURE SetOffset(Width: Word);
(* ------------------------------------------------- *)
(* Stellt die virtuelle Bildschirmbreite Width ein   *)
(* (Anzahl Textspalten bzw. Bytes in den Grafikmodi) *)
(* ------------------------------------------------- *)
BEGIN
  Port[CRTC] := $13;            (* Index f�r Offset Register *)
  Port[CRTC+1] := Width DIV 2;  (* Register mit Breite laden *)
  ScrWidth := Width;            (* Update der BIOS-Variablen *)
END;

PROCEDURE ScreenPos(x,y: Word);
(* ------------------------------------------------ *)
(* Bestimmt die Position der linken oberen Ecke des *)
(* sichtbaren Bildes im virtuellen Bildschirm.      *)
(* ------------------------------------------------ *)
BEGIN
  SetStartAddr(y * ScrWidth + x);
END;

PROCEDURE WaitVRetrace;
(* ------------------------------------------------------------- *)
(* Wartet auf den Beginn der n„chsten vertikalen Retrace-Periode *)
(* ------------------------------------------------------------- *)
BEGIN
  (* Falls n”tig auf Beginn der Display-Periode warten *)
  REPEAT UNTIL (Port[CRTC+6] AND $08) = 0;
  (* Auf Beginn der n„chsten Retrace-Periode warten *)
  REPEAT UNTIL (Port[CRTC+6] AND $08) > 0;
END;

PROCEDURE WaitDisplay;
(* -------------------------------------------------- *)
(* Wartet auf den Beginn der n„chsten Display-Periode *)
(* -------------------------------------------------- *)
BEGIN
  (* Falls n”tig auf Beginn der Retrace-Periode warten *)
  REPEAT UNTIL (Port[CRTC+6] AND $08) > 0;
  (* Auf Beginn der n„chsten Display-Periode warten *)
  REPEAT UNTIL (Port[CRTC+6] AND $08) = 0;
END;

PROCEDURE GScrollVertical(n: Integer);
(* -------------------------------------- *)
(* Bewegt das Bild per Softscrolling �ber *)
(* den virtuellen Grafikbildschirm:       *)
(* wenn n < 0: n Rasterzeilen nach oben   *)
(* wenn n > 0: n Rasterzeilen nach unten  *)
(* -------------------------------------- *)
   VAR i: Integer;
BEGIN
  IF n < 0 THEN                          (* nach oben: *)
    FOR i := 1 TO -n DO BEGIN
      WaitDisplay;                    (* auf Display-Periode warten *)
      SetStartAddr(ScrAddr - ScrWidth);  (* Breite subtrahieren *)
    END
  ELSE                                   (* nach unten: *)
    FOR i := 1 TO n DO BEGIN
      WaitDisplay;                    (* auf Display-Periode warten *)
      SetStartAddr(ScrAddr + ScrWidth);  (* Breite addieren *)
    END;
END;

PROCEDURE PresetRowScan(Row: Byte);
(* ---------------------------------------- *)
(* Bestimmt, mit welcher Zeichen-Punktreihe *)
(* der Bildaufbau in den Textmodi beginnt   *)
(* ---------------------------------------- *)
BEGIN
  Port[CRTC] := $08;
  Port[CRTC+1] := Row;
END;

PROCEDURE TScrollVertical(n: Integer);
(* -------------------------------------- *)
(* Bewegt das Bild per Softscrolling �ber *)
(* den virtuellen Textbildschirm:         *)
(* wenn n < 0: n Textzeilen nach oben     *)
(* wenn n > 0: n Textzeilen nach unten    *)
(* -------------------------------------- *)
   VAR i,j: Integer;
       CharSize: Integer ABSOLUTE $0000:$0485;
BEGIN
  IF n < 0 THEN                            (* nach oben: *)
    FOR i := 1 TO -n DO BEGIN
      WaitDisplay;
      SetStartAddr(ScrAddr - ScrWidth);
      (* Die Adresse wirkt erst beim n„chsten VRetrace! *)
      FOR j := CharSize-1 DOWNTO 0 DO BEGIN
        WaitVRetrace; PresetRowScan(j);
      END;
    END
  ELSE                                     (* nach unten: *)
    FOR i := 1 TO n DO BEGIN
      FOR j := 1 TO CharSize-1 DO BEGIN
        WaitVRetrace; PresetRowScan(j);
      END;
      SetStartAddr(ScrAddr + ScrWidth);
      (* Die Adresse wirkt erst beim n„chsten VRetrace! *)
      WaitVRetrace; PresetRowScan(0);
    END;
END;

PROCEDURE SetPelPan(p: Byte);
(* ---------------------------------- *)
(* L„dt das Horizontal Pixel Panning  *)
(* Register mit dem Wert p            *)
(* ---------------------------------- *)
   VAR Dummy: Byte;
BEGIN
  Dummy := Port[CRTC+6];  (* Input Status #1 Reg. lesen *)
  Port[$3C0] := $13;      (* Index f�r HPP-Reg., CPU/Video-Bit=0 *)
  Port[$3C0] := p;        (* Horizontal Pel Panning Reg. laden *)
  Port[$3C0] := $20;      (* CPU/Video-Bit = 1 *)
  PelPos := p;
END;

PROCEDURE SetScrXY(x,y: Word);
(* ------------------------------------------ *)
(* Legt die linke obere Ecke des sichtbaren   *)
(* Bildes an die virtuelle Grafikposition x,y *)
(* ------------------------------------------ *)
BEGIN
  WaitDisplay;                        (* Auf Display-Periode warten *)
  SetStartAddr(y * ScrWidth + x DIV 8);   (* Neue Startadresse *)
  WaitVRetrace;                       (* Auf Retrace-Periode warten *)
  SetPelPan(x MOD 8);
END;

FUNCTION GetScrX: Word;
(* ------------------------------------------ *)
(* Gibt die virtuelle x-Koordinate der linken *)
(* oberen Ecke des sichtbaren Bildes zur�ck   *)
(* ------------------------------------------ *)
BEGIN
  GetScrX := (ScrAddr MOD ScrWidth) + PelPos;
END;

FUNCTION GetScrY: Word;
(* ------------------------------------------ *)
(* Gibt die virtuelle y-Koordinate der linken *)
(* oberen Ecke des sichtbaren Bildes zur�ck   *)
(* ------------------------------------------ *)
BEGIN
  GetScrY := ScrAddr DIV ScrWidth;
END;

PROCEDURE GSoftScroll(n: Word; h,v: Integer);
(* -------------------------------------- *)
(* Bewegt das Bild per Softscrolling �ber *)
(* den virtuellen Grafikbildschirm.       *)
(* n = Anzahl der Scrollvorg„nge          *)
(* h = horizontale Schrittweite           *)
(*     < 0: nach links, > 0: nach rechts  *)
(* v = vertikale Schrittweite             *)
(*     < 0: nach oben, > 0 nach unten     *)
(* -------------------------------------- *)
   VAR x,y,i: Word;
BEGIN
   x := GetScrX; y := GetScrY;   (* Startwerte holen *)
   FOR i := 1 TO n DO BEGIN
     x := x + h;  y := y + v;    (* Schrittweite addieren *)
     SetScrXY(x,y);              (* Neue Position festlegen *)
   END;
END;

PROCEDURE T8ScrollHorizontal(n: Integer);
(* -------------------------------------- *)
(* Bewegt das Bild per Softscrolling      *)
(* �ber den virtuellen Textbildschirm:    *)
(* wenn n < 0: n Textspalten nach links   *)
(* wenn n > 0: n Textspalten nach rechts  *)
(* Version f�r EGA-Textmodi 0..3          *)
(* -------------------------------------- *)
   VAR i,j: Integer;
BEGIN
  IF n < 0 THEN                       (* nach links: *)
    FOR i := 1 TO -n DO BEGIN
      (* Startadresse vermindern... *)
      WaitDisplay; SetStartAddr(ScrAddr - 1);
      (* ...und die Pixelposition auf 0 herunterz„hlen: *)
      FOR j := 7 DOWNTO 0 DO BEGIN
        WaitVRetrace; SetPelPan(j);
      END;
    END
  ELSE                                (* nach rechts: *)
    FOR i := 1 TO n DO BEGIN
      (* Pixelposition hochz„hlen *)
      FOR j := 1 TO 7 DO BEGIN
        WaitVRetrace; SetPelPan(j);
      END;
      (* Startadresse erh”hen, Pixelposition 0 setzen *)
      SetStartAddr(ScrAddr + 1);
      WaitVRetrace; SetPelPan(0);
    END;
END;

PROCEDURE T9ScrollHorizontal(n: Integer);
(* -------------------------------------- *)
(* Wie T8ScrollHorizontal, aber f�r       *)
(* VGA-Textmodi 0-3 und Monochrom-Modus 7 *)
(* -------------------------------------- *)
   VAR i,j: Integer;
BEGIN
  IF n < 0 THEN                       (* nach links: *)
    FOR i := 1 TO -n DO BEGIN
      (* Startadresse vermindern... *)
      WaitDisplay; SetStartAddr(ScrAddr - 1);
      (* ...die Pixelposition von 8..1 herunterz„hlen... *)
      FOR j := 7 DOWNTO 0 DO BEGIN
        WaitVRetrace; SetPelPan(j);
      END;
      (* ...und zum Schluá auf 0 setzen *)
      WaitVRetrace; SetPelPan(8);
    END
  ELSE                                (* nach rechts: *)
    FOR i := 1 TO n DO BEGIN
      (* Die Pixelposition von 1..8 heraufz„hlen... *)
      FOR j := 0 TO 7 DO BEGIN
        WaitVRetrace; SetPelPan(j);
      END;
      (* ...die Startadresse erh”hen... *)
      SetStartAddr(ScrAddr + 1);
      (* ...und gleichzeitig die Pixelposition auf 0 setzen *)
      WaitVRetrace; SetPelPan(8);
    END;
END;

PROCEDURE EGASetSplit(ScanLine: Word);
(* --------------------------------------------- *)
(* Setzt den Line Compare Wert auf der EGA-Karte *)
(* --------------------------------------------- *)
BEGIN
  Port[CRTC] := $18;              (* Index f�r Line Compare Reg. *)
  Port[CRTC+1] := Lo(Scanline);   (* Lowbyte laden *)
  Port[CRTC] := $07;              (* Index f�r Overflow Reg. *)
  IF (ScanLine AND $100) > 0 THEN
    (* Line Compare-Bit 8 = 1, Overflow-Bit 4 setzen: *)
    Port[CRTC+1] := RegDefault(CRT,7) OR $10
  ELSE
    (* Line Compare-Bit 8 = 0, Overflow-Bit 4 zur�cksetzen: *)
    Port[CRTC+1] := RegDefault(CRT,7) AND NOT $10;
END;

PROCEDURE VGASetSplit(ScanLine: Word);
(* --------------------------------------------- *)
(* Setzt den Line Compare Wert auf der VGA-Karte *)
(* --------------------------------------------- *)
BEGIN
  Port[CRTC] := $18;              (* Index f�r Line Compare Reg. *)
  Port[CRTC+1] := Lo(Scanline);   (* Lowbyte laden *)
  Port[CRTC] := $07;              (* Index f�r Overflow Reg. *)
  IF (ScanLine AND $100) > 0 THEN
    (* Line Compare-Bit 8 = 1, also Overflow-Bit 4 setzen: *)
    Port[CRTC+1] := Port[CRTC+1] OR $10
  ELSE
    (* Line Compare-Bit 8 = 0, also Overflow-Bit 4 zur�cksetzen: *)
    Port[CRTC+1] := Port[CRTC+1] AND NOT $10;
  Port[CRTC] := $09;               (* Index f�r Max. Scanline Reg. *)
  IF (ScanLine AND $200) > 0 THEN
    (* Line Compare-Bit 9 = 1, Max. Scanline-Bit 6 setzen: *)
    Port[CRTC+1] := Port[CRTC+1] OR $40
  ELSE
    (* Line Compare-Bit 9 = 0, Max. Scanline-Bit 6 zur�cksetzen: *)
    Port[CRTC+1] := Port[CRTC+1] AND NOT $40;
END;

PROCEDURE PelPanSplit;
(* ----------------------------------------------------- *)
(* Begrenzt auf der VGA-Karte die Wirkung des Hor. Pixel *)
(* Panning Registers auf den oberen Teil des Bildschirms *)
(* ----------------------------------------------------- *)
BEGIN
  WaitVRetrace;           (* Lesen des Input Status #1 Registers! *)
  Port[$3C0] := $10;      (* Index f�r Mode Control Register *)
  Port[$3C0] := Port[$3C1] OR $40;        (* Bit 5 setzen *)
  Port[$3C0] := $20;      (* CPU/Video-Bit im Indexregister setzen *)
END;

PROCEDURE ColorPlaneEnable(Planes: Byte);
(* ---------------------------------------------------- *)
(* L„dt das Color Plane Enable Reg. mit dem Wert Planes *)
(* ---------------------------------------------------- *)
BEGIN
  WaitVRetrace;           (* Lesen des Input Status #1 Registers! *)
  Port[$3C0] := $12;      (* Index f�r Color Plane Enable Reg. *)
  Port[$3C0] := Planes;   (* Register laden *)
  Port[$3C0] := $20;      (* CPU/Video-Bit setzen, siehe 14.9 *)
END;

(* ----------------- Die Prozeduren aus Kapitel 15 ---------------- *)

PROCEDURE MemoryMap(mm: Byte);
  (* ----------------------------------------- *)
  (* Stellt den Bildspeicher-Adreábereich ein: *)
  (* mm = 0: EGA  A0000 - BFFFF                *)
  (* mm = 1: EGA  A0000 - AFFFF                *)
  (* mm = 2: MDA  B0000 - B7FFF                *)
  (* mm = 3: CGA  B8000 - B8FFF                *)
  (* In dieser Form nur f�r VGA!               *)
  (* ----------------------------------------- *)
BEGIN
  mm := mm AND 3;            (* auf 0..3 beschr„nken *)
  Port[$3CE] := 6;           (* Index f�r Miscellaneous Reg. *)
  Port[$3CF] := (Port[$3CF] AND 3) OR (mm SHL 2);
                             (* Registerbits 2,3 modifizieren *)
  (* F�r EGA-Modi 0..3: Port[$3CF] := ($0E AND 3) OR (mm SHL 3); *)
END;

PROCEDURE Init256N;
(* ------------------------------- *)
(* Initialisiert den 256-Farben-   *)
(* Modus ohne Chain 4-Adressierung *)
(* ------------------------------- *)
  VAR R: Registers;
BEGIN
  (* 256-Farben-Modus $13 initialisieren *)
  R.AX := $13;
  Intr($10,R);
  (* Bit 3 im Memory Mode Register = 0, Chain 4-Adressierung aus *)
  Port[$3C4] := 4;
  Port[$3C5] := Port[$3C5] AND NOT $08;
  (* Bit 6 im Undeline Location Register = 0, Doppelwortmodus aus *)
  Port[$3D4] := $14;
  Port[$3D5] := Port[$3D5] AND NOT $40;
  (* Bit 6 im Mode Control Register = 1, Bytemodus ein *)
  Port[$3D4] := $17;
  Port[$3D5] := Port[$3D5] OR $40;
  FillChar(Mem[$A000:0],$FFFF,0);      (* Bildspeicher l”schen *)
END;

PROCEDURE Init256NXR;
(* ------------------------------------------- *)
(* Initialisiert einen 256-Farben-Modus mit    *)
(* 360x480-Aufl”sung ohne Chain 4-Adressierung *)
(* ------------------------------------------- *)
  CONST ScrSeg = $A000;
        CRTCData: ARRAY[0..24] OF Byte =
        ($6D,$59,$5A,$90,$60,$8B,$0B,$3E,$00,$40,
         $00,$00,$00,$00,$00,$00,$EA,$8C,$DF,$2D,
         $00,$E7,$04,$C3,$FF);
  VAR R: Registers;
      i: Byte;
      NrOfColumns: Word ABSOLUTE $0000:$044A;
         (* BIOS-Variable: Zeichen pro Zeile *)
      PageSize: Word ABSOLUTE $0000:$044C;
         (* BIOS Variable: L„nge einer Bildseite *)
      NrOfRows: Byte ABSOLUTE $0000:$0484;
         (* BIOS-Variable: Anzahl Textzeilen - 1 *)
BEGIN
  R.AX := $13;                  (* 256-Farben-Modus initialisieren *)
  Intr($10,R);
  R.AX := $1124;                (* BIOS-Funktion Zeichengenerator *)
  R.BL := 0;                    (* 8x16-Grafikfont installieren *)
  R.DL := 30;
  Intr($10,R);
  NrOfColumns := 45;                (* 45 Textspalten *)
  NrOfRows := 480 DIV 16 - 1;       (* 30 Textzeilen - 1 *)
  PageSize := (360 DIV 4) * 480;    (* Bildseitenl„nge 43200 Bytes *)
  Port[$3C4] := 4;                  (* Index Memory Mode Reg. *)
  Port[$3C5] := Port[$3C5] AND NOT $08;  (* Bit 3 = 0, Chain 4 Off *)
  FillChar(Mem[ScrSeg:0],$FFFF,0);  (* Bildspeicher l”schen *)
  INLINE($FA);                      (* CLI, Interrupts sperren *)
  Port[$3C4] := 0;                  (* Index Reset Register *)
  Port[$3C5] := 1;                  (* Synchroner Sequencer-Reset *)
  Port[$3C2] := $C7;                (* Misc. Output Register: *)
                                    (* 28 MHz Takt, 480 Rasterzeilen *)
  Port[$3C5] := 3;                  (* Reset-Zustand beenden *)
  INLINE($FB);                      (* STI, Interrupts erlauben *)
  Port[$3D4] := $11;                (* Index Vertical Retrace End *)
  Port[$3D5] := Port[$3D5] AND $7F; (* Protect Bit l”schen *)
  FOR i := 0 TO 24 DO BEGIN         (* CRTC-Register laden *)
    Port[$3D4] := i;                   (* Index *)
    Port[$3D5] := CRTCData[i];         (* Daten *)
  END;
END;

PROCEDURE PlotN(x,y: Word; Color: Byte);
(* -------------------------------------- *)
(* Setzt im 256N-Modus einen Punkt in der *)
(* der Farbe Color an die Position x,y    *)
(* -------------------------------------- *)
  CONST ScrSeg = $A000;
  VAR ScrOfs: Word;
      NrOfColumns: Word ABSOLUTE 0:$044A; (* BIOS: Zeichen pro Zeile *)
BEGIN
  ScrOfs := y * NrOfColumns * 2 + x DIV 4;   (* Offsetadresse *)
  Port[$3C4] := 2;                 (* Index Map Mask Register *)
  Port[$3C5] := 1 SHL (x AND 3);   (* Speicherebene selektieren *)
  Mem[ScrSeg:ScrOfs] := Color;     (* Farbbyte schreiben *)
END;

FUNCTION DotColorN(x,y: Word): Byte;
(* ------------------------------------------------------ *)
(* Liest im 256N-Modus die Punktfarbe an der Position x,y *)
(* ------------------------------------------------------ *)
  CONST ScrSeg = $A000;
  VAR ScrOfs: Word;
      NrOfColumns: Word ABSOLUTE 0:$044A; (* BIOS: Zeichen pro Zeile *)
BEGIN
  ScrOfs := y * NrOfColumns * 2 + x DIV 4;   (* Offsetadresse *)
  Port[$3CE] := 4;                    (* Index Read Map Select Reg. *)
  Port[$3CF] := x AND 3;              (* Speicherebene selektieren *)
  DotColorN := Mem[ScrSeg:ScrOfs];    (* Farbbyte auslesen *)
END;

PROCEDURE ClearScreenN(Color: Byte);
(* ------------------------------------------------------- *)
(* L”scht im 256N-Modus den Bildschirm mit der Farbe Color *)
(* ------------------------------------------------------- *)
  CONST ScrSeg = $A000;
BEGIN
  Port[$3C4] := 2;          (* Index Map Mask Register *)
  Port[$3C5] := $0F;        (* Schreibzugriff auf alle Ebenen *)
  FillChar(Mem[ScrSeg:0],16000,Color);
END;

PROCEDURE  WriteCharN(c: Char; x,y: Word; ForeColor, BackColor: Byte);
(* ----------------------------------------------------- *)
(* Schreibt im 256N/256NXR-Modus das Zeichen c an die    *)
(* Position x,y (Grafikkoordinaten) mit der Vordergrund- *)
(* farbe ForeColor und der Hintergrundfarbe BackColor    *)
(* x,y markiert die linke obere Ecke des Zeichens.       *)
(* ----------------------------------------------------- *)
   TYPE CharMatrix = ARRAY[0..31] OF Byte;
   VAR CharSize: Word ABSOLUTE $0000:$0485;   (* BIOS-Zeichenh”he *)
       FPtr: Pointer ABSOLUTE $0000:$010C;    (* INT 43h-Vektor *)
       CharRow: Byte;
       FontSeg, FontOfs,i,j: Word;
BEGIN
  FontSeg := Seg(FPtr^);              (* Segmentadresse Zeichensatz *)
  FontOfs := Ofs(FPtr^) + CharSize * Byte(c);  (* Offsetadr. Matrix *)
  FOR i := 0 TO CharSize-1 DO BEGIN
    CharRow := Mem[FontSeg:FontOfs];   (* Zeichensatz-Byte holen *)
    FOR j := 0 TO 7 DO BEGIN
      IF (CharRow  AND $80) <> 0 THEN  (* Vordergrundfarbe *)
        PlotN(x+j,y+i,ForeColor)
      ELSE                             (* Hintergrundfarbe *)
        PlotN(x+j,y+i,BackColor);
      CharRow := CharRow SHL 1;        (* Punktreihe links schieben *)
    END;
    Inc(FontOfs);                      (* N„chste Punktreihe *)
  END;
END;

PROCEDURE Init16XR;
(* ----------------------------------- *)
(* Initialisiert einen VGA-Grafikmodus *)
(* mit 720 x 480 Punkten bei 16 Farben *)
(* ----------------------------------- *)
   CONST CRTCData: ARRAY[0..24] OF Byte =
         ($6D,$59,$5A,$90,$60,$89,$0B,$3E,$00,$40,
          $00,$00,$00,$00,$00,$00,$EA,$AC,$DF,$2D,
          $00,$E7,$04,$E3,$FF);
   VAR R: Registers;
       i: Word;
       NrOfColumns: Word ABSOLUTE $0000:$044A;
          (* BIOS-Variable: Zeichen pro Zeile *)
       PageSize: Word ABSOLUTE $0000:$044C;
          (* BIOS-Variable: L„nge einer Bildseite *)
BEGIN
  R.AX := $12;                (* VGA-Grafikmodus 12h initialisieren *)
  Intr($10,R);                      
  NrOfColumns := 90;                (* Neue Bildbreite 90 Zeichen *)
  PageSize := 90 * 480;             (* Neue Bildseitenl„nge *)
  INLINE($FA);                      (* CLI, Interrupts sperren *)
  Port[$3C4] := 0;                  (* Index Reset Register *)
  Port[$3C5] := 1;                  (* Synchroner Sequencer-Reset *)
  Port[$3C2] := Port[$3CC] OR 4;    (* Misc Output Reg. auf 28 Mhz *)
  Port[$3C5] := 3;                  (* Reset-Zustand beenden *)
  INLINE($FB);                      (* STI, Interrupts erlauben *)
  Port[$3D4] := $11;                (* Index Vertical Retrace End *)
  Port[$3D5] := Port[$3D5] AND $7F; (* Protect Bit 7 l”schen *)
  FOR i := 0 TO 24 DO BEGIN         (* Alle CRTC-Register laden: *)
    Port[$3D4] := i;                   (* Index *)
    Port[$3D5] := CRTCData[i];         (* Daten *)
  END;
END;

PROCEDURE Init90S;
(* --------------------------------- *)
(* Initialisiert den 90x30-Textmodus *)
(* --------------------------------- *)
  CONST CRTCData: ARRAY[0..24] OF Byte =
        ($6D,$59,$5A,$90,$60,$8B,$0B,$3E,$00,$4F,
         $0D,$0E,$00,$00,$00,$00,$EA,$AC,$DF,$2D,
         $1F,$E7,$04,$A3,$FF);
  VAR R: Registers;
      i: Byte;
      NrOfColumns: Word ABSOLUTE $0000:$044A;
         (* BIOS-Variable: Zeichen pro Zeile *)
      PageSize: Word ABSOLUTE $0000:$044C;
         (* BIOS Variable: L„nge einer Bildseite *)
      NrOfRows: Byte ABSOLUTE $0000:$0484;
         (* BIOS-Variable: Anzahl Textzeilen - 1 *)
BEGIN
  R.AX := $03;                      (* VGA Textmodus 3 *)
  Intr($10,R);                      (* initialisieren *)
  NrOfColumns := 90;                (* Neue Bildbreite 90 Spalten *)
  PageSize := 180*(480 DIV 16);     (* Neue Bildseitenl„nge *)
  NrOfRows := 29;                   (* 30 Textzeilen - 1 *)
  INLINE($FA);                      (* CLI, Interrupts sperren *)
  Port[$3C4] := 0;                  (* Index Reset Register *)
  Port[$3C5] := 1;                  (* Synchroner Sequencer-Reset *)
  Port[$3C4] := 1;                  (* Index Clocking Mode Reg. *)
  Port[$3C5] := Port[$3C5] OR 1;    (* Bit 1 = 1, 8x16-Zeichenbox *)
  Port[$3C2] := Port[$3CC] OR $80;  (* Misc. Output Register: *)
                                    (* Bit 7 = 1, 480 Rasterzeilen *)
  Port[$3C4] := 0;                  (* Index Reset Register *)
  Port[$3C5] := 3;                  (* Reset-Zustand beenden *)
  INLINE($FB);                      (* STI, Interrupts erlauben *)
  Port[$3D4] := $11;                (* Index Vertical Retrace End *)
  Port[$3D5] := Port[$3D5] AND $7F; (* Protect Bit l”schen *)
  FOR i := 0 TO 24 DO BEGIN         (* CRTC-Register laden *)
    Port[$3D4] := i;                   (* Index *)
    Port[$3D5] := CRTCData[i];         (* Daten *)
  END;
  i := Port[$3DA];                  (* Input Status #1 Reg. lesen *)
  Port[$3C0] := $13;                (* Index Horiz. Pixel Panning  *)
  Port[$3C0] := 0;                  (* 0 f�r 8 Pixel Zeichenbreite *)
  Port[$3C0] := $20;                (* CPU-Zugriff auf Palette ein *)
END;

(* ================================================================ *)

BEGIN  (* Initialisierungsteil *)
  PelPos := 0;
END.

(* ================================================================ *)
