home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / pascal / anivga12 / anivga.pas < prev    next >
Pascal/Delphi Source File  |  1993-07-11  |  326KB  |  11,305 lines

  1. {$A+,B-,D+,L+,N-,E-,O-,R-,S-,V-,G-,F-,I-,X-}
  2. {$M 16384,0,655360}
  3. {$UNDEF IOcheck}
  4.  
  5. {Programmtool zur Realisierung schneller Animationen auf der VGA-Grafik-    }
  6. {karte von: Kai Rohrbacher, 1988-1993, Turbo-Pascal 6.0                     }
  7.  
  8. { Features:                                                                 }
  9.  
  10. { - flickerfreie Animation durch page-flipping, Auswertung von Retrace-     }
  11. {   signalen und Verwendung eines speziellen VGA-256-Farbengrafikmodus      }
  12. { - Spritebewegung pixelweise (und nicht nur byteweise) möglich             }
  13. { - beliebiges Hintergrundbild, vor der die Animation geschieht             }
  14. { - Animationen können auf ein Bildschirmfenster begrenzt werden            }
  15. { - volle Unterstützung der 256-Farbmöglichkeiten der VGA-Karte             }
  16. { - verschiedene Spritedarstellungsmöglichkeiten:                           }
  17. {   - Sprites können pixelweise als durchsichtig gegenüber dem Hintergrund  }
  18. {     deklariert werden                                                     }
  19. {   - Sprites können ihre Farbe in Abhängigkeit ihres momentanen Hinter-    }
  20. {     grundes verändern ("Schattenfunktion")                                }
  21. { - Routine zur exakten Feststellung der Kollision zweier Sprites           }
  22. { - Sprites werden beim Verschwinden an einer der Bildschirmgrenzen korrekt }
  23. {   abgeschnitten                                                           }
  24. { - Verwaltung von bis zu 32767 verschiedenen Sprites (Voreinstellung: 1000)}
  25. { - gleichzeitige Darstellung von bis zu 32767 Sprites (Voreinstellung: 500)}
  26. { - maximale Spritegröße 64k                                                }
  27. { - maximaler Umfang aller Sprites zusammen nur durch Hauptspeicher begrenzt}
  28. { - arbeitet mit virtuellen Koordinaten im Bereich -16000..+16000, daher    }
  29. {   einfaches horizontales und vertikales Scrolling möglich                 }
  30. { - Scrollbares Hintergrundbild ebenfalls unterstützt                       }
  31. { - Einschränkung der Animation auf ein Window ist möglich                  }
  32. { - viele unterstützende Routinen: zeichnen von Linien (mit eingebautem     }
  33. {   Clipping-Algorithmus), Punkten und Grafik-Text (dto.), automatische Ver-}
  34. {   waltungs des Heaps zum Speichern/Laden von Sprites, Hintergrundbildern, }
  35. {   Ändern von Spritedarstellungsmodi während der Laufzeit, Geschwindig-    }
  36. {   keitsanpassung an unterschiedlich schnelle Rechner, ...                 }
  37.  
  38. UNIT ANIVGA;
  39. INTERFACE
  40.  
  41. USES CRT,DOS,Compression;
  42.  
  43. CONST ANIVGAVersion=12; {Versionsnummer}
  44.       NMAX=499;
  45.       XMAX=319;
  46.       YMAX=199;
  47.       LoadMAX=1000;  {max. Anzahl an gleichzeitig geladenenen Sprites}
  48.       LINESIZE=(XMAX+1) DIV 4;    {Größe einer Zeile = 80 Bytes}
  49.       PAGESIZE=(YMAX+1)*LINESIZE; {200 Zeilen zu je 320/4 Bytes}
  50.       BACKGNDPAGE=2;
  51.       SCROLLPAGE=3;
  52.  
  53.       STATIC=0;      {Konstanten für Hintergrundart}
  54.       SCROLLING=1;
  55.  
  56.       MaxTiles=10000;   {max. Anzahl an Hintergrund-Kacheln}
  57.       StartVirtualX:INTEGER=0;  {obere linke Bildschirmecke}
  58.       StartVirtualY:INTEGER=0;
  59.  
  60.       {unterstützte Darstellungsmodi der Sprites: }
  61.       Display_NORMAL=0;   {normal  : durchsichtig für Farbe 0}
  62.       Display_FAST  =1;   {schnell : keine Hintergrundberücksichtigung}
  63.       Display_SHADOW=2;   {Schatten: Farbumsetzung anhand des Hintergrundes}
  64.       Display_SHADOWEXACT=3; {Farbe 0 ist auch für Schatten durchsichtig}
  65.       Display_UNKNOWN=255;{Fehlerwert}
  66.  
  67.       {Fehlercodes des Animationspaketes: }
  68.       Err_None=0;
  69.       Err_NotEnoughMemory=1;
  70.       Err_FileIO=2;
  71.       Err_InvalidSpriteNumber=3;
  72.       Err_NoSprite=4;
  73.       Err_InvalidPageNumber=5;
  74.       Err_NoVGA=6;
  75.       Err_NoPicture=7;
  76.       Err_InvalidPercentage=8;
  77.       Err_NoTile=9;
  78.       Err_InvalidTileNumber=10;
  79.       Err_InvalidCoordinates=11;
  80.       Err_BackgroundToBig=12;
  81.       Err_InvalidMode=13;
  82.       Err_InvalidSpriteLoadNumber=14;
  83.       Err_NoPalette=15;
  84.       Err_PaletteWontFit=16;
  85.       Err_InvalidFade=17;
  86.       Err_NoFont=18;
  87.       Err_EMSError=19;
  88.  
  89. CONST MaxFontHeight=16;
  90.       MaxFontWidth=15;
  91.       TagMonoFont=0;
  92.       TagColorFont=1;
  93.       TagProportional=$80;
  94. TYPE  MonoFontChar=ARRAY[0..MaxFontHeight-1] OF WORD;
  95.       MonoFont=ARRAY[0..255] OF MonoFontchar;
  96.       ColorFontChar=ARRAY[0..MaxFontHeight-1] OF ARRAY[0..MaxFontWidth-1] OF BYTE;
  97.       ColorFont=ARRAY[0..255] OF ColorFontchar;
  98.       FontOrient=(horizontal,vertical);    {mögliche Textausgaberichtungen}
  99. CONST GraphTextOrientation:FontOrient=horizontal; {aktuelle Ausgaberichtung}
  100.       GraphTextColor:BYTE=white;           {aktuelle Textausgabefarben}
  101.       GraphTextBackground:BYTE=white;
  102.       CurrentFont:POINTER=NIL;             {Zeiger auf aktuellen Font}
  103.       UpdateOuterArea:BYTE=2;              {äußeren Hintergrund updaten}
  104.       WinClip:BOOLEAN=FALSE;               {Pixel auf Fenster clippen?}
  105. VAR   FontHeight,
  106.       FontWidth,
  107.       FontType,
  108.       FontProportion:BYTE;
  109.       FontWidthTable:ARRAY[0..255] OF BYTE;
  110.  
  111. TYPE Table=ARRAY[0..NMAX] OF INTEGER;
  112.      ColorTable=ARRAY[0..255] OF BYTE;
  113.  
  114. TYPE PaletteEntry=RECORD red,green,blue:BYTE END;
  115.      Palette=ARRAY[0..255] OF PaletteEntry;
  116.      PalettePtr=^Palette;
  117.  
  118. CONST DefaultColors:Palette=       {Defaultfarben-Palette des 256-Farbmodus}
  119.  (                                 {ausgelesen mithilfe des BIOS-Aufrufs:  }
  120.   (red:  0; green:  0; blue:  0),  { MOV AX,1017h ;lese Palettenregister}
  121.   (red:  0; green:  0; blue: 42),  { XOR BX,BX    ;von Farbe 0 an }
  122.   (red:  0; green: 42; blue:  0),  { MOV CX,100h  ;alle 256 Farben}
  123.   (red:  0; green: 42; blue: 42),  { LES DX,Ziel  ;nach ES:DX }
  124.   (red: 42; green:  0; blue:  0),  { INT 10h }
  125.   (red: 42; green:  0; blue: 42),  {Achtung! Die Werte könn(t)en nur dann  }
  126.   (red: 42; green: 21; blue:  0),  {ausgelesen werden, wenn der Grafikmodus}
  127.   (red: 42; green: 42; blue: 42),  {bereits aktiv ist, deshalb wurden sie  }
  128.   (red: 21; green: 21; blue: 21),  {hier "statisch" aufgenommen!}
  129.   (red: 21; green: 21; blue: 63),
  130.   (red: 21; green: 63; blue: 21),
  131.   (red: 21; green: 63; blue: 63),
  132.   (red: 63; green: 21; blue: 21),
  133.   (red: 63; green: 21; blue: 63),
  134.   (red: 63; green: 63; blue: 21),
  135.   (red: 63; green: 63; blue: 63),
  136.   (red:  0; green:  0; blue:  0),
  137.   (red:  5; green:  5; blue:  5),
  138.   (red:  8; green:  8; blue:  8),
  139.   (red: 11; green: 11; blue: 11),
  140.   (red: 14; green: 14; blue: 14),
  141.   (red: 17; green: 17; blue: 17),
  142.   (red: 20; green: 20; blue: 20),
  143.   (red: 24; green: 24; blue: 24),
  144.   (red: 28; green: 28; blue: 28),
  145.   (red: 32; green: 32; blue: 32),
  146.   (red: 36; green: 36; blue: 36),
  147.   (red: 40; green: 40; blue: 40),
  148.   (red: 45; green: 45; blue: 45),
  149.   (red: 50; green: 50; blue: 50),
  150.   (red: 56; green: 56; blue: 56),
  151.   (red: 63; green: 63; blue: 63),
  152.   (red:  0; green:  0; blue: 63),
  153.   (red: 16; green:  0; blue: 63),
  154.   (red: 31; green:  0; blue: 63),
  155.   (red: 47; green:  0; blue: 63),
  156.   (red: 63; green:  0; blue: 63),
  157.   (red: 63; green:  0; blue: 47),
  158.   (red: 63; green:  0; blue: 31),
  159.   (red: 63; green:  0; blue: 16),
  160.   (red: 63; green:  0; blue:  0),
  161.   (red: 63; green: 16; blue:  0),
  162.   (red: 63; green: 31; blue:  0),
  163.   (red: 63; green: 47; blue:  0),
  164.   (red: 63; green: 63; blue:  0),
  165.   (red: 47; green: 63; blue:  0),
  166.   (red: 31; green: 63; blue:  0),
  167.   (red: 16; green: 63; blue:  0),
  168.   (red:  0; green: 63; blue:  0),
  169.   (red:  0; green: 63; blue: 16),
  170.   (red:  0; green: 63; blue: 31),
  171.   (red:  0; green: 63; blue: 47),
  172.   (red:  0; green: 63; blue: 63),
  173.   (red:  0; green: 47; blue: 63),
  174.   (red:  0; green: 31; blue: 63),
  175.   (red:  0; green: 16; blue: 63),
  176.   (red: 31; green: 31; blue: 63),
  177.   (red: 39; green: 31; blue: 63),
  178.   (red: 47; green: 31; blue: 63),
  179.   (red: 55; green: 31; blue: 63),
  180.   (red: 63; green: 31; blue: 63),
  181.   (red: 63; green: 31; blue: 55),
  182.   (red: 63; green: 31; blue: 47),
  183.   (red: 63; green: 31; blue: 39),
  184.   (red: 63; green: 31; blue: 31),
  185.   (red: 63; green: 39; blue: 31),
  186.   (red: 63; green: 47; blue: 31),
  187.   (red: 63; green: 55; blue: 31),
  188.   (red: 63; green: 63; blue: 31),
  189.   (red: 55; green: 63; blue: 31),
  190.   (red: 47; green: 63; blue: 31),
  191.   (red: 39; green: 63; blue: 31),
  192.   (red: 31; green: 63; blue: 31),
  193.   (red: 31; green: 63; blue: 39),
  194.   (red: 31; green: 63; blue: 47),
  195.   (red: 31; green: 63; blue: 55),
  196.   (red: 31; green: 63; blue: 63),
  197.   (red: 31; green: 55; blue: 63),
  198.   (red: 31; green: 47; blue: 63),
  199.   (red: 31; green: 39; blue: 63),
  200.   (red: 45; green: 45; blue: 63),
  201.   (red: 49; green: 45; blue: 63),
  202.   (red: 54; green: 45; blue: 63),
  203.   (red: 58; green: 45; blue: 63),
  204.   (red: 63; green: 45; blue: 63),
  205.   (red: 63; green: 45; blue: 58),
  206.   (red: 63; green: 45; blue: 54),
  207.   (red: 63; green: 45; blue: 49),
  208.   (red: 63; green: 45; blue: 45),
  209.   (red: 63; green: 49; blue: 45),
  210.   (red: 63; green: 54; blue: 45),
  211.   (red: 63; green: 58; blue: 45),
  212.   (red: 63; green: 63; blue: 45),
  213.   (red: 58; green: 63; blue: 45),
  214.   (red: 54; green: 63; blue: 45),
  215.   (red: 49; green: 63; blue: 45),
  216.   (red: 45; green: 63; blue: 45),
  217.   (red: 45; green: 63; blue: 49),
  218.   (red: 45; green: 63; blue: 54),
  219.   (red: 45; green: 63; blue: 58),
  220.   (red: 45; green: 63; blue: 63),
  221.   (red: 45; green: 58; blue: 63),
  222.   (red: 45; green: 54; blue: 63),
  223.   (red: 45; green: 49; blue: 63),
  224.   (red:  0; green:  0; blue: 28),
  225.   (red:  7; green:  0; blue: 28),
  226.   (red: 14; green:  0; blue: 28),
  227.   (red: 21; green:  0; blue: 28),
  228.   (red: 28; green:  0; blue: 28),
  229.   (red: 28; green:  0; blue: 21),
  230.   (red: 28; green:  0; blue: 14),
  231.   (red: 28; green:  0; blue:  7),
  232.   (red: 28; green:  0; blue:  0),
  233.   (red: 28; green:  7; blue:  0),
  234.   (red: 28; green: 14; blue:  0),
  235.   (red: 28; green: 21; blue:  0),
  236.   (red: 28; green: 28; blue:  0),
  237.   (red: 21; green: 28; blue:  0),
  238.   (red: 14; green: 28; blue:  0),
  239.   (red:  7; green: 28; blue:  0),
  240.   (red:  0; green: 28; blue:  0),
  241.   (red:  0; green: 28; blue:  7),
  242.   (red:  0; green: 28; blue: 14),
  243.   (red:  0; green: 28; blue: 21),
  244.   (red:  0; green: 28; blue: 28),
  245.   (red:  0; green: 21; blue: 28),
  246.   (red:  0; green: 14; blue: 28),
  247.   (red:  0; green:  7; blue: 28),
  248.   (red: 14; green: 14; blue: 28),
  249.   (red: 17; green: 14; blue: 28),
  250.   (red: 21; green: 14; blue: 28),
  251.   (red: 24; green: 14; blue: 28),
  252.   (red: 28; green: 14; blue: 28),
  253.   (red: 28; green: 14; blue: 24),
  254.   (red: 28; green: 14; blue: 21),
  255.   (red: 28; green: 14; blue: 17),
  256.   (red: 28; green: 14; blue: 14),
  257.   (red: 28; green: 17; blue: 14),
  258.   (red: 28; green: 21; blue: 14),
  259.   (red: 28; green: 24; blue: 14),
  260.   (red: 28; green: 28; blue: 14),
  261.   (red: 24; green: 28; blue: 14),
  262.   (red: 21; green: 28; blue: 14),
  263.   (red: 17; green: 28; blue: 14),
  264.   (red: 14; green: 28; blue: 14),
  265.   (red: 14; green: 28; blue: 17),
  266.   (red: 14; green: 28; blue: 21),
  267.   (red: 14; green: 28; blue: 24),
  268.   (red: 14; green: 28; blue: 28),
  269.   (red: 14; green: 24; blue: 28),
  270.   (red: 14; green: 21; blue: 28),
  271.   (red: 14; green: 17; blue: 28),
  272.   (red: 20; green: 20; blue: 28),
  273.   (red: 22; green: 20; blue: 28),
  274.   (red: 24; green: 20; blue: 28),
  275.   (red: 26; green: 20; blue: 28),
  276.   (red: 28; green: 20; blue: 28),
  277.   (red: 28; green: 20; blue: 26),
  278.   (red: 28; green: 20; blue: 24),
  279.   (red: 28; green: 20; blue: 22),
  280.   (red: 28; green: 20; blue: 20),
  281.   (red: 28; green: 22; blue: 20),
  282.   (red: 28; green: 24; blue: 20),
  283.   (red: 28; green: 26; blue: 20),
  284.   (red: 28; green: 28; blue: 20),
  285.   (red: 26; green: 28; blue: 20),
  286.   (red: 24; green: 28; blue: 20),
  287.   (red: 22; green: 28; blue: 20),
  288.   (red: 20; green: 28; blue: 20),
  289.   (red: 20; green: 28; blue: 22),
  290.   (red: 20; green: 28; blue: 24),
  291.   (red: 20; green: 28; blue: 26),
  292.   (red: 20; green: 28; blue: 28),
  293.   (red: 20; green: 26; blue: 28),
  294.   (red: 20; green: 24; blue: 28),
  295.   (red: 20; green: 22; blue: 28),
  296.   (red:  0; green:  0; blue: 16),
  297.   (red:  4; green:  0; blue: 16),
  298.   (red:  8; green:  0; blue: 16),
  299.   (red: 12; green:  0; blue: 16),
  300.   (red: 16; green:  0; blue: 16),
  301.   (red: 16; green:  0; blue: 12),
  302.   (red: 16; green:  0; blue:  8),
  303.   (red: 16; green:  0; blue:  4),
  304.   (red: 16; green:  0; blue:  0),
  305.   (red: 16; green:  4; blue:  0),
  306.   (red: 16; green:  8; blue:  0),
  307.   (red: 16; green: 12; blue:  0),
  308.   (red: 16; green: 16; blue:  0),
  309.   (red: 12; green: 16; blue:  0),
  310.   (red:  8; green: 16; blue:  0),
  311.   (red:  4; green: 16; blue:  0),
  312.   (red:  0; green: 16; blue:  0),
  313.   (red:  0; green: 16; blue:  4),
  314.   (red:  0; green: 16; blue:  8),
  315.   (red:  0; green: 16; blue: 12),
  316.   (red:  0; green: 16; blue: 16),
  317.   (red:  0; green: 12; blue: 16),
  318.   (red:  0; green:  8; blue: 16),
  319.   (red:  0; green:  4; blue: 16),
  320.   (red:  8; green:  8; blue: 16),
  321.   (red: 10; green:  8; blue: 16),
  322.   (red: 12; green:  8; blue: 16),
  323.   (red: 14; green:  8; blue: 16),
  324.   (red: 16; green:  8; blue: 16),
  325.   (red: 16; green:  8; blue: 14),
  326.   (red: 16; green:  8; blue: 12),
  327.   (red: 16; green:  8; blue: 10),
  328.   (red: 16; green:  8; blue:  8),
  329.   (red: 16; green: 10; blue:  8),
  330.   (red: 16; green: 12; blue:  8),
  331.   (red: 16; green: 14; blue:  8),
  332.   (red: 16; green: 16; blue:  8),
  333.   (red: 14; green: 16; blue:  8),
  334.   (red: 12; green: 16; blue:  8),
  335.   (red: 10; green: 16; blue:  8),
  336.   (red:  8; green: 16; blue:  8),
  337.   (red:  8; green: 16; blue: 10),
  338.   (red:  8; green: 16; blue: 12),
  339.   (red:  8; green: 16; blue: 14),
  340.   (red:  8; green: 16; blue: 16),
  341.   (red:  8; green: 14; blue: 16),
  342.   (red:  8; green: 12; blue: 16),
  343.   (red:  8; green: 10; blue: 16),
  344.   (red: 11; green: 11; blue: 16),
  345.   (red: 12; green: 11; blue: 16),
  346.   (red: 13; green: 11; blue: 16),
  347.   (red: 15; green: 11; blue: 16),
  348.   (red: 16; green: 11; blue: 16),
  349.   (red: 16; green: 11; blue: 15),
  350.   (red: 16; green: 11; blue: 13),
  351.   (red: 16; green: 11; blue: 12),
  352.   (red: 16; green: 11; blue: 11),
  353.   (red: 16; green: 12; blue: 11),
  354.   (red: 16; green: 13; blue: 11),
  355.   (red: 16; green: 15; blue: 11),
  356.   (red: 16; green: 16; blue: 11),
  357.   (red: 15; green: 16; blue: 11),
  358.   (red: 13; green: 16; blue: 11),
  359.   (red: 12; green: 16; blue: 11),
  360.   (red: 11; green: 16; blue: 11),
  361.   (red: 11; green: 16; blue: 12),
  362.   (red: 11; green: 16; blue: 13),
  363.   (red: 11; green: 16; blue: 15),
  364.   (red: 11; green: 16; blue: 16),
  365.   (red: 11; green: 15; blue: 16),
  366.   (red: 11; green: 13; blue: 16),
  367.   (red: 11; green: 12; blue: 16),
  368.   (red:  0; green:  0; blue:  0),
  369.   (red:  0; green:  0; blue:  0),
  370.   (red:  0; green:  0; blue:  0),
  371.   (red:  0; green:  0; blue:  0),
  372.   (red:  0; green:  0; blue:  0),
  373.   (red:  0; green:  0; blue:  0),
  374.   (red:  0; green:  0; blue:  0),
  375.   (red:  0; green:  0; blue:  0)
  376.  );
  377.  
  378. VAR Error:BYTE; {globale Fehlervariable}
  379.     SpriteN:Table;
  380.     SpriteX:Table;
  381.     SpriteY:Table;
  382.     NextSprite:ARRAY[0..LoadMAX] OF WORD;
  383.     PAGE,PAGEADR,SCROLLADR,BACKGNDADR:WORD;
  384.     Color:BYTE;           {Zeichenfarbe für Linien}
  385.     ActualColors:Palette; {aktuelle Farbpalette}
  386.     was_cut:BOOLEAN;      {TRUE/FALSE, falls "GetImage" clippen mußte   }
  387.     left_cut,             {Var., die durch "GetImage" gesetzt werden und}
  388.     right_cut,            {bei "was_cut"=TRUE darüber Auskunft geben,   }
  389.     top_cut,              {wo und wieviel des Bildes abgeschnitten      }
  390.     bottom_cut:WORD;      {werden mußte                                 }
  391.  
  392.     WinXMIN,WinYMIN,WinXMAX,WinYMAX,WinWidth,WinHeight:WORD;
  393.  
  394.     BackgroundMode:BYTE;
  395.     BackTile:ARRAY[0..MaxTiles] OF BYTE;    {Kachelnspeicher}
  396.     XTiles,YTiles:INTEGER;                  {Breite,Höhe des def. Bereiches }
  397.     BackX1,BackY1,BackX2,BackY2:INTEGER;    {Koordinaten des def. Bereiches }
  398.  
  399. CONST   Fade_Squares =0;
  400.         Fade_Moiree1 =1;
  401.         Fade_Moiree2 =2;
  402.         Fade_Moiree3 =3;
  403.         Fade_Moiree4 =4;
  404.         Fade_Moiree5 =5;
  405.         Fade_Moiree6 =6;
  406.         Fade_Moiree7 =7;
  407.         Fade_Moiree8 =8;
  408.         Fade_Moiree9 =9;
  409.         Fade_Moiree10=10;
  410.         Fade_Moiree11=11;
  411.         Fade_Moiree12=12;
  412.         Fade_Moiree13=13;
  413.         Fade_Moiree14=14;
  414.         Fade_SweepInFromTop=15;
  415.         Fade_SweepInFromBottom=16;
  416.         Fade_SweepInFromLeft=17;
  417.         Fade_SweepInFromRight=18;
  418.         Fade_ScrollInFromTop=19;
  419.         Fade_ScrollInFromBottom=20;
  420.         Fade_ScrollInFromLeft=21;
  421.         Fade_ScrollInFromRight=22;
  422.         Fade_Circles =23;
  423.         Fade_Moiree15=24;
  424.  
  425. {---- für EMS-Routinen ----}
  426.  
  427. CONST BACKTAB:ARRAY[0..3] OF WORD=(0,PAGESIZE,2*PAGESIZE,3*PAGESIZE);
  428. TYPE Puffer=ARRAY[0..4*PAGESIZE-1 +15] OF BYTE; {Puffer für eine Seite}
  429. VAR  buf:^Puffer; {Zeiger darauf}
  430.  
  431. Const EMSInt = $67;   {für EMS benutzter Interrupt}
  432.       USEEMS = TRUE;  {bei FALSE: keine Nutzung von vorhandenem EMS,}
  433.                       {bei TRUE : Nutzung, wenn vorhanden}
  434.  
  435. Var EMSError:BYTE;    {<>0 heißt: es trat ein Fehler auf }
  436.     BackgroundEMSHandle:WORD;   {Zugriffshandle auf allozierten EMS-Block}
  437.     EMSused:BOOLEAN;  {zeigt an, ob tatsächlich EMS verwendet wird}
  438.  
  439.  PROCEDURE ShadowTab;
  440.  PROCEDURE SetShadowTab(brightness:BYTE);
  441.  PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
  442.  PROCEDURE GetPalette(VAR pal:Palette);
  443.  PROCEDURE FadeToPalette(destPal:Palette; AnzSteps:WORD);
  444.  FUNCTION LoadPalette(name:String; number:BYTE; VAR pal:Palette):WORD;
  445.  PROCEDURE EnsureEMSConsistency(EMSHandle:WORD);
  446.  PROCEDURE SetCycleTime(milliseconds:WORD);
  447.  PROCEDURE SetSpriteCycle(nr,len:WORD);
  448.  FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:WORD):POINTER;
  449.  PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:WORD);
  450.  PROCEDURE FreeImageMem(p:POINTER);
  451.  PROCEDURE InitGraph;
  452.  PROCEDURE Screen(pa:WORD);
  453.  PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:WORD);
  454.  PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
  455.  FUNCTION GetPixel(x,y:INTEGER):BYTE;
  456.  FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE;
  457.  FUNCTION PageGetPixel(x,y:INTEGER; pa:WORD):BYTE;
  458.  PROCEDURE PutPixel(x,y:INTEGER; color:Byte);
  459.  PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte);
  460.  PROCEDURE PagePutPixel(x,y:INTEGER; color:BYTE; pa:WORD);
  461.  PROCEDURE LoadFont(s:STRING);
  462.  FUNCTION OutTextLength(s:STRING):WORD;
  463.  PROCEDURE OutTextXY(x,y:INTEGER; pa:WORD; s:STRING);
  464.  PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
  465.  PROCEDURE MakeTextSprite(s:STRING; nr:WORD);
  466.  FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN;
  467.  PROCEDURE SetSplitIndex(number:INTEGER);
  468.  FUNCTION GetSplitIndex:INTEGER;
  469.  PROCEDURE SetAnimateWindow(x1,y1,x2,y2:INTEGER);
  470.  PROCEDURE Animate;
  471.  PROCEDURE FreeSpriteMem(number:WORD);
  472.  FUNCTION LoadSprite(name:String; number:WORD):WORD;
  473.  FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
  474.  PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
  475.  PROCEDURE SetBackgroundMode(mode:BYTE);
  476.  PROCEDURE MakeTileArea(FirstTile:BYTE; TileWidth,TileHeight:INTEGER);
  477.  PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
  478.  FUNCTION GetTile(x,y:INTEGER):BYTE;
  479.  PROCEDURE SetOffscreenTile(TileNr:BYTE);
  480.  PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
  481.  FUNCTION GetModeByte(Sp:WORD):BYTE;
  482.  PROCEDURE FillPage(pa:WORD; color:Byte);
  483.  PROCEDURE FillBackground(color:BYTE);
  484.  PROCEDURE GetBackgroundFromPage(pa:WORD);
  485.  PROCEDURE WritePage(name:STRING; pa:WORD);
  486.  PROCEDURE LoadPage(name:STRING; pa:WORD);
  487.  PROCEDURE WriteBackgroundPage(name:STRING);
  488.  PROCEDURE LoadBackgroundPage(name:STRING);
  489.  PROCEDURE FadeIn(pa,ti,style:WORD);
  490.  PROCEDURE CopyVRAMtoVRAM(source,dest:POINTER; len:WORD);
  491.  PROCEDURE IntroScroll(n,wait:WORD);
  492.  PROCEDURE InitRoutines;
  493.  PROCEDURE CloseRoutines;
  494.  FUNCTION GetErrorMessage:STRING;
  495.  FUNCTION FindFile(P:PathStr):PathStr;
  496.  
  497. {--------------------------------------------------------------------------}
  498.  
  499. IMPLEMENTATION
  500.  
  501. CONST ANIVGAVersionS:STRING[11]='AniVGA V1.2'; {Versionsnummer}
  502.       StartIndex=0;
  503.       EndIndex=StartIndex+3;
  504.       {Offsetadressen der Grafikseiten (in Segment $A000):}
  505.       Offset_Adr:Array[StartIndex..EndIndex] OF WORD=($0000,$3E80,$7D00,$BB80);
  506.       {Segmentadressen der Grafikseiten (bei Offset = 0) :}
  507.       Segment_Adr:ARRAY[StartIndex..EndIndex] OF WORD=($A000,$A3E8,$A7D0,$ABB8);
  508.  
  509.       {Sprite(header)aufbau:  }
  510.  
  511.       {0..1   DW Plane_0_Daten}
  512.       {2..3   DW Plane_1_Daten}
  513.       {4..5   DW Plane_2_Daten}
  514.       {6..7   DW Plane_3_Daten}
  515.       {8..9   DW Breite (in 4er-Gruppen)}
  516.       {10..11 DW Höhe in Zeilen}
  517.       {12..15 DB 1,2,4,8      ; Translate-Tabelle für Port-Ansteuerung}
  518.       {16..17 DW SpriteLength ; Länge der Spritedatei}
  519.       {                       ; jetzt für temporäre Variablen reservierter}
  520.       {                       ; Bereich:}
  521.       {18..19 DW ?            ; licutoff_      | hit1xfirst}
  522.       {20..21 DW ?            ; zeilenadr      | hit1yfirst}
  523.       {22..23 DW ?            ; bildx          | hit2xfirst}
  524.       {24..25 DW ?            ; yoffset_       | hit2yfirst}
  525.       {26..27 DW ?            ; end_min_start  | ueberlappx_1}
  526.       {28..29 DW ?            ; WinXMIN_       | ueberlappy_1}
  527.       {30..31 DW ?            ; WinXMAX_       | x1}
  528.       {32..33 DW ?            ; WinYMIN_       | x2}
  529.       {34..35 DW ?            ;                | y1}
  530.       {36..37 DW ?            ;                | y2}
  531.       {38..39 DB 'K','R'      ; Kennung als Sprite}
  532.       {40     DB 1            ; Versionsnummer}
  533.       {41     DB 0            ; Modusnummer für Sprite}
  534.       {42..43 DW linke_Begrenzungen }
  535.       {44..45 DW rechte_Begrenzungen}
  536.       {46..47 DW obere_Begrenzungen }
  537.       {48..49 DW untere_Begrenzungen}
  538.       {50..?? DB Daten}
  539.  
  540.       {zum Bsp.:  xxrxxxxx, mit: r=rot=40, g=grün=45, b=blau=35, x=weiß=30}
  541.       {           xrgrxxxx}
  542.       {           rbgbrxxx}
  543.  
  544.       {Adressen von wichtigen Werten innerhalb des Spriteheaders:}
  545.       Left=42;
  546.       Right=44;
  547.       Top=46;
  548.       Bottom=48;
  549.       Breite=8;
  550.       Hoehe=10;
  551.       Translate=12;
  552.       SpriteLength=16;
  553.       Kennung=38;
  554.       Version=40;
  555.       Modus=41;
  556.  
  557.       {Adressen der temporären Variablen für die Zeichenroutinen:}
  558.       licutoff_=18;
  559.       zeilenadr=20;
  560.       bildx=22;
  561.       yoffset_=24;
  562.       end_min_start=26;
  563.       WinXMIN_=28;
  564.       WinXMAX_=30;
  565.       WinYMIN_=32;
  566.  
  567.       {Adressen der temporären Variablen für die Kollisionsprüfroutine:}
  568.       hit1xfirst=18;
  569.       hit1yfirst=20;
  570.       hit2xfirst=22;
  571.       hit2yfirst=24;
  572.       ueberlappx_1=26;
  573.       ueberlappy_1=28;
  574.       x1=30;
  575.       x2=32;
  576.       y1=34;
  577.       y2=36;
  578.  
  579.       TranslateTab:ARRAY[0..3] OF BYTE=(1,2,4,8); {Für Maskenadressierung}
  580.       PICHeader:STRING[3]='PIC'; {Kennung in Bilderdateien}
  581.       Schatten :BYTE=70;         {Default-Helligkeit von Schatten}
  582.  
  583. TYPE SpriteHeader= RECORD
  584.                     Zeiger_auf_Plane:Array[0..3] OF Word;
  585.                     Breite_in_4er_Gruppen:WORD;
  586.                     Hoehe_in_Zeilen:WORD;
  587.                     Translate:Array[1..4] OF Byte;
  588.                     SpriteLength:WORD;
  589.                     Dummy:Array[1..10] OF Word;
  590.                     Kennung:ARRAY[1..2] OF CHAR;
  591.                     Version:BYTE;
  592.                     Modus:BYTE;
  593.                     ZeigerL,ZeigerR,ZeigerO,ZeigerU:Word;
  594.                    END;
  595.  
  596. CONST Kopf=SizeOf(SpriteHeader);
  597.       FontMask:ARRAY[0..MaxFontWidth-1] OF WORD=
  598.        ($4000,$2000,$1000,$800,$400,$200,$100,$80,$40,$20,$10,8,4,2,1);
  599.       internalFont:ARRAY[0..255,0..5] OF BYTE= {verwendeter Zeichensatz:  }
  600.      ((  0,  0,  0,  0,  0,  0), {#0}          {selbstgestrickter 6x6 Font}
  601.       (  0, 54,  0, 62, 28,  0), {#1}
  602.       ( 62, 42, 62, 34, 54, 62), {#2}
  603.       (  0, 20, 62, 62, 28,  8), {#3}
  604.       (  0,  8, 28, 62, 28,  8), {#4}
  605.       (  8, 28, 54, 62,  8, 28), {#5}
  606.       (  8, 28, 62, 62,  8, 28), {#6}
  607.       (  0,  8, 54, 54,  8,  0), {#7}
  608.       ( 62, 54, 42, 42, 54, 62), {#8}
  609.       (  0, 28, 50, 38, 28,  0), {#9}
  610.       ( 62, 34, 42, 42, 34, 62), {#10}
  611.       ( 14,  6,  8, 28, 34, 28), {#11}
  612.       ( 28, 34, 28,  8, 62,  8), {#12}
  613.       ( 14, 10,  8,  8, 56, 56), {#13}
  614.       (  0, 30, 18, 30, 18, 54), {#14}
  615.       (  0,  8, 42, 20, 42,  8), {#15}
  616.       (  0, 32, 56, 62, 56, 32), {#16}
  617.       (  0,  2, 14, 62, 14,  2), {#17}
  618.       (  8, 28, 42, 42, 28,  8), {#18}
  619.       (  0, 54, 54, 54,  0, 54), {#19}
  620.       (  0, 30, 42, 26, 10, 10), {#20}
  621.       (  6,  8,  4, 10, 36, 24), {#21}
  622.       (  0,  0,  0,  0, 62, 62), {#22}
  623.       (  8, 28,  8, 28,  8, 62), {#23}
  624.       (  0,  8, 28, 62,  8,  8), {#24}
  625.       (  0,  8,  8, 62, 28,  8), {#25}
  626.       (  0,  8,  4, 62,  4,  8), {#26}
  627.       (  0,  8, 16, 62, 16,  8), {#27}
  628.       (  0,  0, 48, 62,  0,  0), {#28}
  629.       (  0,  0, 20, 62, 20,  0), {#29}
  630.       (  0,  0,  0,  8, 28, 62), {#30}
  631.       (  0,  0, 62, 28,  8,  0), {#31}
  632.       (  0,  0,  0,  0,  0,  0), { }
  633.       (  0, 12, 12, 12,  0, 12), {!}
  634.       (  0, 20, 20,  0,  0,  0), {"}
  635.       (  0, 20, 62, 20, 62, 20), {#}
  636.       (  8, 30, 40, 28, 10, 60), { $}
  637.       (  0, 50,  4,  8, 16, 38), {%}
  638.       (  0, 28, 54, 28, 38, 26), {&}
  639.       (  0,  4,  8,  0,  0,  0), {'}
  640.       (  0, 28, 48, 48, 48, 28), {(}
  641.       (  0, 56, 12, 12, 12, 56), {)}
  642.       (  0, 20,  8, 62,  8, 20), {*}
  643.       (  0,  0,  8, 62,  8,  0), {+}
  644.       (  0,  0,  0,  8,  8, 16), {,}
  645.       (  0,  0,  0, 62,  0,  0), {-}
  646.       (  0,  0,  0,  0, 12,  0), {.}
  647.       (  1,  2,  4,  8, 16, 32), {/}
  648.       (  0, 28, 38, 42, 50, 28), {0}
  649.       (  0, 12, 28, 12, 12, 30), {1}
  650.       (  0, 60,  6, 28, 48, 62), {2}
  651.       (  0, 60,  6, 28,  6, 60), {3}
  652.       (  0, 48, 52, 62, 12, 12), {4}
  653.       (  0, 62, 48, 60,  6, 60), {5}
  654.       (  0, 62, 32, 62, 34, 62), {6}
  655.       (  0, 62,  6, 12, 24, 24), {7}
  656.       (  0, 28, 54, 28, 54, 28), {8}
  657.       (  0, 28, 34, 30,  2, 28), {9}
  658.       (  0,  0,  8,  0,  8,  0), {:}
  659.       (  0,  0,  8,  0,  8, 16), {;}
  660.       (  0,  6, 12, 24, 12,  6), {<}
  661.       (  0,  0, 62,  0, 62,  0), {=}
  662.       (  0, 24, 12,  6, 12, 24), {>}
  663.       ( 28, 34,  4,  8,  0,  8), {?}
  664.       (  0, 28, 34, 46, 32, 30), {@}
  665.       (  0, 28, 50, 62, 50, 50), {A}
  666.       (  0, 60, 50, 60, 50, 60), {B}
  667.       (  0, 30, 48, 48, 48, 30), {C}
  668.       (  0, 60, 54, 50, 54, 60), {D}
  669.       (  0, 62, 48, 60, 48, 62), {E}
  670.       (  0, 62, 48, 60, 48, 48), {F}
  671.       (  0, 30, 48, 54, 50, 30), {G}
  672.       (  0, 50, 50, 62, 50, 50), {H}
  673.       (  0, 30, 12, 12, 12, 30), {I}
  674.       (  0, 62,  2,  2, 50, 28), {J}
  675.       (  0, 50, 52, 56, 52, 50), {K}
  676.       (  0, 48, 48, 48, 48, 62), {L}
  677.       (  0, 34, 54, 42, 34, 34), {M}
  678.       (  0, 34, 50, 42, 38, 34), {N}
  679.       (  0, 28, 50, 50, 50, 28), {O}
  680.       (  0, 60, 50, 60, 48, 48), {P}
  681.       (  0, 24, 52, 52, 52, 26), {Q}
  682.       (  0, 60, 34, 60, 52, 50), {R}
  683.       (  0, 62, 48, 62,  6, 62), {S}
  684.       (  0, 62, 24, 24, 24, 24), {T}
  685.       (  0, 50, 50, 50, 50, 62), {U}
  686.       (  0, 50, 50, 50, 50, 12), {V}
  687.       (  0, 34, 34, 42, 62, 20), {W}
  688.       (  0, 34, 54, 28, 54, 34), {X}
  689.       (  0, 50, 50, 28, 12, 12), {Y}
  690.       (  0, 62,  6, 28, 48, 62), {Z}
  691.       (  0, 30, 24, 24, 24, 30), {[}
  692.       ( 32, 16,  8,  4,  2,  1), {\}
  693.       (  0, 30,  6,  6,  6, 30), {]}
  694.       (  8, 20, 34,  0,  0,  0), {^}
  695.       (  0,  0,  0,  0,  0, 62), {_}
  696.       ( 16,  8,  0,  0,  0,  0), {`}
  697.       (  0,  0, 28, 50, 50, 30), {a}
  698.       (  0, 32, 60, 34, 34, 60), {b}
  699.       (  0,  0, 30, 48, 48, 30), {c}
  700.       (  0,  2, 30, 34, 34, 30), {d}
  701.       (  0,  0, 28, 62, 32, 28), {e}
  702.       (  0,  6,  8, 30,  8,  8), {f}
  703.       (  0, 28, 34, 30,  2, 28), {g}
  704.       (  0, 48, 60, 50, 50, 50), {h}
  705.       ( 12,  0, 12, 12, 12, 12), {i}
  706.       (  6,  0,  6,  6, 54, 28), {j}
  707.       (  0, 48, 52, 56, 54, 54), {k}
  708.       (  0, 24, 24, 24, 24, 14), {l}
  709.       (  0,  0, 52, 62, 42, 34), {m}
  710.       (  0,  0, 60, 50, 50, 50), {n}
  711.       (  0,  0, 28, 50, 50, 28), {o}
  712.       (  0,  0, 60, 50, 60, 48), {p}
  713.       (  0,  0, 28, 38, 30,  6), {q}
  714.       (  0,  0, 44, 26, 24, 24), {r}
  715.       (  0, 14, 16, 12, 34, 28), {s}
  716.       (  0, 24, 62, 24, 26, 12), {t}
  717.       (  0,  0, 50, 50, 50, 30), {u}
  718.       (  0,  0, 50, 50, 50, 28), {v}
  719.       (  0,  0, 34, 42, 42, 28), {w}
  720.       (  0,  0, 54, 24, 12, 54), {x}
  721.       (  0,  0, 50, 62,  2, 28), {y}
  722.       (  0,  0, 60, 12, 48, 62), {z}
  723.       (  0, 14, 24, 48, 24, 14),(*{*)
  724.       (  0,  4,  4,  0,  4,  4), {|}
  725.       (  0, 56, 12,  6, 12, 56),(*}*)
  726.       (  0, 26, 36,  0,  0,  0), {~}
  727.       (  0,  8, 20, 34, 62,  0), {#127}
  728.       ( 28, 50, 32, 50, 28, 48), {#128}
  729.       (  0, 50,  0, 50, 50, 30), {#129}
  730.       (  6,  8, 28, 62, 32, 28), {#130}
  731.       (  4, 10,  0, 30, 49, 31), {#131}
  732.       ( 26,  0, 28, 50, 50, 30), {#132}
  733.       ( 12,  2, 28, 34, 34, 30), {#133}
  734.       (  4, 10,  4, 28, 50, 30), {#134}
  735.       (  0, 30, 48, 30,  4, 24), {#135}
  736.       ( 28,  0, 28, 62, 48, 28), {#136}
  737.       ( 20,  0, 28, 62, 32, 28), {#137}
  738.       ( 12,  2, 28, 62, 48, 28), {#138}
  739.       ( 26,  0, 12, 12, 12, 12), {#139}
  740.       ( 12, 18,  0, 12, 12, 12), {#140}
  741.       ( 24,  4,  0, 12, 12, 12), {#141}
  742.       ( 50,  0, 28, 50, 62, 50), {#142}
  743.       ( 12,  0, 28, 50, 62, 50), {#143}
  744.       ( 28, 62, 48, 60, 48, 62), {#144}
  745.       (  0, 52, 10, 28, 40, 22), {#145}
  746.       (  0, 14, 20, 62, 36, 38), {#146}
  747.       (  8, 20,  0, 28, 50, 28), {#147}
  748.       ( 20,  0, 28, 50, 50, 28), {#148}
  749.       ( 24,  4,  0, 28, 50, 28), {#149}
  750.       (  8, 20,  0, 50, 50, 30), {#150}
  751.       ( 24,  4,  0, 50, 50, 30), {#151}
  752.       ( 20,  0, 50, 62,  2, 28), {#152}
  753.       ( 20,  0, 28, 50, 50, 28), {#153}
  754.       ( 50,  0, 50, 50, 50, 62), {#154}
  755.       (  4, 30, 32, 32, 30,  4), {#155}
  756.       ( 12, 18, 56, 16, 34, 62), {#156}
  757.       ( 54,  8, 62,  8, 62,  8), {#157}
  758.       ( 48, 40, 52, 46, 36, 38), {#158}
  759.       ( 12, 10, 24, 12, 40, 24), {#159}
  760.       ( 12, 16,  0, 28, 34, 30), {#160}
  761.       ( 12, 16,  0,  8,  8,  8), {#161}
  762.       ( 12, 16,  0, 28, 50, 28), {#162}
  763.       ( 12, 16,  0, 50, 50, 30), {#163}
  764.       ( 26, 36,  0, 44, 18, 18), {#164}
  765.       ( 26, 36,  0, 50, 42, 38), {#165}
  766.       ( 28, 36, 26,  0, 62,  0), {#166}
  767.       ( 28, 34, 28,  0, 62,  0), {#167}
  768.       (  8,  0,  8, 16, 34, 28), {#168}
  769.       (  0,  0, 63, 48,  0,  0), {#169}
  770.       (  0,  0, 63,  3,  0,  0), {#170}
  771.       ( 18, 20,  8, 16, 42, 10), {#171}
  772.       ( 18, 20,  8, 20, 38,  2), {#172}
  773.       ( 12,  0, 12, 12, 12,  0), {#173}
  774.       ( 10, 20, 40, 20, 10,  0), {#174}
  775.       ( 40, 20, 10, 20, 40,  0), {#175}
  776.       ( 21, 42, 21, 42, 21, 42), {#176}
  777.       ( 63, 63, 63, 63, 63, 63), {#177}
  778.       ( 42, 21, 42, 21, 42, 21), {#178}
  779.       (  4,  4,  4,  4,  4,  4), {#179}
  780.       (  4,  4,  4, 60,  4,  4), {#180}
  781.       (  4,  4, 60,  4, 60,  4), {#181}
  782.       ( 10, 10, 10, 58, 10, 10), {#182}
  783.       (  0,  0,  0, 62, 10, 10), {#183}
  784.       (  0,  0, 60,  4, 60,  4), {#184}
  785.       ( 10, 10, 58,  2, 58, 10), {#185}
  786.       ( 10, 10, 10, 10, 10, 10), {#186}
  787.       (  0,  0, 62,  2, 58, 10), {#187}
  788.       ( 10, 10, 58,  2, 62,  0), {#188}
  789.       ( 10, 10, 10, 62,  0,  0), {#189}
  790.       (  4,  4, 60,  4, 60,  0), {#190}
  791.       (  0,  0,  0, 60,  4,  4), {#191}
  792.       (  4,  4,  4,  7,  0,  0), {#192}
  793.       (  4,  4,  4, 63,  0,  0), {#193}
  794.       (  0,  0,  0, 63,  4,  4), {#194}
  795.       (  4,  4,  4,  7,  4,  4), {#195}
  796.       (  0,  0,  0, 63,  0,  0), {#196}
  797.       (  4,  4,  4, 63,  4,  4), {#197}
  798.       (  4,  4,  7,  4,  7,  4), {#198}
  799.       ( 10, 10, 10, 11, 10, 10), {#199}
  800.       ( 10, 10, 11,  8, 15,  0), {#200}
  801.       (  0,  0, 15,  8, 11, 10), {#201}
  802.       ( 10, 10, 59,  0, 63,  0), {#202}
  803.       (  0,  0, 63,  0, 59, 10), {#203}
  804.       ( 10, 10, 11,  8, 11, 10), {#204}
  805.       (  0,  0, 63,  0, 63,  0), {#205}
  806.       ( 10, 10, 59,  0, 59, 10), {#206}
  807.       (  4,  4, 63,  0, 63,  0), {#207}
  808.       ( 10, 10, 10, 63,  0,  0), {#208}
  809.       (  0,  0, 63,  0, 63,  4), {#209}
  810.       (  0,  0,  0, 63, 10, 10), {#210}
  811.       ( 10, 10, 10, 15,  0,  0), {#211}
  812.       (  4,  4,  7,  4,  7,  0), {#212}
  813.       (  0,  0,  7,  4,  7,  4), {#213}
  814.       (  0,  0,  0, 15, 10, 10), {#214}
  815.       ( 10, 10, 10, 63, 10, 10), {#215}
  816.       (  4,  4, 63,  4, 63,  4), {#216}
  817.       (  4,  4,  4, 60,  0,  0), {#217}
  818.       (  0,  0,  7,  4,  4,  4), {#218}
  819.       ( 63, 63, 63, 63, 63, 63), {#219}
  820.       (  0,  0,  0, 63, 63, 63), {#220}
  821.       ( 48, 48, 48, 48, 48, 48), {#221}
  822.       (  3,  3,  3,  3,  3,  3), {#222}
  823.       ( 63, 63, 63,  0,  0,  0), {#223}
  824.       (  0,  0, 26, 36, 36, 26), {#224}
  825.       (  0, 28, 38, 44, 34, 44), {#225}
  826.       (  0, 62, 34, 32, 32, 32), {#226}
  827.       (  0,  0, 62, 20, 20, 20), {#227}
  828.       ( 62, 18,  8, 16, 34, 62), {#228}
  829.       (  0,  0, 30, 36, 36, 24), {#229}
  830.       (  0, 18, 18, 30, 16, 48), {#230}
  831.       (  0,  0, 26, 44,  8,  8), {#231}
  832.       (  0, 62,  8, 20,  8, 62), {#232}
  833.       (  0, 28, 34, 62, 34, 28), {#233}
  834.       (  0, 28, 34, 34, 20, 54), {#234}
  835.       ( 14, 16,  8, 28, 34, 28), {#235}
  836.       (  0,  0, 20, 42, 20,  0), {#236}
  837.       (  0,  2, 20, 42, 20, 32), {#237}
  838.       (  0, 30, 32, 62, 32, 30), {#238}
  839.       (  0,  0, 28, 34, 34, 34), {#239}
  840.       (  0, 62,  0, 62,  0, 62), {#240}
  841.       (  0,  8, 28,  8,  0, 62), {#241}
  842.       ( 16,  8,  4,  8, 16, 62), {#242}
  843.       (  4,  8, 16,  8,  4, 62), {#243}
  844.       (  4, 10,  8,  8,  8,  8), {#244}
  845.       (  8,  8,  8,  8, 40, 16), {#245}
  846.       (  0,  8,  0, 62,  0,  8), {#246}
  847.       ( 26, 36,  0, 26, 36,  0), {#247}
  848.       ( 24, 36, 24,  0,  0,  0), {#248}
  849.       (  0,  0,  0, 12,  0,  0), {#249}
  850.       (  0,  0,  0,  4,  0,  0), {#250}
  851.       ( 15,  8,  8, 40, 24,  8), {#251}
  852.       ( 44, 18, 18,  0,  0,  0), {#252}
  853.       ( 56,  4, 24, 32, 60,  0), {#253}
  854.       (  0,  0, 28, 28,  0,  0), {#254}
  855.       (  0,  0,  0,  0,  0,  0));{#255}
  856.  
  857. VAR Steigung:BYTE;        {entscheidet, welcher Alg. Anwendung findet}
  858.     DY_mal2,DY_m_DX_mal2:INTEGER;
  859.     oldMode:byte;
  860.     regs:registers;
  861.  
  862.     IsAT:BYTE;
  863.     TimeFlag:BYTE;
  864.     CycleTime:LONGINT;
  865.  
  866.     SPRITEAD  :ARRAY[0..LoadMAX] OF WORD;     {normalisierte Segmentadressen}
  867.     SPRITEPTR :ARRAY[0..LoadMAX] OF POINTER;  {vollständige 32-Bit-Zeiger   }
  868.     SPRITESIZE:ARRAY[0..LoadMAX] OF WORD;     {allozierte Spritegröße}
  869.  
  870.     CRTAddress, StatusReg : WORD;
  871.  
  872.     WinLowerRight,WinXMINdiv4,WinYMIN_mul_LINESIZE,
  873.     WinYMINmLINESIZEaWinXMINdiv4:WORD;
  874.     WinWidthDiv4:BYTE;
  875.     BWinXMIN,BWinYMIN,BWinXMAX,BWinYMAX,BWinLowerRight,
  876.     BWinYMIN_mul_LINESIZE:WORD; {Backups von Win* Variablen}
  877.  
  878.     SplitIndex,SplitIndex_mal2:INTEGER; {Splitpunkt für Sprites & Clipping}
  879.  
  880.  
  881. {-----------------------------------------------------}
  882.  
  883. PROCEDURE ShadowTab; ASSEMBLER;
  884. {Pseudoprozedur, um Daten der Farbumsetztabelle im Codesegment }
  885. {unterzubringen, AUF KEINEN FALL AUFRUFEN!!!                   }
  886. {Defaultwerte entsprechen Abdunkelung auf 70% des Farb-Helligkeitswerts}
  887. ASM
  888.    DB 254,104,120,124,112,108,114, 24, 20,128,144,  3,136,  5,140,  7
  889.    DB 254,254, 17, 17, 18, 19, 20, 20, 21,  8, 23, 24, 24, 25, 26,  7
  890.    DB   1,  1,107,108,  5,108,109,  4,  4,  4,  6,  6,116,116,117,  2
  891.    DB   2,  2,123,124,  3,124,125,  1,152,155,156,156,  5,156,156,157
  892.    DB 160,163,164,164,164,164,164,165,168,171,172,172,  3,172,172,173
  893.    DB  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
  894.    DB  24, 24, 24, 24, 24, 24, 24, 24,176,177,178,179,180,181,182,183
  895.    DB 184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199
  896.    DB 200,201,203,204,204,204,205,207,208,209,211,212,212,212,213,215
  897.    DB 216,217,219,220,220,220,221,223,246,227,228,228,228,228,228,229
  898.    DB 234,235,236,236,236,236,236,237,242,243,244,244,244,244,244,245
  899.    DB 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254
  900.    DB 254,254,254,254,254,254,254,254, 17, 17, 17, 17, 17, 17, 17, 17
  901.    DB  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
  902.    DB  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
  903.    DB  17, 17, 17, 17, 17, 17, 17, 17,254,254,254,254,254,254,254,  7
  904. END;
  905.  
  906. PROCEDURE CS_TranslateTab; ASSEMBLER;
  907. {kleine Pseudoprozedur, um die Umsetztabelle für die Bitmaske auch im}
  908. {Codesegment zu haben}
  909. ASM
  910.  DB 1,2,4,8
  911. END;
  912.  
  913. PROCEDURE SetShadowTab(brightness:BYTE);
  914. { in: brightness = gewünschte Helligkeit der Farben im Schattenbereich, }
  915. {                  in Prozent zu der Helligkeit ihrer Originalfarben    }
  916. {out: ShadowTab  = (Näherungs-)Farbtabelle für gewünschte Abdunkelung   }
  917. {     Schatten   = neue Helligkeit (Schatten ist globale Variable!)     }
  918. {rem: Defaultwert in ShadowTab ist 70% Helligkeit der Ursprungsfarben!  }
  919. {     Diese Routine dauert ihre Zeit (ca. 4 sec auf 8MHz-AT!)           }
  920.  
  921. VAR neue_Tabelle:ColorTable;
  922.     p1:POINTER;
  923.  
  924. BEGIN
  925.  IF (brightness<0) OR (brightness>100)
  926.   THEN BEGIN
  927.         Error:=Err_InvalidPercentage;
  928.         exit
  929.        END;
  930.  p1:=@neue_Tabelle; {Trick, da der Assembler nicht mit dem SS-Segment klarkommt}
  931.  ASM
  932.    MOV CX,256 {äußerer Schleifenzähler}
  933.    LES DI,p1  {ES:DI = ^neue_Tabelle[i] }
  934.    MOV SI,OFFSET ActualColors {DS:SI = ^ActualColors[]}
  935.  
  936.  @outerloop:
  937.    LODSB          {AL = tempColors[i].red}
  938.    MUL brightness {wird über Stack adressiert!}
  939.    MOV DL,100
  940.    DIV DL         {AL = tempColors[i].red * brightness DIV 100}
  941.    MOV BL,AL      {BL := AL = neuer Rotanteil}
  942.  
  943.    LODSB          {dto., für grün}
  944.    MUL brightness
  945.    MOV DL,100
  946.    DIV DL
  947.    MOV BH,AL      {...nach BH}
  948.  
  949.    LODSB          {dto., für blau}
  950.    MUL brightness
  951.    MOV DL,100
  952.    DIV DL
  953.    MOV DH,AL      {...nach DH}
  954.  
  955.      {BL / BH / DH = RGB-Anteile der Farbe, für die eine Näherung zu finden ist}
  956.      PUSH CX
  957.      PUSH SI
  958.      PUSH DI
  959.      PUSH BP
  960.  
  961.      MOV DI,65535 {bisher gefundenes minimales Fehlerquadrat}
  962.      MOV CX,256   {alle 256 Default-Farben durchsehen}
  963.      MOV SI,OFFSET ActualColors  {DS:SI = ^ActualColors[]}
  964.     @searchloop:
  965.      MOV AL,BL    {Differenz im Rotanteil berechnen}
  966.      SUB AL,[SI]
  967.      JL @noNewMin {neue Farbe darf nicht heller sein!}
  968.      MUL AL       {Fehlerquadrat berechnen}
  969.      MOV BP,AX
  970.  
  971.      MOV AL,BH    {dto., für Grünanteil}
  972.      SUB AL,[SI+1]
  973.      JL @noNewMin
  974.      MUL AL
  975.      ADD BP,AX
  976.      JC @noNewMin {mordsmäßige Abweichungen sofort ignorieren}
  977.  
  978.      MOV AL,DH    {dto., für Blauanteil}
  979.      SUB AL,[SI+2]
  980.      JL @noNewMin
  981.      MUL AL
  982.      ADD AX,BP
  983.      JC @noNewMin
  984.  
  985.      CMP AX,DI      {bessere Approximation gefunden?}
  986.      JAE @noNewMin  {nein}
  987.      MOV DI,AX      {ja, Fehlerquadrat und Farbe merken}
  988.      MOV DL,CL
  989.      OR DI,DI       {Fehlerquadrat = 0?}
  990.      JZ @ColorDone  {ja, bessere Näherung können wir nicht mehr finden!}
  991.  
  992.     @noNewMin:
  993.      ADD SI,3
  994.      LOOP @searchloop
  995.  
  996.        CMP DI,65535   {keine Farbe gefunden?}
  997.        JNE @ColorDone {doch, fertig!}
  998.        MOV CX,256     {nein, also nochmal suchen}
  999.        MOV SI,OFFSET ActualColors  {DS:SI = ^ActualColors[]}
  1000.       @searchloop2:
  1001.        LODSB
  1002.        SUB AL,BL      {Diff ≈±2^6 -> Quadrat ≈±2^12 -> 3 * Quadrat < MaxInt}
  1003.        IMUL AL        {also kein Overflow möglich}
  1004.        MOV BP,AX
  1005.  
  1006.        LODSB          {dto., für Grünanteil}
  1007.        SUB AL,BH
  1008.        IMUL AL
  1009.        ADD BP,AX
  1010.  
  1011.        LODSB          {dto., für Blauanteil}
  1012.        SUB AL,DH
  1013.        IMUL AL
  1014.        ADD AX,BP
  1015.  
  1016.        CMP AX,DI      {bessere Approximation gefunden?}
  1017.        JAE @noNewMin2 {nein}
  1018.        MOV DI,AX      {ja, Fehlerquadrat und Farbe merken}
  1019.        MOV DL,CL
  1020.  
  1021.       @noNewMin2:
  1022.        LOOP @searchloop2
  1023.  
  1024.  
  1025.     @ColorDone:     {100h - DL = gefundene optimale Farbe}
  1026.      POP BP
  1027.      POP DI         {ES:DI = ^neue_Tabelle[i] }
  1028.      POP SI         {DS:SI = ^ActualColors[i] }
  1029.      POP CX
  1030.  
  1031.    MOV AL,DL      {in neue_Tabelle[i] eintragen}
  1032.    NEG AL         {AL = 100h - DL = beste Näherung}
  1033.    STOSB
  1034.  
  1035.    DEC CX         {Ersatz für "LOOP @outerloop"; nächste Farbe!}
  1036.    JCXZ @fertig
  1037.    JMP @outerloop
  1038.   @fertig:
  1039.  
  1040.  END; {of ASM}
  1041.  MOVE(neue_Tabelle,@ShadowTab^,256); {Farbtabelle übernehmen}
  1042.  Schatten:=brightness
  1043. END;
  1044.  
  1045. PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
  1046. { in: pal = Zeiger auf zu setzende Palette }
  1047. {     update = TRUE/FALSE für: ShadowTab neu/nicht neu berechnen}
  1048. {out: ActualColors = aktuelle Farbpalette  }
  1049. {rem: Palette wurde übernommen und evtl. ShadowTab neuberechnet }
  1050. BEGIN
  1051.  IF @pal<>@ActualColors
  1052.   THEN ActualColors:=pal;  {Farbpalette in ActualColors übernehmen}
  1053.  ASM
  1054.    MOV SI,OFFSET ActualColors  {DS:SI = ^ActualColors[]}
  1055.  
  1056.    CLI
  1057.  
  1058.     mov dx,StatusReg
  1059.   @WaitNotVSyncLoop:
  1060.     in   al,dx
  1061.     and  al,8
  1062.     jnz  @WaitNotVSyncLoop
  1063.   @WaitVSyncLoop:
  1064.     in   al,dx
  1065.     and  al,8
  1066.     jz   @WaitVSyncLoop
  1067.  
  1068.    MOV DX,3C8h
  1069.    XOR AL,AL
  1070.    OUT DX,AL
  1071.    INC DX
  1072.  
  1073.    MOV CX,256/2
  1074.   @L1:
  1075.    LODSW
  1076.    OUT DX,AL
  1077.    MOV AL,AH
  1078.    OUT DX,AL
  1079.    LODSW
  1080.    OUT DX,AL
  1081.    MOV AL,AH
  1082.    OUT DX,AL
  1083.    LODSW
  1084.    OUT DX,AL
  1085.    MOV AL,AH
  1086.    OUT DX,AL
  1087.    LOOP @L1
  1088.  
  1089.    STI
  1090.  END; {of ASM}
  1091.  IF update THEN SetShadowTab(Schatten)
  1092. END;
  1093.  
  1094. PROCEDURE GetPalette(VAR pal:Palette); ASSEMBLER;
  1095. { in: pal = Zeiger auf Palette-Speicher}
  1096. {out: pal = momentan aktuelle Palette  }
  1097. ASM
  1098.    CLI
  1099.    XOR AL,AL
  1100.    MOV DX,3C7h
  1101.    OUT DX,AL
  1102.    LES DI,pal
  1103.    MOV CX,768
  1104.    MOV DX,3C9h
  1105.   @L1:
  1106.    IN AL,DX
  1107.    STOSB
  1108.    LOOP @L1
  1109.    STI
  1110. END;
  1111.  
  1112. PROCEDURE FadeToPalette(destPal:Palette; AnzSteps:WORD);
  1113. { in: ActualColors = aktuell gesetzte Farbpalette}
  1114. {     destPal  = Zielpalette}
  1115. {     AnzSteps = Zwischenschrittanzahl}
  1116. {out: ActualColors = destPal}
  1117. {rem: Die Prozedur blendet von ActualColors zu destPal über, und zwar}
  1118. {     in AnzSteps Schritten}
  1119. {     Das Palettesetzen wird auf den vertikalen Retrace synchronisiert,}
  1120. {     so daß ein Zwischenschritt mindestens 1/70 sec benötigt}
  1121. VAR oldColors,pal:Palette;
  1122.     i,steps:INTEGER;
  1123.     s,d:POINTER;
  1124. BEGIN
  1125.  dec(anzsteps);
  1126.  IF anzsteps<1
  1127.   THEN steps:=1
  1128.   ELSE steps:=anzsteps; {steps ins selbe Segment wie pal bringen}
  1129.  oldColors:=ActualColors;
  1130.  pal:=destpal;    {pal und oldColors ins selbe Segment bringen}
  1131.  s:=@pal; d:=@ActualColors;
  1132.  FOR i:=0 TO steps-1 DO
  1133.   BEGIN
  1134.    {jetzt per Assembler folgende Sequenz berechnen:}
  1135.    { FOR c:=0 TO 255 DO  }
  1136.    {  BEGIN              }
  1137.    {   ActualColors[c].red:=LONGINT(pal[c].red-oldColors[c].red)*i }
  1138.    {    DIV steps+ oldColors[c].red;                               }
  1139.    {   ActualColors[c].green:=LONGINT(pal[c].green-oldColors[c].green)*i }
  1140.    {     DIV steps+ oldColors[c].green;  }
  1141.    {   ActualColors[c].blue:=LONGINT(pal[c].blue-oldColors[c].blue)*i }
  1142.    {     DIV steps+ oldColors[c].blue; }
  1143.    {  END;  }
  1144.  
  1145.    ASM
  1146.     LES DI,d  {ES:DI = Zeiger auf ActualColors-Tabelle}
  1147.     LDS SI,s  {DS:SI = Zeiger auf pal-Tabelle}
  1148.     MOV BX,OFFSET oldColors-OFFSET pal -1  {DS:SI+BX+1 = Zeiger auf oldColors}
  1149.  
  1150.     MOV CX,256
  1151.    @docolor:
  1152.     XOR AH,AH
  1153.     LODSB           {AX := pal[c].red}
  1154.     SUB AL,[SI+BX]  
  1155.     SBB AH,0        {AX := pal[c].red - oldColors[c].red = delta}
  1156.     IMUL i          {DX:AX := delta * i}
  1157.     IDIV steps      {AX := delta * i/steps}
  1158.     ADD AL,[SI+BX]  {AX := delta * i/steps + oldColors[c].red}
  1159.     STOSB
  1160.  
  1161.     {dto. für grün}
  1162.     XOR AH,AH
  1163.     LODSB
  1164.     SUB AL,[SI+BX]
  1165.     SBB AH,0
  1166.     IMUL i
  1167.     IDIV steps
  1168.     ADD AL,[SI+BX]
  1169.     STOSB
  1170.  
  1171.     {dto. für blau}
  1172.     XOR AH,AH
  1173.     LODSB
  1174.     SUB AL,[SI+BX]
  1175.     SBB AH,0
  1176.     IMUL i
  1177.     IDIV steps
  1178.     ADD AL,[SI+BX]
  1179.     STOSB
  1180.  
  1181.     LOOP @docolor
  1182.  
  1183.     MOV AX,SEG @DATA
  1184.     MOV DS,AX
  1185.    END;
  1186.    SetPalette(ActualColors,FALSE)
  1187.   END;
  1188.  SetPalette(pal,TRUE)
  1189. END;
  1190.  
  1191. FUNCTION LoadPalette(name:String; number:BYTE; VAR pal:Palette):WORD;
  1192. { in: name   = Name des zu ladenden Palette-Files (Typ: "*.PAL" )}
  1193. {     number = Nummer, die die erste Farbe aus diesem File bekommen soll  }
  1194. {     ActualColors = gerade aktuelle Farbpalette}
  1195. {out: Anzahl der aus dem File gelesenen Farben (0 = Fehler trat auf)      }
  1196. {     pal = aus dem File gelesene Farbpalette, evtl. ergänzt}
  1197. {rem: Alle nicht überschriebenen Farben werden in "pal" auf die Werte der }
  1198. {     gerade aktuellen Farben "ActualColors" gesetzt; die Palette wurde   }
  1199. {     nur geladen, nicht gesetzt!}
  1200. LABEL quitloop;
  1201. VAR len:LONGINT;
  1202.     f:FileOfByte;
  1203.     i,count:WORD;
  1204.     TempPal:Palette;
  1205.     flag:BOOLEAN;
  1206.     tempName:STRING;
  1207. BEGIN
  1208.  count:=0;  {Zahl der bisher eingelesenen Paletteneinträge}
  1209.  tempName:=FindFile(name);
  1210.  IF tempName<>'' THEN name:=tempName;
  1211.  _assign(f,name);
  1212.  {$I-} _reset(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
  1213.  if (ioresult<>0) OR (CompressError<>CompressErr_NoError)
  1214.   THEN BEGIN  {Datei existiert nicht oder nicht unter diesem Pfad}
  1215.         Error:=Err_FileIO;
  1216.         CompressError:=CompressErr_NoError;
  1217.         LoadPalette:=0; exit
  1218.        END;
  1219.  len:=_filesize(f);  {Dateilänge ermitteln}
  1220.  if (len mod 3<>0) OR (len>3*256) OR (len<3)
  1221.   THEN BEGIN
  1222.         Error:=Err_NoPalette;
  1223.         goto quitloop;
  1224.        END;
  1225.  IF len+number*3>3*256
  1226.   THEN BEGIN
  1227.         Error:=Err_PaletteWontFit;
  1228.         goto quitloop;
  1229.        END;
  1230.  
  1231.  TempPal:=ActualColors; {temporäre Palette mit aktuellen Farben vorbesetzen}
  1232.  {$I-}
  1233.   _blockread(f,TempPal[number],len);
  1234.  {$IFDEF IOcheck} {$I+} {$ENDIF}
  1235.  
  1236.   IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
  1237.    THEN BEGIN
  1238.          Error:=Err_FileIO;
  1239.          CompressError:=CompressErr_NoError;
  1240.          goto quitloop;
  1241.         END;
  1242.  
  1243.   flag:=FALSE;
  1244.   FOR i:=number TO Pred(number+(len DIV 3))
  1245.    DO flag:=flag OR (TempPal[i].red>63)
  1246.                  OR (TempPal[i].green>63)
  1247.                  OR (TempPal[i].blue>63);
  1248.   IF flag
  1249.    THEN BEGIN
  1250.          Error:=Err_NoPalette;
  1251.          goto quitloop;
  1252.         END;
  1253.  
  1254.   {Alles ging gut: Palette zurückgeben}
  1255.   pal:=TempPal;
  1256.   count:=len DIV 3;
  1257.  
  1258. quitloop: ;
  1259.  _close(f);
  1260.  LoadPalette:=count
  1261. END;
  1262.  
  1263. {Nun folgen die Codestücke, die Verwendung finden, um ein Sprite auf den }
  1264. {Schirm zu bringen; die Schnittstelle ist für alle gleich:               }
  1265. { in: CX    = Anzahl Bytes, die von...                                   }
  1266. {     DS:SI = (Zeiger auf Quelladresse) nach...                          }
  1267. {     ES:BX = (Zeiger auf Zieladresse) zu bringen sind;                  }
  1268. {     DI    = Bitplane (0..3) (=X-Koordinate AND 3)                      }
  1269. {     Die Bitmaske für den richtigen Schreibe-Planezugriff wurde bereits }
  1270. {     gesetzt, eine evtl. nötige Leseplane dagegen nicht!                }
  1271. {     Die Routinen können sicher sein, daß CX<>0 ist                     }
  1272. {rem: Jede dieser Routinen MUSS EXAKT 16 Bytes lang und VOLL RELOKATIBEL }
  1273. {     sein, sowie die Register BP,DS,ES unverändert lassen!!!!!!!!!!!!!  }
  1274. {     Außerdem müssen die einzelnen Routinen zur Unterscheidbarkeit in   }
  1275. {     ihren ersten zwei Bytes paarweise verschieden sein!                }
  1276.  
  1277. PROCEDURE Modus0; ASSEMBLER;
  1278. {Modus 0 betrachtet die Farbe 0 als durchsichtig für den Hintergrund}
  1279. ASM
  1280.    INC CX
  1281.    STC                {BX so verringern, daß es zusammen mit SI zugleich}
  1282.    SBB BX,SI          {als Zielindex verwendet werden kann}
  1283.  @L1:
  1284.    LODSB              {Spritebyte holen }
  1285.    OR AL,AL           {ist es gleich 0? }
  1286.    LOOPZ @L1          {ja, nichts zu tun}
  1287.    JCXZ @L2           {alle Bytes durch?}
  1288.    MOV ES:[BX+SI],AL  {nein, übertragen }
  1289.    JMP @L1 {short}    {nächstes Byte    }
  1290.  @L2:
  1291. END;
  1292.  
  1293. PROCEDURE Modus1; ASSEMBLER;
  1294. {Modus 1 schreibt die Daten sofort ohne weitere Untersuchung auf den Schirm}
  1295. ASM
  1296.    MOV DI,BX          {DI setzen, damit Stringbefehle verwendet werden können}
  1297.    XOR AX,AX          {AX := 0 setzen}
  1298.    SHR CX,1           {Anzahl zu übertragender Wörter}
  1299.    REP MOVSW          {Daten auf einen Satz übertragen}
  1300.    ADC CX,AX          {noch ein einzelnes Byte übrig? }
  1301.    REP MOVSB
  1302.    MOV AX,AX          {4 Füllbytes; schneller als 4 NOPs}
  1303.    MOV AX,AX
  1304. END;
  1305.  
  1306. PROCEDURE Modus2Work; ASSEMBLER;
  1307. {Fortsetzung von Modus2 - all das, was nicht mehr in 16 Bytes unterzubringen}
  1308. {war, kommt hierher}
  1309. ASM
  1310.    OUT DX,AX          {Lesezugriff auf passende Plane ermöglichen}
  1311.  
  1312.    PUSH DS            {DS zeigt noch auf Spritedaten, muß aber auf }
  1313.                       {Hintergrund zeigen!                         }
  1314.    MOV AX,ES          {DS:SI := ES:DI  (Quellzeiger:=Zielzeiger)   }
  1315.    MOV DS,AX
  1316.    MOV SI,DI
  1317.    MOV BX,OFFSET ShadowTab   {Zeiger auf Farbumsetztabelle setzen  }
  1318.  
  1319.  @L4:
  1320.    LODSB              {Hintergrundfarbe holen...  }
  1321.    SEGCS XLAT         {...mit Farbtabelle umsetzen}
  1322.    STOSB              {...und auf aktueller Seite darstellen}
  1323.    LOOP @L4
  1324.  
  1325.    POP DS
  1326. END;
  1327.  
  1328. PROCEDURE Modus2; ASSEMBLER;
  1329. {Modus 2 ist für "Schatten" und ähnliches gedacht: hierbei werden die   }
  1330. {eigentlichen Spritedaten ignoriert und stattdessen die Hintergrunddaten}
  1331. {gelesen, die sich an der Spriteposition befinden und deren Farbwerte   }
  1332. {gegen die der Farbtabelle "ShadowTab" ersetzt (um bspw. Schatten zu    }
  1333. {erzeugen, müßte diese Tabelle zu jeder Farbe eine dunklere enthalten)  }
  1334. ASM
  1335.    MOV AX,DI          {Bitplane für Lesezugriff nach AX bringen}
  1336.    MOV DI,BX          {für Stringbefehle die Zieladresse nach DI bringen}
  1337.    MOV AH,AL          {Bitplane ins Highbyte bringen}
  1338.    MOV AL,4
  1339.    MOV DX,3CEh
  1340.    MOV SI,OFFSET Modus2Work  {fieser Trick: "CALL Modus2Work" würde (da re- }
  1341.    CALL SI                   {lativ codiert) zu falscher Adresse verzweigen!}
  1342. END;
  1343.  
  1344. PROCEDURE Modus3Work; ASSEMBLER;
  1345. {Fortsetzung von Modus3 - all das, was nicht mehr in 16 Bytes unterzubringen}
  1346. {war, kommt hierher}
  1347. ASM
  1348.    STC           
  1349.    SBB BX,SI      {Quell-/Zieldaten beide mit nur 1 Indexregister ansprechen}
  1350.    MOV DX,BP      {BP-Register retten}
  1351.    MOV BP,BX
  1352.    MOV BX,OFFSET ShadowTab   {Zeiger auf Farbumsetztabelle setzen }
  1353.  
  1354.  @L1:
  1355.    LODSB              {Spritedatum holen...  }
  1356.    OR AL,AL           { (Farbe 0 ignorieren, da "durchsichtig") }
  1357.    LOOPZ @L1
  1358.    JCXZ @L2
  1359.    MOV AL,ES:[BP+SI]  {Hintergrundfarbe holen...  }
  1360.    SEGCS XLAT         {...mit Farbtabelle umsetzen}
  1361.    MOV ES:[BP+SI],AL  {...und auf aktueller Seite darstellen}
  1362.    JMP @L1
  1363.  @L2:
  1364.    MOV BP,DX          {alten Inhalt von BP wiederherstellen}
  1365. END;
  1366.  
  1367. PROCEDURE Modus3; ASSEMBLER;
  1368. {Modus 3 ist ebenfalls für "Schatten" gedacht: hierbei werden alle Sprite-   }
  1369. {punkte, deren Farbe <>0 ist berücksichtigt: die Hintergrundfarbe, die sich  }
  1370. {unter diesen Punkten befindet, wird gegen den korrespondierenden Farbwert   }
  1371. {aus der Tabelle "ShadowTab" ersetzt.                                        }
  1372. {In anderen Worten: dieser Modus entspricht dem Modus 2, mit dem Unterschied,}
  1373. {daß die Spritefarbe 0 als für den Schatten durchsichtig betrachtet wird!    }
  1374. ASM
  1375.    MOV DX,3CEh        {Zugriff auf Leseplane vorbereiten:}
  1376.    MOV AX,DI          
  1377.    MOV AH,AL          {Leseplane nach AH bringen}
  1378.    MOV AL,4
  1379.    OUT DX,AX          {Lesezugriff auf passende Plane ermöglichen}
  1380.    INC CX             {Anzahl Bytes um 1 erhöhen (wg. LODSB) }
  1381.    MOV AX,OFFSET Modus3Work  {Trick damit Code relokatibel wird!}
  1382.    CALL AX
  1383. END;
  1384.  
  1385. PROCEDURE Adressen; ASSEMBLER;
  1386. {Tabelle der Startadressen der 3 Routinen im Codesegment}
  1387. ASM
  1388.    DW OFFSET Modus0
  1389.    DW OFFSET Modus1
  1390.    DW OFFSET Modus2
  1391.    DW OFFSET Modus3
  1392. END;
  1393.  
  1394.  
  1395. PROCEDURE GADR; ASSEMBLER;
  1396. {Tabelle der Grafikzeilen-Startadressen (Offset-Anteil)}
  1397. ASM
  1398.    DW $0000,$0050,$00A0,$00F0,$0140,$0190,$01E0,$0230
  1399.    DW $0280,$02D0,$0320,$0370,$03C0,$0410,$0460,$04B0
  1400.    DW $0500,$0550,$05A0,$05F0,$0640,$0690,$06E0,$0730
  1401.    DW $0780,$07D0,$0820,$0870,$08C0,$0910,$0960,$09B0
  1402.    DW $0A00,$0A50,$0AA0,$0AF0,$0B40,$0B90,$0BE0,$0C30
  1403.    DW $0C80,$0CD0,$0D20,$0D70,$0DC0,$0E10,$0E60,$0EB0
  1404.    DW $0F00,$0F50,$0FA0,$0FF0,$1040,$1090,$10E0,$1130
  1405.    DW $1180,$11D0,$1220,$1270,$12C0,$1310,$1360,$13B0
  1406.    DW $1400,$1450,$14A0,$14F0,$1540,$1590,$15E0,$1630
  1407.    DW $1680,$16D0,$1720,$1770,$17C0,$1810,$1860,$18B0
  1408.    DW $1900,$1950,$19A0,$19F0,$1A40,$1A90,$1AE0,$1B30
  1409.    DW $1B80,$1BD0,$1C20,$1C70,$1CC0,$1D10,$1D60,$1DB0
  1410.    DW $1E00,$1E50,$1EA0,$1EF0,$1F40,$1F90,$1FE0,$2030
  1411.    DW $2080,$20D0,$2120,$2170,$21C0,$2210,$2260,$22B0
  1412.    DW $2300,$2350,$23A0,$23F0,$2440,$2490,$24E0,$2530
  1413.    DW $2580,$25D0,$2620,$2670,$26C0,$2710,$2760,$27B0
  1414.    DW $2800,$2850,$28A0,$28F0,$2940,$2990,$29E0,$2A30
  1415.    DW $2A80,$2AD0,$2B20,$2B70,$2BC0,$2C10,$2C60,$2CB0
  1416.    DW $2D00,$2D50,$2DA0,$2DF0,$2E40,$2E90,$2EE0,$2F30
  1417.    DW $2F80,$2FD0,$3020,$3070,$30C0,$3110,$3160,$31B0
  1418.    DW $3200,$3250,$32A0,$32F0,$3340,$3390,$33E0,$3430
  1419.    DW $3480,$34D0,$3520,$3570,$35C0,$3610,$3660,$36B0
  1420.    DW $3700,$3750,$37A0,$37F0,$3840,$3890,$38E0,$3930
  1421.    DW $3980,$39D0,$3A20,$3A70,$3AC0,$3B10,$3B60,$3BB0
  1422.    DW $3C00,$3C50,$3CA0,$3CF0,$3D40,$3D90,$3DE0,$3E30
  1423.    DW $3E80
  1424. END;
  1425.  
  1426. FUNCTION EMSinstalled(VAR PageFrameSeg:WORD):Boolean;
  1427. { in: - }
  1428. {out: PageFrameSeg = Segmentadresse des EMS-Puffers}
  1429. {     TRUE/FALSE für: EMS installiert/nicht installiert}
  1430. {     Error, EMSError = evtl. Fehlercode}
  1431. {rem: Für USEEMS=FALSE wird immer FALSE zurückgegeben  }
  1432. TYPE Tag=ARRAY[1..8] OF CHAR;
  1433. VAR p:POINTER;
  1434. Begin
  1435.  EMSError:=0;
  1436.  IF NOT USEEMS
  1437.   THEN BEGIN
  1438.         EMSinstalled:=FALSE;
  1439.         exit
  1440.        END;
  1441.  GetIntVec(EMSInt,p);
  1442.  IF Tag(Ptr(SEG(p^),$A)^)='EMMXXXX0'
  1443.   THEN BEGIN {EMS-Handler vorhanden, aber bitte mind. V3.2:}
  1444.         WITH Regs DO
  1445.          BEGIN
  1446.           AH:=$46; Intr(EMSInt,Regs); {Versionscode holen}
  1447.           EMSInstalled:=AL>=$32;      {Version >= 3.2 ?  }
  1448.  
  1449.           AH:=$41; Intr(EMSInt,Regs); {BX=Segmentadresse, AH=evtl. Fehler}
  1450.           PageFrameSeg:=BX;
  1451.           EmsError:=AH;
  1452.           IF EmsError<>0 THEN Error:=Err_EMSError;
  1453.          END;
  1454.        END
  1455.   ELSE EMSInstalled:=FALSE
  1456. End;
  1457.  
  1458. FUNCTION EMSPagesAvailable:WORD;
  1459. { in: - }
  1460. {out: liefert Anzahl verfügbarer EMS Seiten (a 16K zurück)}
  1461. {     Error, EMSError = evtl. Fehlercode}
  1462. {rem: nur aufrufen, wenn EMSinstalled = TRUE!}
  1463. BEGIN
  1464.  WITH Regs DO
  1465.   BEGIN
  1466.    AH:=$42; Intr(EMSInt,Regs); {bestimme Anzahl freie Seiten}
  1467.    EMSPagesAvailable:=BX;
  1468.    EmsError := AH;
  1469.    IF EmsError<>0 THEN Error:=Err_EMSError;
  1470.   END;
  1471. END;
  1472.  
  1473. FUNCTION EMSAllocate(pages:Word):WORD;
  1474. { in: pages = #zu belegende EMS-Seiten}
  1475. {out: Handle auf diesen EMS-Bereich}
  1476. {     Error, EMSError = evtl. Fehlercode}
  1477. {rem: pages darf die PagesAvail-Anzahl nicht überschreiten!}
  1478. BEGIN
  1479.  With Regs do
  1480.   BEGIN
  1481.    AH:=$43;
  1482.    BX:=pages;
  1483.    Intr(EMSInt,Regs);
  1484.    EmsError    := AH;
  1485.    IF EmsError<>0 THEN Error:=Err_EMSError;
  1486.    EMSAllocate := DX;
  1487.   END;
  1488. END;
  1489.  
  1490. Procedure EMSSwapPageIn(EMSHandle, LogicNr,PhysicalNr:Word);
  1491. { in: EMSHandle  = Handle eines allozierten EMS-Blocks}
  1492. {     LogicNr    = logische Seite innerhalb dieses EMS-Blockes}
  1493. {     PhysicalNr = physikalische Seite des EMS-Frames (=0..3) }
  1494. {out: Error, EMSError = evtl. Fehlercode}
  1495. {rem: Mapped die logische Seite "LogicNr" des EMS-Bereichs, der mit dem}
  1496. {     Handle "EMSHandle" alloziert wurde, in die physikalische Seite   }
  1497. {     "PhysicalNr"}
  1498. {     Zugriff ist anschließend über MEM[BACKGNDADR:PhysicalNr*$4000] }
  1499. {     möglich}
  1500. {     PhysicalNr = 0..3}
  1501. {     LogicNr    = 0..allozierte Pageanzahl-1}
  1502. BEGIN
  1503.  With Regs do
  1504.   BEGIN
  1505.    AH:=$44; DX:=EMSHandle; BX:=LogicNr; AL:=PhysicalNr;
  1506.    Intr(EMSInt,Regs);
  1507.    EmsError := AH;
  1508.    IF EmsError<>0 THEN Error:=Err_EMSError;
  1509.   END;
  1510. END;
  1511.  
  1512. PROCEDURE EMSFillFrame(EMSHandle:WORD);
  1513. { in: EMSHandle = Handle auf 4 Seiten großen EMS-Bereich}
  1514. {out: EMS-Frame-Puffer wurde mit den ersten 4 Seiten gefüllt}
  1515. {     Error, EMSError = evtl. Fehlercode}
  1516. CONST a:ARRAY[0..7] OF WORD=(0,0,1,1,2,2,3,3);
  1517. BEGIN
  1518.  With Regs do
  1519.   BEGIN
  1520.    AX:=$5000; DX:=EMSHandle; CX:=4;
  1521.    DS:=Seg(a); SI:=Ofs(a);
  1522.    Intr(EMSInt,Regs);
  1523.    EmsError := AH;
  1524.    IF EmsError<>0 THEN Error:=Err_EMSError;
  1525.   END;
  1526. END;
  1527.  
  1528. PROCEDURE EMSRelease(handle:WORD);
  1529. { in: handle = Handle des freizugebenden EMS-Blockes}
  1530. {out: Error, EMSError = evtl. Fehlercode}
  1531. {rem: EMS-Block wurde freigegeben}
  1532. BEGIN
  1533.  With Regs do
  1534.   BEGIN
  1535.    AH:=$45;
  1536.    DX:=handle;
  1537.    Intr(EMSInt,Regs);
  1538.    EmsError:=AH;
  1539.    IF EmsError<>0 THEN Error:=Err_EMSError;
  1540.   END;
  1541. END;
  1542.  
  1543. FUNCTION EMSIsHardwareEMS:BOOLEAN;
  1544. { in: - }
  1545. {out: FALSE, wenn EMS nur emuliert wird}
  1546. {     Error, EMSError = evtl. Fehlercode}
  1547. {rem: Die Prüfung wird wie in der LIM4.0-Spezifikation empfohlen dadurch}
  1548. {     durchgeführt, daß eine logische Seite unter allen physikalischen  }
  1549. {     Seiten eingeblendet wird}
  1550. CONST a:ARRAY[0..7] OF WORD=(0,0,0,1,0,2,0,3);
  1551. VAR SegAdr,handle:WORD;
  1552.     mirror:BOOLEAN;
  1553. BEGIN
  1554.  IF NOT(EMSInstalled(SegAdr)) OR (EMSPagesAvailable<1)
  1555.   THEN EMSIsHardwareEMS:=TRUE  {irgendne bessere Idee?}
  1556.   ELSE BEGIN
  1557.         handle:=EMSAllocate(1);
  1558.         With Regs do
  1559.          BEGIN {log. Seite 0 in alle 4 physikalischen Seiten einblenden}
  1560.           AX:=$5000; DX:=handle; CX:=4;
  1561.           DS:=Seg(a); SI:=Ofs(a);
  1562.           Intr(EMSInt,Regs);
  1563.           EmsError := AH;
  1564.           IF EmsError<>0 THEN Error:=Err_EMSError;
  1565.          END;
  1566.         MEM[SegAdr:16383]:=0;
  1567.         mirror:=(MEM[SegAdr:1*16384+16383] OR
  1568.                  MEM[SegAdr:2*16384+16383] OR
  1569.                  MEM[SegAdr:3*16384+16383])=0;
  1570.         MEM[SegAdr:16383]:=$FF;
  1571.         mirror:=mirror AND
  1572.                 ((MEM[SegAdr:1*16384+16383] AND
  1573.                   MEM[SegAdr:2*16384+16383] AND
  1574.                   MEM[SegAdr:3*16384+16383])=$FF);
  1575.         EMSRelease(handle);
  1576.         EMSIsHardWareEMS:=mirror
  1577.        END;
  1578. END;
  1579.  
  1580. PROCEDURE EnsureEMSConsistency(EMSHandle:WORD);
  1581. { in: EMSused = TRUE}
  1582. {     EMSHandle = Handle für allozierten EMS-Block}
  1583. {     BACKGNDADR:0 = Start des EMS-Frames}
  1584. {out: Der EMS-Frame enthält die gewünschten 64K Daten}
  1585. BEGIN
  1586.  EMSFillFrame(EMSHandle); {Zugriff auf EMS vorbereiten}
  1587. END;
  1588.  
  1589. FUNCTION AT:BOOLEAN;
  1590. { in: - }
  1591. {out: TRUE/FALSE, wenn die Maschine (mindestens) ein AT ist}
  1592. BEGIN
  1593.  AT:=MEM[$F000:$FFFE]=$FC
  1594. END;
  1595.  
  1596.  
  1597. PROCEDURE SetCycleTime(milliseconds:WORD);
  1598. { in: Mindestzeit eines Animationszyklus in Millisekunden}
  1599. {out: CycleTime := dieser Wert in Mikrosekunden}
  1600. {     TimeFlag  := $80}
  1601. {rem: Für den ersten Animationszyklus nach Aufruf dieser Routine }
  1602. {     gilt wg. TimeFlag := $80 die Zeitbedingung noch nicht!     }
  1603. {     Schaltet der Benutzer (durch Angabe von milliseconds=0) die}
  1604. {     Zeitüberwachung explizit ab, so wird das durch IsAT := $80,}
  1605. {     d.h.: "Rechner ist ein PC" vorgetäuscht. Sonst ist IsAT = 0}
  1606. BEGIN
  1607.  TimeFlag:=$80;
  1608.  CycleTime:=LONGINT(milliseconds)*LONGINT(1000);
  1609.  IF (milliseconds<>0) AND AT
  1610.   THEN IsAT:=0     {ja, Zeitüberwachung soll benutzt werden  }
  1611.   ELSE IsAT:=$80   {nein, keine möglich oder nicht gewünscht }
  1612. END;
  1613.  
  1614. PROCEDURE SetSpriteCycle(nr,len:WORD);
  1615. { in: nr  = Spriteladenummer des ersten Sprites des Zyklus      }
  1616. {     len = Länge des zu definierenden Spritezyklus             }
  1617. {out: NextSprite[nr] bis NextSprite[nr+len-1] wurden so gesetzt,}
  1618. {     daß sie im Ring aufeinander zeigen, d.h.: sie bilden      }
  1619. {     einen Spritezyklus}
  1620. {rem: Soll der Zyklus aus nicht direkt aufeinanderfolgenden     }
  1621. {     (physikalischen) Sprites gebildet werden, so müssen die   }
  1622. {     entsprechenden Einträge in NextSprite[] manuell gemacht   }
  1623. {     werden }
  1624. {     Diese Routine verwendet SpriteLADEnummern!}
  1625. VAR i:WORD;
  1626. BEGIN
  1627.  IF (nr<1) OR (nr+len-1>LoadMAX)
  1628.   THEN Error:=Err_InvalidSpriteLoadNumber
  1629.   ELSE BEGIN
  1630.         FOR i:=nr TO nr+len-2 DO NextSprite[i]:=SUCC(i);
  1631.         NextSprite[PRED(nr+len)]:=nr  {letztes Sprite zeigt auf erstes}
  1632.        END;
  1633. END;
  1634.  
  1635.  
  1636. FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:WORD):POINTER;
  1637. { in: (x1,y1) = linke obere Ecke des zu sichernden Bildausschnittes       }
  1638. {     (x2,y2) = rechte untere Ecke dazu (alles virtuelle Koordinaten!)    }
  1639. {     pa      = Grafikseite, von der der Ausschnitt zu sichern ist (0..3) }
  1640. {     StartVirtualX,StartVirtualY = linke obere Bildschirmecke            }
  1641. {out: Zeiger auf Heapbereich, der den kopierten Bildausschnitt enthält    }
  1642. {     left_cut= evtl. nötiger linker Cutoff des Bildausschnittes (gibt an,}
  1643. {               um wieviel Punkte der Ausschnitt links außerhalb des      }
  1644. {               Bildschirm ragte)                                         }
  1645. {     right_cut, top_cut, bottom_cut = dto., für andere Ränder            }
  1646. {     was_cut = TRUE/FALSE, falls ein zurechtstutzen des Bildausschnittes }
  1647. {               nötig war/nicht nötig war                                 }
  1648. {rem: Der benötigte Speicher wird von der Routine automatisch reserviert  }
  1649. {     Sollte dies nicht möglich sein (oder liegt der Bildausschnitt gänz- }
  1650. {     lich außerhalb des sichtbaren Bereichs), so wird NIL zurückgegeben! }
  1651. {     Nur wenn "was_cut" TRUE ist, sind die Werte der globalen "..._cut"  }
  1652. {     Variablen <>0 gesetzt worden, d.h.: ist der Ausschnitt _ganz_ außer-}
  1653. {     halb des Bildes (also zurückgegebener Zeiger=NIL), dann liefert die }
  1654. {     Routine trotzdem "was_cut"=FALSE!}
  1655. VAR len,breite,hoehe,StartAdr,actualAdr,SegmAdr:WORD;
  1656.     p:POINTER;
  1657. BEGIN
  1658.  was_cut:=FALSE; left_cut:=0; right_cut:=0; top_cut:=0; bottom_cut:=0;
  1659.  dec(x1,StartVirtualX);   {Bildschirmkoordinaten berechnen}
  1660.  dec(y1,StartVirtualY);
  1661.  IF (x1>XMAX) or (y1>YMAX) or (x2<0) or (y2<0) or (x1>x2) or (y1>y2)
  1662.   THEN BEGIN  {Bildausschnitt nicht auf dem Bildschirm}
  1663.         GetImage:=NIL;
  1664.         exit
  1665.        END;
  1666.  {Ausschnitt auf Bildschirm zurechtklippen:}
  1667.  IF x1<0 THEN BEGIN left_cut :=-x1; x1:=0; was_cut:=TRUE END;
  1668.  IF y1<0 THEN BEGIN top_cut:=-y1; y1:=0; was_cut:=TRUE END;
  1669.  IF x2>XMAX THEN BEGIN right_cut :=x2-XMAX; x2:=XMAX; was_cut:=TRUE END;
  1670.  IF y2>YMAX THEN BEGIN bottom_cut:=y2-YMAX; y2:=YMAX; was_cut:=TRUE END;
  1671.  
  1672.  breite:=SUCC(x2-x1); hoehe:=SUCC(y2-y1);
  1673.  len:=breite*hoehe+2*2; {1 Pixel = 1 Byte; dazu: 2 Wörter für breite & hoehe}
  1674.  IF len>MaxAvail
  1675.   THEN BEGIN
  1676.         Error:=Err_NotEnoughMemory;
  1677.         GetImage:=NIL;
  1678.         exit
  1679.        END;
  1680.  IF (pa<0) OR (pa>SCROLLPAGE)  {Seitennummer muß 0..3 sein}
  1681.   THEN BEGIN
  1682.         Error:=Err_InvalidPageNumber;
  1683.         GetImage:=NIL;
  1684.         exit
  1685.        END
  1686.   ELSE SegmAdr:=Segment_Adr[pa];
  1687.  GetMem(p,len);         {Speicher auf dem Heap besorgen}
  1688.  IF pa<>BACKGNDPAGE
  1689.   THEN ASM {VRAM nach RAM}
  1690.         CLD
  1691.         LES DI,p        {ES:DI = Zeiger auf den besorgten Speicher}
  1692.         MOV AX,breite
  1693.         STOSW           {Breite zuerst ablegen...}
  1694.         MOV AX,hoehe
  1695.         STOSW           {...gefolgt von der Höhe, danach dann die Daten}
  1696.  
  1697.         MOV BX,AX       {BX := hoehe (für später) }
  1698.         MOV SI,y1
  1699.         SHL SI,1
  1700.         MOV SI,CS:[OFFSET gadr + SI]   {SI := y1 * LINESIZE}
  1701.         MOV AX,x1
  1702.         MOV DL,AL
  1703.         SHR AX,1
  1704.         SHR AX,1
  1705.         ADD SI,AX       {SI := Offsetanteil der Startadresse}
  1706.         MOV StartAdr,SI
  1707.         MOV actualAdr,SI
  1708.         AND DL,3
  1709.         MOV AH,DL
  1710.         MOV AL,4
  1711.         MOV DX,3CEh
  1712.         OUT DX,AX       {Startplane anwählen}
  1713.         MOV DS,SegmAdr
  1714.  
  1715.         {DS:SI = Zeiger auf erstes zu speicherndes Byte; ES:DI = Zieladr. dafür}
  1716.         {AH = Startplane, AL = 4, BX = abzuarbeitende Zeilenanzahl}
  1717.  
  1718.         MOV DX,breite
  1719.         ADD DX,3
  1720.         SHR DX,1
  1721.         SHR DX,1        {DX = Anzahl zu sichernde Bytes je Zeile}
  1722.  
  1723.       @L1:
  1724.         MOV CX,DX       {Daten einer Zeile abspeichern}
  1725.         SHR CX,1        {schneller als "REP MOVSB"}
  1726.         REP MOVSW
  1727.         ADC CX,CX
  1728.         REP MOVSB
  1729.         MOV SI,actualAdr  {Quellzeiger um 1 Grafikzeile weitersetzen}
  1730.         ADD SI,LINESIZE
  1731.         MOV actualAdr,SI
  1732.         DEC BX          {Zeilenzähler verringern}
  1733.         JNE @L1
  1734.  
  1735.         INC AH          {nächste Plane anwählen}
  1736.         CMP AH,4
  1737.         JNE @nowrap1    {wrap in den Bitplanes bedeutet: Startadresse}
  1738.         MOV AH,0        {um 1 Adresse weitersetzen! }
  1739.         INC StartAdr
  1740.       @nowrap1:
  1741.         MOV DX,3CEh
  1742.         OUT DX,AX
  1743.         MOV BX,hoehe
  1744.         MOV DX,breite
  1745.         INC DX
  1746.         INC DX
  1747.         SHR DX,1
  1748.         SHR DX,1
  1749.         MOV SI,StartAdr
  1750.         MOV actualAdr,SI
  1751.  
  1752.       @L2:
  1753.         MOV CX,DX
  1754.         SHR CX,1        {schneller als "REP MOVSB"}
  1755.         REP MOVSW
  1756.         ADC CX,CX
  1757.         REP MOVSB
  1758.         MOV SI,actualAdr
  1759.         ADD SI,LINESIZE
  1760.         MOV actualAdr,SI
  1761.         DEC BX
  1762.         JNE @L2
  1763.  
  1764.         INC AH
  1765.         CMP AH,4
  1766.         JNE @nowrap2
  1767.         MOV AH,0
  1768.         INC StartAdr
  1769.       @nowrap2:
  1770.         MOV DX,3CEh
  1771.         OUT DX,AX
  1772.         MOV BX,hoehe
  1773.         MOV DX,breite
  1774.         INC DX
  1775.         SHR DX,1
  1776.         SHR DX,1
  1777.         MOV SI,StartAdr
  1778.         MOV actualAdr,SI
  1779.  
  1780.       @L3:
  1781.         MOV CX,DX
  1782.         SHR CX,1        {schneller als "REP MOVSB"}
  1783.         REP MOVSW
  1784.         ADC CX,CX
  1785.         REP MOVSB
  1786.         MOV SI,actualAdr
  1787.         ADD SI,LINESIZE
  1788.         MOV actualAdr,SI
  1789.         DEC BX
  1790.         JNE @L3
  1791.  
  1792.         INC AH
  1793.         CMP AH,4
  1794.         JNE @nowrap3
  1795.         MOV AH,0
  1796.         INC StartAdr
  1797.       @nowrap3:
  1798.         MOV DX,3CEh
  1799.         OUT DX,AX
  1800.         MOV BX,hoehe
  1801.         MOV DX,breite
  1802.         SHR DX,1
  1803.         SHR DX,1
  1804.         MOV SI,StartAdr
  1805.         MOV actualAdr,SI
  1806.  
  1807.       @L4:
  1808.         MOV CX,DX
  1809.         SHR CX,1        {schneller als "REP MOVSB"}
  1810.         REP MOVSW
  1811.         ADC CX,CX
  1812.         REP MOVSB
  1813.         MOV SI,actualAdr
  1814.         ADD SI,LINESIZE
  1815.         MOV actualAdr,SI
  1816.         DEC BX
  1817.         JNE @L4
  1818.  
  1819.         MOV AX,SEG @DATA
  1820.         MOV DS,AX
  1821.        END
  1822.   ELSE BEGIN
  1823.         IF EMSused
  1824.          THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  1825.        ASM {RAM nach RAM}
  1826.         CLD
  1827.         LES DI,p        {ES:DI = Zeiger auf den besorgten Speicher}
  1828.         MOV AX,breite
  1829.         STOSW           {Breite zuerst ablegen...}
  1830.         MOV AX,hoehe
  1831.         STOSW           {...gefolgt von der Höhe, danach dann die Daten}
  1832.  
  1833.         MOV DX,AX       {DX := hoehe (für später) }
  1834.         MOV SI,y1
  1835.         SHL SI,1
  1836.         MOV SI,CS:[OFFSET gadr + SI]   {SI := y1 * LINESIZE}
  1837.         MOV AX,x1
  1838.         MOV BL,AL
  1839.         SHR AX,1
  1840.         SHR AX,1
  1841.         ADD SI,AX       {SI := Offsetanteil der Startadresse}
  1842.         AND BX,3
  1843.         MOV AH,BL
  1844.         SHL BX,1
  1845.         ADD SI,[OFFSET BACKTab + BX]  {Startplane anwählen}
  1846.         MOV StartAdr,SI
  1847.         MOV actualAdr,SI
  1848.         MOV DS,SegmAdr
  1849.         MOV BX,DX
  1850.  
  1851.         {DS:SI = Zeiger auf erstes zu speicherndes Byte; ES:DI = Zieladr. dafür}
  1852.         {AH = Startplane, BX = abzuarbeitende Zeilenanzahl}
  1853.  
  1854.         MOV DX,breite
  1855.         ADD DX,3
  1856.         SHR DX,1
  1857.         SHR DX,1        {DX = Anzahl zu sichernde Bytes je Zeile}
  1858.  
  1859.       @L1:
  1860.         MOV CX,DX       {Daten einer Zeile abspeichern}
  1861.         SHR CX,1        {schneller als "REP MOVSB"}
  1862.         REP MOVSW
  1863.         ADC CX,CX
  1864.         REP MOVSB
  1865.         MOV SI,actualAdr  {Quellzeiger um 1 Grafikzeile weitersetzen}
  1866.         ADD SI,LINESIZE
  1867.         MOV actualAdr,SI
  1868.         DEC BX          {Zeilenzähler verringern}
  1869.         JNE @L1
  1870.  
  1871.         INC AH          {nächste Plane anwählen}
  1872.         ADD StartAdr,PAGESIZE
  1873.         CMP AH,4
  1874.         JNE @nowrap1    {wrap in den Bitplanes bedeutet: Startadresse}
  1875.         MOV AH,0        {um 1 Adresse weitersetzen! }
  1876.         SUB StartAdr,4*PAGESIZE -1
  1877.       @nowrap1:
  1878.         MOV BX,hoehe
  1879.         MOV DX,breite
  1880.         INC DX
  1881.         INC DX
  1882.         SHR DX,1
  1883.         SHR DX,1
  1884.         MOV SI,StartAdr
  1885.         MOV actualAdr,SI
  1886.  
  1887.       @L2:
  1888.         MOV CX,DX
  1889.         SHR CX,1        {schneller als "REP MOVSB"}
  1890.         REP MOVSW
  1891.         ADC CX,CX
  1892.         REP MOVSB
  1893.         MOV SI,actualAdr
  1894.         ADD SI,LINESIZE
  1895.         MOV actualAdr,SI
  1896.         DEC BX
  1897.         JNE @L2
  1898.  
  1899.         INC AH
  1900.         ADD StartAdr,PAGESIZE
  1901.         CMP AH,4
  1902.         JNE @nowrap2
  1903.         MOV AH,0
  1904.         SUB StartAdr,4*PAGESIZE -1
  1905.       @nowrap2:
  1906.         MOV BX,hoehe
  1907.         MOV DX,breite
  1908.         INC DX
  1909.         SHR DX,1
  1910.         SHR DX,1
  1911.         MOV SI,StartAdr
  1912.         MOV actualAdr,SI
  1913.  
  1914.       @L3:
  1915.         MOV CX,DX
  1916.         SHR CX,1        {schneller als "REP MOVSB"}
  1917.         REP MOVSW
  1918.         ADC CX,CX
  1919.         REP MOVSB
  1920.         MOV SI,actualAdr
  1921.         ADD SI,LINESIZE
  1922.         MOV actualAdr,SI
  1923.         DEC BX
  1924.         JNE @L3
  1925.  
  1926.         INC AH
  1927.         ADD StartAdr,PAGESIZE
  1928.         CMP AH,4
  1929.         JNE @nowrap3
  1930.         MOV AH,0
  1931.         SUB StartAdr,4*PAGESIZE -1
  1932.       @nowrap3:
  1933.         MOV BX,hoehe
  1934.         MOV DX,breite
  1935.         SHR DX,1
  1936.         SHR DX,1
  1937.         MOV SI,StartAdr
  1938.         MOV actualAdr,SI
  1939.  
  1940.       @L4:
  1941.         MOV CX,DX
  1942.         SHR CX,1        {schneller als "REP MOVSB"}
  1943.         REP MOVSW
  1944.         ADC CX,CX
  1945.         REP MOVSB
  1946.         MOV SI,actualAdr
  1947.         ADD SI,LINESIZE
  1948.         MOV actualAdr,SI
  1949.         DEC BX
  1950.         JNE @L4
  1951.  
  1952.         MOV AX,SEG @DATA
  1953.         MOV DS,AX
  1954.        END;
  1955.        END;
  1956.  GetImage:=p
  1957. END;
  1958.  
  1959. PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:WORD);
  1960. { in: (x,y) = linke obere Ecke des Zieles (in virtuellen Koordinaten)   }
  1961. {     p     = Zeiger auf (durch GetImage erstellten) Bildausschnitt     }
  1962. {     pa    = Grafikseite, in die der Bildausschnitt kopiert werden soll}
  1963. {     StartVirtualX,StartVirtualY = linke obere Bildschirmecke          }
  1964. {out: - }
  1965. {rem: Der Bildausschnitt wurde zur Bildschirmdarstellung zurechtgeklippt}
  1966. {     Bei Übergabe von NIL als Zeiger stellt die Routine gar nichts dar }
  1967. {     Dies hilft für eine direkte Übernahme der von GetImage gelieferten}
  1968. {     Werte!}
  1969. VAR breite,hoehe,SegmAdr,actualAdr,StartAdr,breite1,breite2,breite3,breite4,
  1970.     licut_div4,topcut,pl_adr1,pl_adr2,pl_adr3,pl_adr4:WORD;
  1971.     licutoff,temp:INTEGER;
  1972. BEGIN
  1973.  IF p=NIL THEN exit;
  1974.  dec(x,StartVirtualX);   {Bildschirmkoordinaten berechnen}
  1975.  dec(y,StartVirtualY);
  1976.  IF (x>XMAX) or (y>YMAX) THEN exit;
  1977.  IF (pa<0) OR (pa>SCROLLPAGE)
  1978.   THEN BEGIN
  1979.         Error:=Err_InvalidPageNumber;
  1980.         exit
  1981.        END
  1982.   ELSE SegmAdr:=Segment_Adr[pa];
  1983.  breite:=MEMW[SEG(p^):OFS(p^)];
  1984.  hoehe :=MEMW[SEG(p^):OFS(p^)+2];
  1985.  IF (x+breite<=0) or (y+hoehe<=0) THEN exit;
  1986.  IF x<0 THEN BEGIN licutoff:=-x; x:=0 END
  1987.         ELSE licutoff:=0;
  1988.  IF y<0 THEN BEGIN
  1989.               topcut:=-y;
  1990.               y:=0
  1991.              END
  1992.         ELSE topcut:=0;
  1993.  
  1994.  breite1:=(breite + 3) shr 2;  {Breite einer Zeile für die erste, zweite,}
  1995.  breite2:=(breite + 2) shr 2;  {dritte und vierte Bitplane}
  1996.  breite3:=(breite + 1) shr 2;
  1997.  breite4:=(breite + 0) shr 2;
  1998.  
  1999.  {Anfangsadressen der 4 Bitplanes berechnen; dabei evtl. linken cutoff mit}
  2000.  {einbeziehen (plus 4 Bytes zum überspringen von "breite" und "hoehe"     }
  2001.  licut_div4:=licutoff shr 2;
  2002.  pl_adr1:=4 +licut_div4 +topcut*breite1;
  2003.  pl_adr2:=4 +licut_div4 +topcut*breite2 +hoehe*breite1;
  2004.  pl_adr3:=4 +licut_div4 +topcut*breite3 +hoehe*(breite1+breite2);
  2005.  pl_adr4:=4 +licut_div4 +topcut*breite4 +hoehe*(breite1+breite2+breite3);
  2006.  
  2007.  {licutoff mod 4 gibt an, in welcher Reihenfolge die Punkte aus dem Heap  }
  2008.  {gelesen werden müssen: 0 = Planereihenfolge (1,2,3,4); 1=(2,3,4,1);     }
  2009.  {2=(3,4,1,2); 3=(4,1,2,3); zu beachten ist, daß die Breiten der einzelnen}
  2010.  {Bitplanetabellen natürlich mit diesen verbunden bleibt und deshalb      }
  2011.  {mitgetauscht werden muß!}
  2012.  ASM
  2013.     CLD
  2014.     MOV AX,licutoff
  2015.     AND AL,3
  2016.     OR AL,AL
  2017.     JE @no_exchange
  2018.     CMP AL,1
  2019.     JNE @L10
  2020.  
  2021.     MOV AX,pl_adr2     {Verschiebung um 1 Bit: }
  2022.     MOV BX,pl_adr3     {AX = Plane2, BX = Plane3, CX = Plane4, DX = Plane1}
  2023.     MOV CX,pl_adr4
  2024.     MOV DX,pl_adr1     {wrap-around, deshalb: Adresse um 1 erhöhen, was   }
  2025.     INC DX             {einer Weitersetzung um 4 Bildpunkte entspricht    }
  2026.     MOV pl_adr1,AX     {(z.B.: Pixel (1,5,9,...),(2,6,10,...),(3,7,11,...)}
  2027.     MOV pl_adr2,BX     {und (0,4,8,...); letztere Bitplane wird um 1 Byte }
  2028.     MOV pl_adr3,CX     {weitergesetzt: liefert (richtige) (4,8,12,...)    }
  2029.     MOV pl_adr4,DX     {(Planes abwechselnd von oben nach unten lesen!)   }
  2030.     MOV AX,breite2     {Jetzt Planebreiten: }
  2031.     MOV BX,breite3     {AX = Plane2, BX = Plane3, CX = Plane4, DX = Plane1}
  2032.     MOV CX,breite4
  2033.     MOV DX,breite1
  2034.     JMP @store
  2035.  
  2036.   @L10:
  2037.     CMP AL,2
  2038.     JNE @L20
  2039.  
  2040.     MOV AX,pl_adr3     {Verschiebung um 2 Bit: }
  2041.     MOV BX,pl_adr4     {AX = Plane3, BX = Plane4, CX = Plane1, DX = Plane2}
  2042.     MOV CX,pl_adr1
  2043.     INC CX
  2044.     MOV DX,pl_adr2
  2045.     INC DX
  2046.     MOV pl_adr1,AX
  2047.     MOV pl_adr2,BX
  2048.     MOV pl_adr3,CX
  2049.     MOV pl_adr4,DX
  2050.     MOV AX,breite3     {dto. für Planebreiten: }
  2051.     MOV BX,breite4     {AX = Plane3, BX = Plane4, CX = Plane1, DX = Plane2}
  2052.     MOV CX,breite1
  2053.     MOV DX,breite2
  2054.     JMP @store
  2055.   @L20:
  2056.     MOV AX,pl_adr4     {Verschiebung um 3 Bit: }
  2057.     MOV BX,pl_adr1     {AX = Plane4, BX = Plane1, CX = Plane2, DX = Plane3}
  2058.     INC BX
  2059.     MOV CX,pl_adr2
  2060.     INC CX
  2061.     MOV DX,pl_adr3
  2062.     INC DX
  2063.     MOV pl_adr1,AX
  2064.     MOV pl_adr2,BX
  2065.     MOV pl_adr3,CX
  2066.     MOV pl_adr4,DX
  2067.     MOV AX,breite4     {dto. für Planebreiten: }
  2068.     MOV BX,breite1     {AX = Plane4, BX = Plane1, CX = Plane2, DX = Plane3}
  2069.     MOV CX,breite2
  2070.     MOV DX,breite3
  2071.   @store:
  2072.     MOV breite1,AX
  2073.     MOV breite2,BX
  2074.     MOV breite3,CX
  2075.     MOV breite4,DX
  2076.  
  2077.   @no_exchange:        {jetzt gilt: (pl_adr?,breite?) enthalten die Source-}
  2078.                        {Bitplanes/-breiten in der richtigen Reihenfolge    }
  2079.  
  2080.     MOV AX,topcut
  2081.     SUB hoehe,AX       {Höhe um evtl. oberen cutoff korrigieren}
  2082.     MOV AX,licutoff
  2083.     SUB breite,AX      {dto. für Breite und linken cutoff}
  2084.  
  2085.     MOV AX,x           {falls Ausschnitt über rechten Bildschirmrand}
  2086.     ADD AX,breite      {ragen würde: rechten cutoff bestimmen       }
  2087.     SUB AX,XMAX+1
  2088.     JLE @no_recutoff
  2089.     SUB breite,AX      {AX Punkte rechts abschneiden}
  2090.   @no_recutoff:
  2091.  
  2092.     MOV AX,y           {genau dasselbe für unteren Bildschirmrand}
  2093.     ADD AX,hoehe
  2094.     SUB AX,YMAX+1
  2095.     JLE @no_bocutoff
  2096.     SUB hoehe,AX       {AX Zeilen unten abschneiden}
  2097.   @no_bocutoff:
  2098.  END;
  2099.  
  2100.  IF pa<>BACKGNDPAGE
  2101.   THEN ASM {RAM nach VRAM}
  2102.         LDS SI,p
  2103.         ADD pl_adr2,SI     {Offsetanteil des Zeigers zu den Planeadr. addieren}
  2104.         ADD pl_adr3,SI
  2105.         ADD pl_adr4,SI
  2106.  
  2107.         ADD SI,pl_adr1     {breite, hoehe und Teile oberhalb des Bildschirms}
  2108.         MOV ES,SegmAdr
  2109.  
  2110.         MOV DI,y
  2111.         SHL DI,1
  2112.         MOV DI,CS:[OFFSET gadr + DI]  {DI := y * LINESIZE}
  2113.         MOV AX,x
  2114.         MOV BL,AL
  2115.         SHR AX,1
  2116.         SHR AX,1
  2117.         ADD DI,AX         {DI := y * LINESIZE + (x DIV 4)}
  2118.         MOV StartAdr,DI
  2119.         MOV actualAdr,DI
  2120.  
  2121.         AND BX,3          {Startplane := x mod 3}
  2122.         MOV AH,CS:[OFFSET CS_TranslateTab + BX]
  2123.         MOV AL,2
  2124.         MOV DX,3C4h
  2125.         OUT DX,AX         {als Schreibplane anwählen}
  2126.  
  2127.         MOV DX,hoehe
  2128.         MOV DI,actualAdr
  2129.  
  2130.         {DS:SI = Zeiger auf Daten, ES:DI = Zieladresse dafür auf dem Schirm,}
  2131.         {AH = Bitmaske für Zugriff, AL = 2 }
  2132.         MOV BX,breite
  2133.         ADD BX,3
  2134.         SHR BX,1
  2135.         SHR BX,1
  2136.         mov cx,bx
  2137.       @L1:
  2138.         push si
  2139.         SHR CX,1        {schneller als "REP MOVSB"}
  2140.         REP MOVSW
  2141.         ADC CX,CX
  2142.         REP MOVSB
  2143.         pop si
  2144.         mov cx,bx
  2145.         add si,breite1
  2146.         MOV DI,actualAdr
  2147.         ADD DI,LINESIZE
  2148.         MOV actualAdr,DI
  2149.         DEC DX
  2150.         JNE @L1
  2151.  
  2152.  
  2153.         SHL AH,1          {nächste Bitplane anwählen; bei einem wrap von}
  2154.         CMP AH,16         {Bitplane 3 zu Bitplane 0 muß dabei die Start-}
  2155.         JNE @nowrap1      {adresse um 1 Byte weitergesetzt werden       }
  2156.         MOV AH,1
  2157.         INC StartAdr
  2158.       @nowrap1:
  2159.         MOV DX,3C4h
  2160.         OUT DX,AX
  2161.         MOV SI,pl_adr2
  2162.         MOV DI,StartAdr
  2163.         MOV actualAdr,DI
  2164.         MOV DX,hoehe
  2165.         MOV BX,breite
  2166.         INC BX
  2167.         INC BX
  2168.         SHR BX,1
  2169.         SHR BX,1
  2170.         mov cx,bx
  2171.       @L2:
  2172.         push si
  2173.         SHR CX,1        {schneller als "REP MOVSB"}
  2174.         REP MOVSW
  2175.         ADC CX,CX
  2176.         REP MOVSB
  2177.         pop si
  2178.         mov cx,bx
  2179.         add si,breite2
  2180.         MOV DI,actualAdr
  2181.         ADD DI,LINESIZE
  2182.         MOV actualAdr,DI
  2183.         DEC DX
  2184.         JNE @L2
  2185.  
  2186.  
  2187.         SHL AH,1
  2188.         CMP AH,16
  2189.         JNE @nowrap2
  2190.         MOV AH,1
  2191.         INC StartAdr
  2192.       @nowrap2:
  2193.         MOV DX,3C4h
  2194.         OUT DX,AX
  2195.         MOV SI,pl_adr3
  2196.         MOV DI,StartAdr
  2197.         MOV actualAdr,DI
  2198.         MOV DX,hoehe
  2199.         MOV BX,breite
  2200.         INC BX
  2201.         SHR BX,1
  2202.         SHR BX,1
  2203.         mov cx,bx
  2204.       @L3:
  2205.         push si
  2206.         SHR CX,1        {schneller als "REP MOVSB"}
  2207.         REP MOVSW
  2208.         ADC CX,CX
  2209.         REP MOVSB
  2210.         pop si
  2211.         mov cx,bx
  2212.         add si,breite3
  2213.         MOV DI,actualAdr
  2214.         ADD DI,LINESIZE
  2215.         MOV actualAdr,DI
  2216.         DEC DX
  2217.         JNE @L3
  2218.  
  2219.  
  2220.         SHL AH,1
  2221.         CMP AH,16
  2222.         JNE @nowrap3
  2223.         MOV AH,1
  2224.         INC StartAdr
  2225.       @nowrap3:
  2226.         MOV DX,3C4h
  2227.         OUT DX,AX
  2228.         MOV SI,pl_adr4
  2229.         MOV DI,StartAdr
  2230.         MOV actualAdr,DI
  2231.         MOV DX,hoehe
  2232.         MOV BX,breite
  2233.         SHR BX,1
  2234.         SHR BX,1
  2235.         mov cx,bx
  2236.       @L4:
  2237.         push si
  2238.         SHR CX,1        {schneller als "REP MOVSB"}
  2239.         REP MOVSW
  2240.         ADC CX,CX
  2241.         REP MOVSB
  2242.         pop si
  2243.         mov cx,bx
  2244.         add si,breite4
  2245.         MOV DI,actualAdr
  2246.         ADD DI,LINESIZE
  2247.         MOV actualAdr,DI
  2248.         DEC DX
  2249.         JNE @L4
  2250.  
  2251.         MOV AX,SEG @DATA
  2252.         MOV DS,AX
  2253.        END
  2254.   ELSE BEGIN
  2255.         IF EMSused
  2256.          THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  2257.        ASM {RAM nach RAM}
  2258.         MOV AX,DS
  2259.         MOV ES,AX         {ES := altes Datensegment}
  2260.  
  2261.         LDS SI,p
  2262.         ADD pl_adr2,SI    {Offsetanteil des Zeigers zu den Planeadr. addieren}
  2263.         ADD pl_adr3,SI
  2264.         ADD pl_adr4,SI
  2265.  
  2266.         ADD SI,pl_adr1    {breite, hoehe und Teile oberhalb des Bildschirms}
  2267.  
  2268.         MOV DI,y
  2269.         SHL DI,1
  2270.         MOV DI,CS:[OFFSET gadr + DI]  {DI := y * LINESIZE}
  2271.         MOV AX,x
  2272.         MOV BL,AL
  2273.         SHR AX,1
  2274.         SHR AX,1
  2275.         ADD DI,AX         {DI := y * LINESIZE + (x DIV 4)}
  2276.  
  2277.         AND BX,3          {Startplane := x mod 3}
  2278.         MOV AL,BL         {Kopie davon nach AL}
  2279.         SHL BX,1
  2280.         ADD DI,ES:[OFFSET BACKTab + BX]  {als Schreibplane anwählen}
  2281.         MOV StartAdr,DI
  2282.         MOV actualAdr,DI
  2283.  
  2284.         MOV DX,hoehe
  2285.         MOV DI,actualAdr
  2286.         MOV ES,SegmAdr
  2287.  
  2288.         {DS:SI = Zeiger auf Daten, ES:DI = Zieladresse dafür auf dem Schirm,}
  2289.         {AL = Startplane}
  2290.         MOV BX,breite
  2291.         ADD BX,3
  2292.         SHR BX,1
  2293.         SHR BX,1
  2294.         mov cx,bx
  2295.       @L1:
  2296.         push si
  2297.         SHR CX,1        {schneller als "REP MOVSB"}
  2298.         REP MOVSW
  2299.         ADC CX,CX
  2300.         REP MOVSB
  2301.         pop si
  2302.         mov cx,bx
  2303.         add si,breite1
  2304.         MOV DI,actualAdr
  2305.         ADD DI,LINESIZE
  2306.         MOV actualAdr,DI
  2307.         DEC DX
  2308.         JNE @L1
  2309.  
  2310.  
  2311.         INC AL                {nächste Bitplane anwählen; bei einem wrap von}
  2312.         ADD StartAdr,PAGESIZE {Bitplane 3 zu Bitplane 0 muß dabei die Start-}
  2313.         AND AL,3              {adresse um 1 Byte weitergesetzt werden       }
  2314.         JNE @nowrap1
  2315.         SUB StartAdr,4*PAGESIZE-1
  2316.       @nowrap1:
  2317.         MOV SI,pl_adr2
  2318.         MOV DI,StartAdr
  2319.         MOV actualAdr,DI
  2320.         MOV DX,hoehe
  2321.         MOV BX,breite
  2322.         INC BX
  2323.         INC BX
  2324.         SHR BX,1
  2325.         SHR BX,1
  2326.         mov cx,bx
  2327.       @L2:
  2328.         push si
  2329.         SHR CX,1        {schneller als "REP MOVSB"}
  2330.         REP MOVSW
  2331.         ADC CX,CX
  2332.         REP MOVSB
  2333.         pop si
  2334.         mov cx,bx
  2335.         add si,breite2
  2336.         MOV DI,actualAdr
  2337.         ADD DI,LINESIZE
  2338.         MOV actualAdr,DI
  2339.         DEC DX
  2340.         JNE @L2
  2341.  
  2342.  
  2343.         INC AL                {nächste Bitplane anwählen; bei einem wrap von}
  2344.         ADD StartAdr,PAGESIZE {Bitplane 3 zu Bitplane 0 muß dabei die Start-}
  2345.         AND AL,3              {adresse um 1 Byte weitergesetzt werden       }
  2346.         JNE @nowrap2
  2347.         SUB StartAdr,4*PAGESIZE-1
  2348.       @nowrap2:
  2349.         MOV SI,pl_adr3
  2350.         MOV DI,StartAdr
  2351.         MOV actualAdr,DI
  2352.         MOV DX,hoehe
  2353.         MOV BX,breite
  2354.         INC BX
  2355.         SHR BX,1
  2356.         SHR BX,1
  2357.         mov cx,bx
  2358.       @L3:
  2359.         push si
  2360.         SHR CX,1        {schneller als "REP MOVSB"}
  2361.         REP MOVSW
  2362.         ADC CX,CX
  2363.         REP MOVSB
  2364.         pop si
  2365.         mov cx,bx
  2366.         add si,breite3
  2367.         MOV DI,actualAdr
  2368.         ADD DI,LINESIZE
  2369.         MOV actualAdr,DI
  2370.         DEC DX
  2371.         JNE @L3
  2372.  
  2373.  
  2374.         INC AL                {nächste Bitplane anwählen; bei einem wrap von}
  2375.         ADD StartAdr,PAGESIZE {Bitplane 3 zu Bitplane 0 muß dabei die Start-}
  2376.         AND AL,3              {adresse um 1 Byte weitergesetzt werden       }
  2377.         JNE @nowrap3
  2378.         SUB StartAdr,4*PAGESIZE-1
  2379.       @nowrap3:
  2380.         MOV SI,pl_adr4
  2381.         MOV DI,StartAdr
  2382.         MOV actualAdr,DI
  2383.         MOV DX,hoehe
  2384.         MOV BX,breite
  2385.         SHR BX,1
  2386.         SHR BX,1
  2387.         mov cx,bx
  2388.       @L4:
  2389.         push si
  2390.         SHR CX,1        {schneller als "REP MOVSB"}
  2391.         REP MOVSW
  2392.         ADC CX,CX
  2393.         REP MOVSB
  2394.         pop si
  2395.         mov cx,bx
  2396.         add si,breite4
  2397.         MOV DI,actualAdr
  2398.         ADD DI,LINESIZE
  2399.         MOV actualAdr,DI
  2400.         DEC DX
  2401.         JNE @L4
  2402.  
  2403.         MOV AX,SEG @DATA
  2404.         MOV DS,AX
  2405.        END
  2406.        END;
  2407. END;
  2408.  
  2409. PROCEDURE FreeImageMem(p:POINTER);
  2410. { in: p = Zeiger auf per GetImage() gespeichertes Bild}
  2411. {out: - }
  2412. {rem: Der für das Bild reservierte Heap-Speicher wurde freigegeben}
  2413. BEGIN
  2414.  IF p<>NIL THEN FreeMem(p,MEMW[Seg(p^):Ofs(p^)]*MEMW[Seg(p^):Ofs(p^)+2] + 2*2)
  2415. END;
  2416.  
  2417. PROCEDURE Screen(pa:WORD);
  2418. { in: pa = anzuzeigende Bildschirmseite (0..1) }
  2419. {out: - }
  2420. {rem: Es wurde auf die Darstellung der Grafikseite pa umgeschaltet     }
  2421. {     Dabei wurde NICHT auf irgendwelche Retrace-Signale synchronisiert}
  2422. {     Sinnvoll sind nur die Seiten 0 oder 1, es findet aber keine      }
  2423. {     Überprüfung statt!}
  2424. BEGIN
  2425.  ASM
  2426.     MOV DX,CRTAddress        {CRT-Controller}
  2427.     MOV AL,$0D               {LB-Startadress-Register}
  2428.     CLI                      {Darf keinenfalls unterbrochen werden!}
  2429.     OUT DX,AL
  2430.     INC DX
  2431.                              {Realisiere "AX := Offset_Adr[pa]":}
  2432.     MOV BX,pa
  2433.     MOV SI,BX
  2434.     AND SI,3                 {Pagewert * 2 (da Worteinträge!)}
  2435.     SHL SI,1                 {dazu Startadresse des Feldes addieren}
  2436.     ADD SI,OFFSET Offset_Adr-StartIndex*2  {evtl. Verschiebung korrigieren}
  2437.     LODSW                    {und Wert holen}
  2438.     OUT DX,AL                {LB der neuen Startadresse setzen}
  2439.     DEC DX
  2440.     MOV AL,$0C
  2441.     OUT DX,AL
  2442.     INC DX
  2443.     MOV AL,AH                {HB der neuen Startadresse setzen}
  2444.     OUT DX,AL
  2445.     STI
  2446.  END;
  2447. END;
  2448.  
  2449. PROCEDURE InitGraph;
  2450. { in: PAGE = aktuelle Grafikseite}
  2451. {out: - }
  2452. {rem: Schaltet die VGA-Karte in den 320x200x256x4-Modus; ACHTUNG!    }
  2453. {     Dieser Modus ist verschieden vom Modus $13 der VGA!!!          }
  2454. {     Dabei wird auf die Darstellung der Seite 1-PAGE geschaltet     }
  2455. {     Es werden die Defaultfarben des Modus $13 gesetzt!             }
  2456. BEGIN
  2457.  ASM
  2458.     MOV AX,0013h   {Mit BIOS Grafikmodus $13 (=320x200x256) setzen   }
  2459.     INT 10h        
  2460.     MOV DX,03C4h   {im Sequenzer das Speichermodusregister auswählen }
  2461.     MOV AL,04      
  2462.     OUT DX,AL      
  2463.     INC DX         {Daten über das zugehörige Datenregister lesen    }
  2464.     IN  AL,DX      
  2465.     AND AL,0F7h    {Bit 3 := 0: 4 Planes nicht chainen}
  2466.     OR  AL,04      {Bit 2 := 1: kein odd/even-Mechan. }
  2467.     OUT DX,AL      {Neuen Wert wirksam machen}
  2468.     MOV DX,03C4h   {S.o.: Sequenzerregister2 (=Map-Maske) wählen,... }
  2469.     MOV AL,02      
  2470.     OUT DX,AL      
  2471.     INC DX
  2472.     MOV AL,0Fh     {...und Zugriff auf alle 4 Bitmaps erlauben       }
  2473.     OUT DX,AL      
  2474.     MOV AX,0A000h  {Ab Segment A000h nun 8000h logische Wörter =     }
  2475.     MOV ES,AX      {4*8000h physikalische Wörter (wg. den 4 Bit-     }
  2476.     SUB DI,DI      {planes) auf 0 setzen                             }
  2477.     MOV AX,DI      
  2478.     MOV CX,8000h   
  2479.     CLD
  2480.     REP STOSW
  2481.  
  2482.     MOV DX,CRTAddress  {im CRT-Controller das underline-location- }
  2483.     MOV AL,14h         {Register auswählen, den Wert aus dem      }
  2484.     OUT DX,AL          {zugehörigen Datenregister auslesen:       }
  2485.     INC DX
  2486.     IN  AL,DX
  2487.     AND AL,0BFh    {Bit 6:=0: keine Doppelwortadressierung von}
  2488.     OUT DX,AL      {Daten im Bildschirmspeicher durchführen}
  2489.     DEC DX         
  2490.     MOV AL,17h     {Mode-control-Register auswählen  }
  2491.     OUT DX,AL      
  2492.     INC DX
  2493.     IN  AL,DX
  2494.     OR  AL,40h     {Bit 6 := 1: Adressierung des Speichers=lineares Bitfeld}
  2495.     OUT DX,AL      
  2496.  END;
  2497.  Screen(1-PAGE);   {sichtbar ist immer die nichtaktuelle Grafikseite}
  2498.  SetPalette(DefaultColors,FALSE)  {Standardpalette setzen, sicher ist sicher}
  2499. END;
  2500.  
  2501.  
  2502. VAR d,dp,dq,WindowY2:INTEGER;
  2503. PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
  2504. { in: x1,y1,x2,y2 = Koordinaten zweier Punkte, }
  2505. {     Color       = Farbe (0..255)             }
  2506. {     StartVirtualX,StartVirtualY = linke obere Bildschirmecke}
  2507. {     WinClip     = TRUE, wenn Linie auf Win* Fenster geclippt werden soll }
  2508. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = Fenster für evtl. Clipping}
  2509. {out: - }
  2510. {rem: Es wurde eine Linie von den VIRTUELLEN Punkten (x1,y1) nach (x2,y2)  }
  2511. {     in der Farbe COLOR gezeichnet; die Routine führt dabei selber die    }
  2512. {     Umrechnung der angeg. Koordinaten in absolute Bildschirmkoordinaten  }
  2513. {     sowie evtl. notwendige Clipping-Schritte aus.                        }
  2514. {     Die Linie wird NICHT automatisch in den Hintergrund übernommen,      }
  2515. {     d.h.: sie ist nur für einen Animationszyklus sichtbar (soll sie      }
  2516. {     permanent bleiben, so muß sie in den Hintergrund gezeichnet werden!)}
  2517. {     (Deshalb ist es sinnvoll, diese Routine NACH Aufruf von ANIMATE aus- }
  2518. {     zuführen, da die gezeichnete Linie sonst sofort wieder verschwindet) }
  2519. CONST CodeLinks =$7;  {%0111}
  2520.       CodeRechts=$B;  {%1011}
  2521.       CodeOben  =$D;  {%1101}
  2522.       CodeUnten =$E;  {%1110}
  2523. BEGIN
  2524.   IF EMSused
  2525.    THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  2526.  
  2527.   {zuerst Linie auf sichtbaren Bereich zurechtklippen; dazu Sutherland-}
  2528.   {Cohen-Algorithmus verwenden: 4 Bit-Code für links|rechts|oben|unten}
  2529.   ASM
  2530.      CLD
  2531.      XOR BX,BX
  2532.      MOV SI,XMAX
  2533.      XOR DI,DI
  2534.      MOV AX,YMAX
  2535.      CMP WinClip,FALSE
  2536.      JE @Start
  2537.      MOV BX,WinXMIN
  2538.      MOV SI,WinXMAX
  2539.      MOV DI,WinYMIN
  2540.      MOV AX,WinYMAX
  2541.    @Start:
  2542.      MOV WindowY2,AX
  2543.      {BX|SI|DI|WindowY2 = WinXMIN|WinXMAX|WinYMIN|WinYMAX bzw. 0|XMAX|0|YMAX}
  2544.  
  2545.      MOV CL,$F         {mit %1111 anfangen}
  2546.      MOV AX,x2
  2547.      SUB AX,StartVirtualX   {x2 in absolute Koordinaten umrechnen}
  2548.      MOV x2,AX
  2549.      CMP AX,BX         {x2 < WindowX1 ?}
  2550.      JL @GC1Punkt2     {ja, Flag für "Punkt links vom Fenster" belassen}
  2551.      AND CL,CodeLinks  {nein, Flag rücksetzen}
  2552.    @GC1Punkt2:
  2553.      CMP AX,SI         {x2 > WindowX2 ?}
  2554.      JG @GC2Punkt2     {ja, Flag für "Punkt rechts vom Fenster" belassen}
  2555.      AND CL,CodeRechts {nein, Flag rücksetzen}
  2556.    @GC2Punkt2:
  2557.      MOV AX,y2
  2558.      SUB AX,StartVirtualY   {y2 in absolute Koordinaten umrechnen}
  2559.      MOV y2,AX
  2560.      CMP AX,DI         {y2 < WindowY1 ?}
  2561.      JL @GC3Punkt2     {ja, Flag für "Punkt oberhalb des Fensters" bel.}
  2562.      AND CL,CodeOben   {nein, Flag rücksetzen}
  2563.    @GC3Punkt2:
  2564.      CMP AX,WindowY2   {y2 > WindowY2 ?}
  2565.      JG @GC4Punkt2     {ja, Flag für "Punkt unterhalb des Fensters" bel.}
  2566.      AND CL,CodeUnten
  2567.    @GC4Punkt2:         {CL enthält jetzt den Gebietscode für Punkt 2}
  2568.  
  2569.      MOV AX,x1
  2570.      SUB AX,StartVirtualX   {x1 in absolute Koordinaten umrechnen}
  2571.      MOV x1,AX
  2572.      MOV AX,y1
  2573.      SUB AX,StartVirtualY   {y1 in absolute Koordinaten umrechnen}
  2574.      MOV y1,AX
  2575.  
  2576.    @Punkt1:
  2577.      MOV CH,$F         {mit %1111 anfangen}
  2578.      MOV AX,x1
  2579.      CMP AX,BX         {x1 < WindowX1 ?}
  2580.      JL @GC1Punkt1     {ja, Flag für "Punkt links vom Fenster" belassen}
  2581.      AND CH,CodeLinks  {nein, Flag rücksetzen}
  2582.    @GC1Punkt1:
  2583.      CMP AX,SI         {x1 > WindowX2 ?}
  2584.      JG @GC2Punkt1     {ja, Flag für "Punkt rechts vom Fenster" belassen}
  2585.      AND CH,CodeRechts {nein, Flag rücksetzen}
  2586.    @GC2Punkt1:
  2587.      MOV AX,y1
  2588.      CMP AX,DI         {y1 < WindowY1 ?}
  2589.      JL @GC3Punkt1     {ja, Flag für "Punkt oberhalb des Fensters" bel.}
  2590.      AND CH,CodeOben   {nein, Flag rücksetzen}
  2591.    @GC3Punkt1:
  2592.      CMP AX,WindowY2   {y1 > WindowY2 ?}
  2593.      JG @GC4Punkt1     {ja, Flag für "Punkt unterhalb des Fensters" bel.}
  2594.      AND CH,CodeUnten
  2595.    @GC4Punkt1:         {CH enthält jetzt den Gebietscode für Punkt 1}
  2596.  
  2597.    {CL enthält den Gebietscode für Punkt 2, CH den für Punkt 1}
  2598.  
  2599.      MOV AX,CX
  2600.      AND AL,AH         {Code1 AND Code2 <> 0 ?}
  2601.      JNZ @LineReady    {ja, Linie ganz außerhalb des Windows}
  2602.      MOV AX,CX
  2603.      OR AL,AH          {Code1 OR Code2 = 0 ?}
  2604.      JZ @DrawLine      {ja, Linie ganz innerhalb des Windows}
  2605.  
  2606.    {Nun eigentliches Clipping vornehmen: }
  2607.      MOV AX,CX
  2608.      OR AH,AH          {Code1 =0 ?}
  2609.      JNZ @CL3          {nein, alles ok}
  2610.      MOV AX,x1         {ja, Punkte vertauschen!}
  2611.      XCHG AX,x2
  2612.      MOV x1,AX
  2613.      MOV AX,y1
  2614.      XCHG AX,y2
  2615.      MOV y1,AX
  2616.      XCHG CL,CH
  2617.    @CL3:
  2618.      MOV AX,x2
  2619.      SUB AX,x1        
  2620.      MOV dp,AX        {dp := x2 - x1}
  2621.      MOV AX,y2
  2622.      SUB AX,y1        
  2623.      MOV dq,AX        {dq := y2 - y1}
  2624.  
  2625.      MOV AL,CH        {AL := Code1}
  2626.      TEST AL,NOT CodeLinks     {Punkt1 links des Windows?}
  2627.      JZ @CL4                   {nein}
  2628.      {ja, neue Koordinaten berechnen:}
  2629.      { y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX1 - X1) }
  2630.      { und x1 := WindowX1}
  2631.      MOV AX,BX
  2632.      SUB AX,x1        {AX := WindowX1-x1}
  2633.      IMUL dq
  2634.      IDIV dp
  2635.      ADD y1,AX
  2636.      MOV x1,BX
  2637.      JMP @Punkt1
  2638.  
  2639.    @CL4:
  2640.      TEST AL,NOT CodeRechts    {Punkt1 rechts des Windows?}
  2641.      JZ @CL5                   {nein}
  2642.      {ja, berechne:}
  2643.      { y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX2 - X1), x1 := WindowX2}
  2644.      MOV AX,SI  {SI = WindowX2}
  2645.      SUB AX,x1
  2646.      IMUL dq
  2647.      IDIV dp
  2648.      ADD y1,AX
  2649.      MOV x1,SI
  2650.      JMP @Punkt1
  2651.  
  2652.    @CL5:
  2653.      TEST AL,NOT CodeOben      {Punkt1 oberhalb des Windows?}
  2654.      JZ @CL6                   {nein}
  2655.      {ja, berechne:}
  2656.      { x1 := x1 + (x2 - x1) / (y2 - y1) * (WindowY1 - y1), y1 := WindowY1 }
  2657.      MOV AX,DI  {DI = WindowY1}
  2658.      SUB AX,y1
  2659.      IMUL dp
  2660.      IDIV dq
  2661.      ADD x1,AX
  2662.      MOV y1,DI
  2663.      JMP @Punkt1
  2664.  
  2665.    @CL6:
  2666.      TEST AL,NOT CodeUnten     {Punkt unterhalb des Windows?}
  2667.      JZ @Punkt1                {nein}
  2668.      {ja, berechne:}
  2669.      { x1 := x1 + (x2 - x1) / (y2 - y1) * (WindowY2 - y1), y1 := WindowY2 }
  2670.      MOV AX,WindowY2
  2671.      PUSH AX
  2672.      SUB AX,y1
  2673.      IMUL dp
  2674.      IDIV dq
  2675.      ADD x1,AX
  2676.      POP AX
  2677.      MOV y1,AX
  2678.      JMP @Punkt1
  2679.  
  2680.    {Hier gilt: die beiden Punkte wurden auf den sichtbaren Bereich zurecht-}
  2681.    {gestutzt; sollte die Gerade keinen sichtbaren Teil besitzen, so wurde  }
  2682.    {direkt zu @LineReady verzweigt! }
  2683.    @DrawLine:
  2684.      MOV DX,x1
  2685.      SUB DX,x2
  2686.      JGE @posdx
  2687.      NEG DX
  2688.    @posdx:
  2689.      MOV AX,y1
  2690.      SUB AX,y2
  2691.      JGE @posdy
  2692.      NEG AX
  2693.    @posdy:
  2694.      {AX = neues deltaY, DX = neues deltaX}
  2695.      XOR CX,CX
  2696.      CMP AX,DX
  2697.      JBE @noswap1
  2698.      XCHG AX,DX
  2699.      INC CX
  2700.    @noswap1:
  2701.      {AX = deltaY, DX = deltaX (für CX=0), AX = deltaX, DX = deltaY (für CX=1)}
  2702.      SHL AX,1
  2703.      MOV dp,AX
  2704.      SUB AX,DX
  2705.      MOV d,AX
  2706.      SUB AX,DX
  2707.      MOV dq,AX
  2708.  
  2709.      JCXZ @then
  2710.      JMP @else
  2711.  
  2712.    @then:
  2713.      MOV CX,x2
  2714.      MOV AX,x1
  2715.      MOV DX,y1
  2716.      MOV BX,y2
  2717.      CMP AX,CX
  2718.      JBE @noswap2
  2719.      XCHG AX,CX
  2720.      XCHG DX,BX
  2721.    @noswap2:
  2722.      {AX = neues X1, CX = neues X2, DX = neues Y1, BX = neues Y2}
  2723.      SUB CX,AX
  2724.      INC CX  {CX := x2 - x1 + 1 }
  2725.      MOV SI,LINESIZE
  2726.      CMP DX,BX
  2727.      JBE @okay1
  2728.      NEG SI
  2729.    @okay1:
  2730.      {SI = ±LINESIZE, CX = #Pixel, AX = X1, DX = Y1}
  2731.      MOV DI,DX
  2732.      SHL DI,1
  2733.      MOV DI,CS:[OFFSET gadr + DI]   {DI := y1 * LINESIZE}
  2734.      MOV BL,AL
  2735.      SHR AX,1
  2736.      SHR AX,1
  2737.      ADD DI,AX       {DI := y1 * LINESIZE + (x1 DIV 4) }
  2738.  
  2739.      AND BX,3        {BX := (x1 AND 4) }
  2740.      SHL BX,1        {*2, da Worteinträge}
  2741.      ADD DI,[OFFSET BACKTab + BX]  {Maske für Zugriff holen: BX * 16000}
  2742.  
  2743.      MOV BL,BACKGNDPAGE   {BH = 0 -> BX = Zeichenseite}
  2744.      SHL BX,1
  2745.      MOV ES,[BX + OFFSET Segment_Adr -StartIndex*2] {ES:DI=Zeiger auf 1.Punkt}
  2746.  
  2747.      MOV AL,Color
  2748.      MOV DX,d
  2749.      MOV BX,dq
  2750.  
  2751.    @loop1:
  2752.      MOV ES:[DI],AL
  2753.      ADD DI,PAGESIZE
  2754.      JC @wrap1
  2755.      CMP DI,4*PAGESIZE
  2756.      JB @nowrap1
  2757.    @wrap1:
  2758.      SUB DI,4*PAGESIZE-1  {wieder in Plane0, aber 1 Byte weiter}
  2759.    @nowrap1:  {AL = Farbe, SI = ±LINESIZE, ES:DI=Zeiger auf 1.Punkt}
  2760.               {BX = dq, DX = d}
  2761.      OR DX,DX
  2762.      JGE @newline
  2763.      ADD DX,dp
  2764.      LOOP @loop1
  2765.      JMP @raus
  2766.  
  2767.    @newline:
  2768.      ADD DI,SI
  2769.      ADD DX,BX
  2770.      LOOP @loop1
  2771.      JMP @raus
  2772.  
  2773.  
  2774.    @else:
  2775.      MOV CX,y2
  2776.      MOV AX,y1
  2777.      MOV DX,x1
  2778.      MOV BX,x2
  2779.      CMP AX,CX
  2780.      JBE @noswap3
  2781.      XCHG AX,CX
  2782.      XCHG DX,BX
  2783.    @noswap3:
  2784.      {AX = neues Y1, BX = neues X2, CX = neues Y2, DX = neues X1}
  2785.      SUB CX,AX
  2786.      INC CX
  2787.      MOV SI,PAGESIZE
  2788.      CMP DX,BX
  2789.      JBE @okay2
  2790.      NEG SI
  2791.    @okay2:
  2792.      {SI = ±PAGESIZE, CX = #Pixel, DX = X1, AX = Y1}
  2793.      MOV DI,AX
  2794.      SHL DI,1
  2795.      MOV DI,CS:[OFFSET gadr + DI]   {DI := y1 * LINESIZE}
  2796.      MOV BL,DL
  2797.      SHR DX,1
  2798.      SHR DX,1
  2799.      ADD DI,DX       {DI := y1 * LINESIZE + (x1 DIV 4) }
  2800.  
  2801.      AND BX,3        {BX := (x1 AND 4) }
  2802.      SHL BX,1        {*2, da Worteinträge}
  2803.      ADD DI,[OFFSET BACKTab + BX]  {Maske für Zugriff holen: BX * 16000}
  2804.  
  2805.      MOV BL,BACKGNDPAGE   {BH = 0 -> BX = Zeichenseite}
  2806.      SHL BX,1
  2807.      MOV ES,[BX + OFFSET Segment_Adr -StartIndex*2] {ES:DI=Zeiger auf 1.Punkt}
  2808.  
  2809.      MOV AL,Color
  2810.      MOV DX,d
  2811.      MOV BX,dq
  2812.  
  2813.    @loop2:
  2814.      MOV ES:[DI],AL
  2815.      ADD DI,LINESIZE
  2816.      OR DX,DX
  2817.      JGE @newcolumn
  2818.      ADD DX,dp
  2819.      LOOP @loop2
  2820.      JMP @raus
  2821.  
  2822.    @newcolumn:
  2823.      OR SI,SI
  2824.      JGE @plus
  2825.      {Inkrement SI<0, auf underflow prüfen:}
  2826.      ADD DI,SI
  2827.      JC @nowrap2
  2828.      ADD DI,4*PAGESIZE-1
  2829.      JMP @nowrap2
  2830.    @plus:
  2831.      {Inkrement SI>0, auf overflow & >= 4*PAGESIZE prüfen}
  2832.      ADD DI,SI
  2833.      JC @wrap2
  2834.      CMP DI,4*PAGESIZE
  2835.      JB @nowrap2
  2836.    @wrap2:
  2837.      SUB DI,4*PAGESIZE-1
  2838.    @nowrap2:
  2839.      ADD DX,BX
  2840.      LOOP @loop2
  2841.      JMP @raus
  2842.  
  2843.    @raus:
  2844.    @LineReady:
  2845.   END;
  2846. END;
  2847.  
  2848. PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:WORD);
  2849. { in: x1,y1,x2,y2 = Koordinaten zweier Punkte, }
  2850. {     Color       = Farbe (0..255)             }
  2851. {     StartVirtualX,StartVirtualY = linke obere Bildschirmecke         }
  2852. {     pa          = Grafikseite, auf der gezeichnet werden soll (0..3) }
  2853. {     WinClip     = TRUE, wenn Linie auf Win* Fenster geclippt werden soll }
  2854. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = Fenster für evtl. Clipping}
  2855. {out: - }
  2856. {rem: Es wurde eine Linie von den VIRTUELLEN Punkten (x1,y1) nach (x2,y2)  }
  2857. {     in der Farbe COLOR gezeichnet; die Routine führt dabei selber die    }
  2858. {     Umrechnung der angeg. Koordinaten in absolute Bildschirmkoordinaten  }
  2859. {     sowie evtl. notwendige Clipping-Schritte aus.                        }
  2860. {     Die Linie wird NICHT automatisch in den Hintergrund übernommen,      }
  2861. {     d.h.: sie ist nur für einen Animationszyklus sichtbar (soll sie      }
  2862. {     permanent bleiben, so muß sie in den Hintergrund gezeichnet werden!)}
  2863. {     (Deshalb ist es sinnvoll, diese Routine NACH Aufruf von ANIMATE aus- }
  2864. {     zuführen, da die gezeichnete Linie sonst sofort wieder verschwindet) }
  2865. CONST CodeLinks =$7;  {%0111}
  2866.       CodeRechts=$B;  {%1011}
  2867.       CodeOben  =$D;  {%1101}
  2868.       CodeUnten =$E;  {%1110}
  2869. BEGIN
  2870.  IF (pa<0) OR (pa>SCROLLPAGE)
  2871.   THEN Error:=Err_InvalidPageNumber
  2872.  ELSE IF pa=BACKGNDPAGE
  2873.   THEN BackgroundLine(x1,y1,x2,y2)
  2874.   ELSE
  2875.   {zuerst Linie auf sichtbaren Bereich zurechtklippen; dazu Sutherland-}
  2876.   {Cohen-Algorithmus verwenden: 4 Bit-Code für links|rechts|oben|unten}
  2877.   ASM
  2878.      CLD
  2879.      XOR BX,BX
  2880.      MOV SI,XMAX
  2881.      XOR DI,DI
  2882.      MOV AX,YMAX
  2883.      CMP WinClip,FALSE
  2884.      JE @Start
  2885.      MOV BX,WinXMIN
  2886.      MOV SI,WinXMAX
  2887.      MOV DI,WinYMIN
  2888.      MOV AX,WinYMAX
  2889.    @Start:
  2890.      MOV WindowY2,AX
  2891.      {BX|SI|DI|WindowY2 = WinXMIN|WinXMAX|WinYMIN|WinYMAX bzw. 0|XMAX|0|YMAX}
  2892.  
  2893.      MOV CL,$F         {mit %1111 anfangen}
  2894.      MOV AX,x2
  2895.      SUB AX,StartVirtualX   {x2 in absolute Koordinaten umrechnen}
  2896.      MOV x2,AX
  2897.      CMP AX,BX         {x2 < WindowX1 ?}
  2898.      JL @GC1Punkt2     {ja, Flag für "Punkt links vom Fenster" belassen}
  2899.      AND CL,CodeLinks  {nein, Flag rücksetzen}
  2900.    @GC1Punkt2:
  2901.      CMP AX,SI         {x2 > WindowX2 ?}
  2902.      JG @GC2Punkt2     {ja, Flag für "Punkt rechts vom Fenster" belassen}
  2903.      AND CL,CodeRechts {nein, Flag rücksetzen}
  2904.    @GC2Punkt2:
  2905.      MOV AX,y2
  2906.      SUB AX,StartVirtualY   {y2 in absolute Koordinaten umrechnen}
  2907.      MOV y2,AX
  2908.      CMP AX,DI         {y2 < WindowY1 ?}
  2909.      JL @GC3Punkt2     {ja, Flag für "Punkt oberhalb des Fensters" bel.}
  2910.      AND CL,CodeOben   {nein, Flag rücksetzen}
  2911.    @GC3Punkt2:
  2912.      CMP AX,WindowY2   {y2 > WindowY2 ?}
  2913.      JG @GC4Punkt2     {ja, Flag für "Punkt unterhalb des Fensters" bel.}
  2914.      AND CL,CodeUnten
  2915.    @GC4Punkt2:         {CL enthält jetzt den Gebietscode für Punkt 2}
  2916.  
  2917.      MOV AX,x1
  2918.      SUB AX,StartVirtualX   {x1 in absolute Koordinaten umrechnen}
  2919.      MOV x1,AX
  2920.      MOV AX,y1
  2921.      SUB AX,StartVirtualY   {y1 in absolute Koordinaten umrechnen}
  2922.      MOV y1,AX
  2923.  
  2924.    @Punkt1:
  2925.      MOV CH,$F         {mit %1111 anfangen}
  2926.      MOV AX,x1
  2927.      CMP AX,BX         {x1 < WindowX1 ?}
  2928.      JL @GC1Punkt1     {ja, Flag für "Punkt links vom Fenster" belassen}
  2929.      AND CH,CodeLinks  {nein, Flag rücksetzen}
  2930.    @GC1Punkt1:
  2931.      CMP AX,SI         {x1 > WindowX2 ?}
  2932.      JG @GC2Punkt1     {ja, Flag für "Punkt rechts vom Fenster" belassen}
  2933.      AND CH,CodeRechts {nein, Flag rücksetzen}
  2934.    @GC2Punkt1:
  2935.      MOV AX,y1
  2936.      CMP AX,DI         {y1 < WindowY1 ?}
  2937.      JL @GC3Punkt1     {ja, Flag für "Punkt oberhalb des Fensters" bel.}
  2938.      AND CH,CodeOben   {nein, Flag rücksetzen}
  2939.    @GC3Punkt1:
  2940.      CMP AX,WindowY2   {y1 > WindowY2 ?}
  2941.      JG @GC4Punkt1     {ja, Flag für "Punkt unterhalb des Fensters" bel.}
  2942.      AND CH,CodeUnten
  2943.    @GC4Punkt1:         {CH enthält jetzt den Gebietscode für Punkt 1}
  2944.  
  2945.    {CL enthält den Gebietscode für Punkt 2, CH den für Punkt 1}
  2946.  
  2947.      MOV AX,CX
  2948.      AND AL,AH         {Code1 AND Code2 <> 0 ?}
  2949.      JNZ @LineReady    {ja, Linie ganz außerhalb des Windows}
  2950.      MOV AX,CX
  2951.      OR AL,AH          {Code1 OR Code2 = 0 ?}
  2952.      JZ @DrawLine      {ja, Linie ganz innerhalb des Windows}
  2953.  
  2954.    {Nun eigentliches Clipping vornehmen: }
  2955.      MOV AX,CX
  2956.      OR AH,AH          {Code1 =0 ?}
  2957.      JNZ @CL3          {nein, alles ok}
  2958.      MOV AX,x1         {ja, Punkte vertauschen!}
  2959.      XCHG AX,x2
  2960.      MOV x1,AX
  2961.      MOV AX,y1
  2962.      XCHG AX,y2
  2963.      MOV y1,AX
  2964.      XCHG CL,CH
  2965.    @CL3:
  2966.      MOV AX,x2
  2967.      SUB AX,x1        
  2968.      MOV dp,AX        {dp := x2 - x1}
  2969.      MOV AX,y2
  2970.      SUB AX,y1        
  2971.      MOV dq,AX        {dq := y2 - y1}
  2972.  
  2973.      MOV AL,CH        {AL := Code1}
  2974.      TEST AL,NOT CodeLinks     {Punkt1 links des Windows?}
  2975.      JZ @CL4                   {nein}
  2976.      {ja, neue Koordinaten berechnen:}
  2977.      { y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX1 - X1) }
  2978.      { und x1 := WindowX1}
  2979.      MOV AX,BX
  2980.      SUB AX,x1        {AX := WindowX1-x1}
  2981.      IMUL dq
  2982.      IDIV dp
  2983.      ADD y1,AX
  2984.      MOV x1,BX
  2985.      JMP @Punkt1
  2986.  
  2987.    @CL4:
  2988.      TEST AL,NOT CodeRechts    {Punkt1 rechts des Windows?}
  2989.      JZ @CL5                   {nein}
  2990.      {ja, berechne:}
  2991.      { y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX2 - X1), x1 := WindowX2}
  2992.      MOV AX,SI  {SI = WindowX2}
  2993.      SUB AX,x1
  2994.      IMUL dq
  2995.      IDIV dp
  2996.      ADD y1,AX
  2997.      MOV x1,SI
  2998.      JMP @Punkt1
  2999.  
  3000.    @CL5:
  3001.      TEST AL,NOT CodeOben      {Punkt1 oberhalb des Windows?}
  3002.      JZ @CL6                   {nein}
  3003.      {ja, berechne:}
  3004.      { x1 := x1 + (x2 - x1) / (y2 - y1) * (WindowY1 - y1), y1 := WindowY1 }
  3005.      MOV AX,DI  {DI = WindowY1}
  3006.      SUB AX,y1
  3007.      IMUL dp
  3008.      IDIV dq
  3009.      ADD x1,AX
  3010.      MOV y1,DI
  3011.      JMP @Punkt1
  3012.  
  3013.    @CL6:
  3014.      TEST AL,NOT CodeUnten     {Punkt unterhalb des Windows?}
  3015.      JZ @Punkt1                {nein}
  3016.      {ja, berechne:}
  3017.      { x1 := x1 + (x2 - x1) / (y2 - y1) * (WindowY2 - y1), y1 := WindowY2 }
  3018.      MOV AX,WindowY2
  3019.      PUSH AX
  3020.      SUB AX,y1
  3021.      IMUL dp
  3022.      IDIV dq
  3023.      ADD x1,AX
  3024.      POP AX
  3025.      MOV y1,AX
  3026.      JMP @Punkt1
  3027.  
  3028.    {Hier gilt: die beiden Punkte wurden auf den sichtbaren Bereich zurecht-}
  3029.    {gestutzt; sollte die Gerade keinen sichtbaren Teil besitzen, so wurde  }
  3030.    {direkt zu @LineReady verzweigt! }
  3031.    @DrawLine:
  3032.      PUSH BP
  3033.      MOV Steigung,0  {Flag zurücksetzen}
  3034.      MOV CX,x2
  3035.      SUB CX,x1       {Punkt1 rechts von Punkt2 ?}
  3036.      JGE @posDX      {nein}
  3037.      NEG CX          {ja, Punkte vertauschen}
  3038.      MOV AX,x1
  3039.      XCHG AX,x2
  3040.      MOV x1,AX
  3041.      MOV AX,y1
  3042.      XCHG AX,y2
  3043.      MOV y1,AX
  3044.  
  3045.    @posDX:
  3046.      MOV DI,y1
  3047.      SHL DI,1
  3048.      MOV DI,CS:[OFFSET gadr + DI]   {DI := y1 * LINESIZE}
  3049.      MOV AX,x1
  3050.      MOV BL,AL
  3051.      SHR AX,1
  3052.      SHR AX,1
  3053.      ADD DI,AX       {DI := y1 * LINESIZE + (x1 DIV 4) }
  3054.  
  3055.      AND BX,3        {BX := (x1 AND 4) }
  3056.      MOV DH,[OFFSET TranslateTab + BX]  {Maske für VRAM-Zugriff holen}
  3057.      MOV DL,2
  3058.  
  3059.      MOV BX,pa       {BX = Zeichenseite}
  3060.      SHL BX,1
  3061.      MOV ES,[BX +OFFSET Segment_Adr -StartIndex*2]
  3062.  
  3063.      {ES:DI=Zeiger auf Grafikadresse von Punkt1, DX=Zugriffsmaske dafür}
  3064.      MOV SI,LINESIZE
  3065.      MOV BX,y2
  3066.      SUB BX,y1       {Punkt1 unterhalb von Punkt2 ?}
  3067.      JG @posDY       {nein}
  3068.      NEG BX          {ja, deltaY und Zeileninkrement negieren}
  3069.      NEG SI
  3070.  
  3071.    @posDY:
  3072.      CMP BX,CX       {deltaY > deltaX ?}
  3073.      JLE @flach      {nein: geringe Steigung, <=1 }
  3074.      XCHG BX,CX      {ja, deltas vertauschen und Flag setzen}
  3075.      MOV Steigung,1
  3076.  
  3077.    {Jetzt Bresenham-Parameter berechnen: 2 * DY, 2 * DY - DX, 2 * (DY - DX) }
  3078.    @flach:
  3079.      SHL BX,1
  3080.      MOV DY_mal2,BX
  3081.      SUB BX,CX
  3082.      MOV BP,BX       {BP := 2 * DY - DX}
  3083.      SUB BX,CX
  3084.      MOV DY_m_DX_mal2,BX
  3085.      INC CX          {CX := Anzahl Pixel}
  3086.      MOV BL,Color
  3087.      MOV BH,1
  3088.      CMP Steigung,0  {steile Linie?}
  3089.      JNZ @high1      {ja}
  3090.  
  3091.    @low1:            {nein}
  3092.      MOV AX,3C4h
  3093.      XCHG AX,DX
  3094.      OUT DX,AX       {richtige Bitplane anwählen}
  3095.      MOV DX,AX       {Maske wieder nach DX retten}
  3096.      MOV AL,BL       {Farbe für Punkt holen}
  3097.      STOSB           {Punkt setzen}
  3098.      SHL DH,1        {Maske für nächsten Punkt berechnen     }
  3099.      CMP DH,16       {noch mit derselben Adresse ansprechbar?}
  3100.      JE @nextbyte1   {nein, Adr. muß(te) um 1 erhöht werden  }
  3101.      DEC DI          {ja, Erhöhung von DI rückgängig machen  }
  3102.    @low1b:
  3103.      OR BP,BP
  3104.      JGE @low2
  3105.      ADD BP,DY_mal2
  3106.      LOOP @low1
  3107.      JMP @raus
  3108.    @nextbyte1:
  3109.      MOV DH,BH       {Maske auf 1 zurücksetzen}
  3110.      JMP @low1b      {Rest wie gehabt}
  3111.  
  3112.    @low2:
  3113.      ADD BP,DY_m_DX_mal2
  3114.      ADD DI,SI
  3115.      LOOP @low1
  3116.      JMP @raus
  3117.  
  3118.  
  3119.    @high1:
  3120.      MOV AX,3C4h
  3121.      XCHG AX,DX
  3122.      OUT DX,AX
  3123.      MOV DX,AX
  3124.      MOV AL,BL
  3125.    @high1b:
  3126.      OR BP,BP
  3127.      JGE @high2
  3128.      ADD BP,DY_mal2
  3129.      MOV ES:[DI],AL
  3130.      ADD DI,SI
  3131.      LOOP @high1b
  3132.      JMP @raus
  3133.  
  3134.    @high2:
  3135.      ADD BP,DY_m_DX_mal2
  3136.      SHL DH,1
  3137.      CMP DH,16
  3138.      JE @nextbyte2
  3139.      MOV ES:[DI],AL
  3140.      ADD DI,SI
  3141.      LOOP @high1
  3142.      JMP @raus
  3143.    @nextbyte2:
  3144.      MOV DH,BH
  3145.      STOSB
  3146.      ADD DI,SI
  3147.      LOOP @high1
  3148.  
  3149.    @raus:
  3150.      POP BP
  3151.    @LineReady:
  3152.   END;
  3153. END;
  3154.  
  3155. FUNCTION GetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
  3156. { in: x,y    = VIRTUELLE Punktkoordinaten des auszulesenden Punktes}
  3157. {     PAGEADR= Grafikseite(nsegment), aus der gelesen werden soll  }
  3158. {     StartVirtualX, StartVirtualY = linke obere Bildschirmecke    }
  3159. {out: Farbe des Punktes}
  3160. {rem: Liegt der Punkt außerhalb des sichtbaren Bereichs, so wird   }
  3161. {     "0" als Ergebniswert zurückgeliefert}
  3162. {     Achtung! Da PAGEADR immer die nichtsichtbare Grafikseite     }
  3163. {     bezeichnet, liest diese Routine auch von dort die Punkte ein!}
  3164. ASM
  3165.  XOR AL,AL              {AL mit 0 vorbesetzen}
  3166.  MOV DI,y
  3167.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3168.  JS @offscrn
  3169.  CMP DI,YMAX
  3170.  JG @offscrn
  3171.  MOV BX,x
  3172.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3173.  JS @offscrn
  3174.  CMP BX,XMAX
  3175.  JG @offscrn
  3176.  SHL DI,1
  3177.  MOV DI,CS:[OFFSET gadr + DI]
  3178.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3179.  MOV AX,BX
  3180.  SHR AX,1
  3181.  SHR AX,1
  3182.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3183.  AND BL,3     {BL = X MOD 4 = Leseplane}
  3184.  MOV AL,4
  3185.  MOV AH,BL
  3186.  MOV DX,3CEh
  3187.  
  3188.  MOV ES,PAGEADR
  3189.  CLI
  3190.  OUT DX,AX
  3191.  MOV AL,ES:[DI]
  3192.  STI
  3193. @offscrn:
  3194. END;
  3195.  
  3196.  
  3197. FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
  3198. { in: x,y   = VIRTUELLE Punktkoordinaten des auszulesenden Punktes}
  3199. {     StartVirtualX, StartVirtualY = linke obere Bildschirmecke   }
  3200. {out: Farbe des Punktes der Hintergrundseite}
  3201. {rem: Liegt der Punkt außerhalb des sichtbaren Bereichs, so wird  }
  3202. {     "0" als Ergebniswert zurückgeliefert}
  3203. {     Da als Hintergrundseite BACKGNDADR verwendet wird, ist die  }
  3204. {     Routine nur für den Hintergrundmodus STATIC sinnvoll!       }
  3205. {     Falls EMS verwendet wird, so muß die aufrufende Routine     }
  3206. {     sicherstellen, daß im EMS-Pageframe die benötigten Daten    }
  3207. {     stehen (per "IF EMSused THEN EnsureEMSConsistency()" Aufruf)}
  3208. ASM
  3209.  XOR AL,AL              {AL mit 0 vorbesetzen}
  3210.  MOV DI,y
  3211.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3212.  JS @offscrn
  3213.  CMP DI,YMAX
  3214.  JG @offscrn
  3215.  MOV BX,x
  3216.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3217.  JS @offscrn
  3218.  CMP BX,XMAX
  3219.  JG @offscrn
  3220.  SHL DI,1
  3221.  MOV DI,CS:[OFFSET gadr + DI]
  3222.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3223.  MOV AX,BX
  3224.  SHR AX,1
  3225.  SHR AX,1
  3226.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3227.  AND BX,3     {BX := (x1 AND 4) }
  3228.  SHL BX,1     {*2, da Worteinträge}
  3229.  ADD DI,[OFFSET BACKTab + BX]  {Maske für Zugriff holen: BX * 16000}
  3230.  MOV ES,BACKGNDADR
  3231.  MOV AL,ES:[DI]
  3232. @offscrn:
  3233. END;
  3234.  
  3235. FUNCTION PageGetPixel(x,y:INTEGER; pa:WORD):BYTE; ASSEMBLER;
  3236. { in: x,y   = VIRTUELLE Punktkoordinaten des auszulesenden Punktes}
  3237. {     pa    = Grafikseite (0..3), von der der Punkt ausgelesen    }
  3238. {             werden soll}
  3239. {     StartVirtualX, StartVirtualY = linke obere Bildschirmecke   }
  3240. {out: Farbe des Punktes der Hintergrundseite}
  3241. {rem: Liegt der Punkt außerhalb des sichtbaren Bereichs, so wird  }
  3242. {     "0" als Ergebniswert zurückgeliefert}
  3243. {     Soll von der gerade SICHTBAREN Seite gelesen werden, so ist }
  3244. {     beim Aufruf als Seite "1-PAGE" anzugeben!                   }
  3245. {     Sinnvolle Werte für "pa" sind nur 0 und 1 (und evtl. BACK-  }
  3246. {     GNDPAGE, wenn der Hintergrundmodus STATIC benutzt wird), es }
  3247. {     findet jedoch keine Überprüfung statt!}
  3248. ASM
  3249.  CMP pa,BACKGNDPAGE
  3250.  JNE @doit
  3251.  PUSH x
  3252.  PUSH y
  3253.  CALL BackgroundGetPixel
  3254.  JMP @offscrn
  3255.  
  3256. @doit:
  3257.  XOR AL,AL              {AL mit 0 vorbesetzen}
  3258.  MOV DI,y
  3259.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3260.  JS @offscrn
  3261.  CMP DI,YMAX
  3262.  JG @offscrn
  3263.  MOV BX,x
  3264.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3265.  JS @offscrn
  3266.  CMP BX,XMAX
  3267.  JG @offscrn
  3268.  SHL DI,1
  3269.  MOV DI,CS:[OFFSET gadr + DI]
  3270.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3271.  MOV AX,BX
  3272.  SHR AX,1
  3273.  SHR AX,1
  3274.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3275.  AND BX,3     {BL = X MOD 4 = Leseplane; BH = 0}
  3276.  MOV AL,4
  3277.  MOV AH,BL
  3278.  MOV BX,pa    {BX = Grafikseite}
  3279.  AND BX,3     {nur Seiten 0..3}
  3280.  SHL BX,1
  3281.  MOV ES,[BX +OFFSET Segment_Adr-StartIndex*2]
  3282.  
  3283.  CLI
  3284.  MOV DX,3CEh
  3285.  OUT DX,AX
  3286.  MOV AL,ES:[DI]
  3287.  STI
  3288. @offscrn:
  3289. END;
  3290.  
  3291.  
  3292. PROCEDURE PutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
  3293. { in: x,y    = VIRTUELLE Punktkoordinaten des zu zeichnenden Punktes}
  3294. {     color  = Farbwert für den zu zeichnenden Punkt}
  3295. {     1-PAGE = Grafikseite, auf der gezeichnet werden soll}
  3296. {     StartVirtualX, StartVirtualY = linke obere Bildschirmecke}
  3297. {     WinClip= TRUE, wenn Linie auf Win* Fenster geclippt werden soll}
  3298. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = Fenster für evtl. Clipping}
  3299. {out: - }
  3300. {rem: Der Punkt (x,y) wurde in absolute Bildschirmkoordinaten umgerechnet  }
  3301. {     und gezeichnet (sofern er auf dem sichtbaren Bildschirmfenster liegt)}
  3302. {     Der Punkt wird NICHT automatisch in den Hintergrundspeicher über-    }
  3303. {     nommen, d.h.: er ist nur einen Animationszyklus lang sichtbar!       }
  3304. {     (Deshalb ist es sinnvoll, diese Routine NACH Aufruf von ANIMATE aus- }
  3305. {     zuführen, da der gezeichnete Punkt sonst sofort wieder verschwindet) }
  3306. ASM
  3307.  CMP WinClip,TRUE
  3308.  JE @goClip
  3309.  
  3310.  MOV DI,y
  3311.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3312.  JS @offscrn
  3313.  CMP DI,YMAX
  3314.  JG @offscrn
  3315.  MOV BX,x
  3316.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3317.  JS @offscrn
  3318.  CMP BX,XMAX
  3319.  JG @offscrn
  3320.  SHL DI,1
  3321.  MOV DI,CS:[OFFSET gadr + DI]
  3322.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3323.  MOV AX,BX
  3324.  SHR AX,1
  3325.  SHR AX,1
  3326.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3327.  AND BX,3
  3328.  MOV AH,[OFFSET TranslateTab + BX]
  3329.  MOV AL,2
  3330.  MOV DX,3C4h
  3331.  
  3332.  MOV BX,1     {ES := Segment_Adr[1-PAGE], denn 1-PAGE = sichtbare Seite}
  3333.  SUB BX,PAGE
  3334.  SHL BX,1
  3335.  MOV ES,[BX +OFFSET Segment_Adr-StartIndex*2]
  3336.  
  3337.  CLI
  3338.  OUT DX,AX
  3339.  MOV AL,color
  3340.  MOV ES:[DI],AL  {ab 386 schneller als STOSB!}
  3341.  STI
  3342.  JMP @ende
  3343.  
  3344. @goClip: {hierher, wenn Clipping auf Fenster}
  3345.  MOV DI,y
  3346.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3347.  CMP DI,WinYMIN
  3348.  JL @offscrn
  3349.  CMP DI,WinYMAX
  3350.  JG @offscrn
  3351.  MOV BX,x
  3352.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3353.  CMP BX,WinXMIN
  3354.  JL @offscrn
  3355.  CMP BX,WinXMAX
  3356.  JG @offscrn
  3357.  SHL DI,1
  3358.  MOV DI,CS:[OFFSET gadr + DI]
  3359.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3360.  MOV AX,BX
  3361.  SHR AX,1
  3362.  SHR AX,1
  3363.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3364.  AND BX,3
  3365.  MOV AH,[OFFSET TranslateTab + BX]
  3366.  MOV AL,2
  3367.  MOV DX,3C4h
  3368.  
  3369.  MOV BX,1     {ES := Segment_Adr[1-PAGE], denn 1-PAGE = sichtbare Seite}
  3370.  SUB BX,PAGE
  3371.  SHL BX,1
  3372.  MOV ES,[BX +OFFSET Segment_Adr-StartIndex*2]
  3373.  
  3374.  CLI
  3375.  OUT DX,AX
  3376.  MOV AL,color
  3377.  MOV ES:[DI],AL  {ab 386 schneller als STOSB!}
  3378.  STI
  3379. @offscrn:
  3380. @ende:
  3381. END;
  3382.  
  3383. PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
  3384. { in: x,y   = VIRTUELLE Punktkoordinaten des zu zeichnenden Punktes}
  3385. {     color = Farbwert für den zu zeichnenden Punkt}
  3386. {     StartVirtualX, StartVirtualY = linke obere Bildschirmecke}
  3387. {     WinClip=TRUE, wenn Linie auf Win* Fenster geclippt werden soll}
  3388. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = Fenster für evtl. Clipping}
  3389. {out: - }
  3390. {rem: Der Punkt (x,y) wurde in absolute Bildschirmkoordinaten umgerechnet und}
  3391. {     in den Hintergrund gezeichnet (sofern er im sichtbaren Bereich liegt)  }
  3392. {     Der Punkt wird NICHT sofort sichtbar, sondern erst nach einem Anima-   }
  3393. {     tionszyklus (dann aber permanent) (Deshalb ist es sinnvoll, diese Rou- }
  3394. {     tine VOR dem Aufruf von ANIMATE auszuführen, so daß evtl. Änderungen   }
  3395. {     des Hintergrundes "sofort" sichtbar werden!)                           }
  3396. {     Da als Hintergrundseite BACKGNDADR verwendet wird, ist die Verwendung  }
  3397. {     der Routine nur für den Hintergrundmodus STATIC sinnvoll!}
  3398. {     Falls EMS verwendet wird, so muß die aufrufende Routine     }
  3399. {     sicherstellen, daß im EMS-Pageframe die benötigten Daten    }
  3400. {     stehen (per "IF EMSused THEN EnsureEMSConsistency()" Aufruf)}
  3401. ASM
  3402.  CMP WinClip,TRUE
  3403.  JE @goClip
  3404.  
  3405.  MOV DI,y
  3406.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3407.  JS @offscrn
  3408.  CMP DI,YMAX
  3409.  JG @offscrn
  3410.  MOV BX,x
  3411.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3412.  JS @offscrn
  3413.  CMP BX,XMAX
  3414.  JG @offscrn
  3415.  SHL DI,1
  3416.  MOV DI,CS:[OFFSET gadr + DI]
  3417.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3418.  MOV AX,BX
  3419.  SHR AX,1
  3420.  SHR AX,1
  3421.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3422.  AND BX,3     {BX := (x1 AND 4) }
  3423.  SHL BX,1     {*2, da Worteinträge}
  3424.  ADD DI,[OFFSET BACKTab + BX]  {Maske für Zugriff holen: BX * 16000}
  3425.  MOV ES,BACKGNDADR
  3426.  MOV AL,color
  3427.  MOV ES:[DI],AL  {ab 386 schneller als STOSB!}
  3428.  JMP @ende
  3429.  
  3430. @goClip: {hierher, wenn Clipping auf Fenster}
  3431.  MOV DI,y
  3432.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3433.  CMP DI,WinYMIN
  3434.  JL @offscrn
  3435.  CMP DI,WinYMAX
  3436.  JG @offscrn
  3437.  MOV BX,x
  3438.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3439.  CMP BX,WinXMIN
  3440.  JL @offscrn
  3441.  CMP BX,WinXMAX
  3442.  JG @offscrn
  3443.  SHL DI,1
  3444.  MOV DI,CS:[OFFSET gadr + DI]
  3445.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3446.  MOV AX,BX
  3447.  SHR AX,1
  3448.  SHR AX,1
  3449.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3450.  AND BX,3     {BX := (x1 AND 4) }
  3451.  SHL BX,1     {*2, da Worteinträge}
  3452.  ADD DI,[OFFSET BACKTab + BX]  {Maske für Zugriff holen: BX * 16000}
  3453.  MOV ES,BACKGNDADR
  3454.  MOV AL,color
  3455.  MOV ES:[DI],AL  {ab 386 schneller als STOSB!}
  3456. @offscrn:
  3457. @ende:
  3458. END;
  3459.  
  3460. PROCEDURE PagePutPixel(x,y:INTEGER; color:BYTE; pa:WORD); ASSEMBLER;
  3461. { in: x,y    = VIRTUELLE Punktkoordinaten des zu zeichnenden Punktes}
  3462. {     color  = Farbwert für den zu zeichnenden Punkt}
  3463. {     pa     = Grafikseite (0..3), auf der gezeichnet werden soll   }
  3464. {     PAGEADR= Grafikseite(nsegment), auf der gezeichnet werden soll}
  3465. {     StartVirtualX, StartVirtualY = linke obere Bildschirmecke}
  3466. {     WinClip= TRUE, wenn Linie auf Win* Fenster geclippt werden soll}
  3467. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = Fenster für evtl. Clipping}
  3468. {out: - }
  3469. {rem: Der Punkt (x,y) wurde in absolute Bildschirmkoordinaten umgerechnet  }
  3470. {     und gezeichnet (sofern er auf dem sichtbaren Bildschirmfenster liegt)}
  3471. {     Soll auf die gerade SICHTBARE Seite gezeichnet werden, so ist}
  3472. {     beim Aufruf als Seite "1-PAGE" anzugeben!                    }
  3473. {     Auch hier gilt, daß der gezeichnete Punkt NICHT automatisch  }
  3474. {     in den Hintergrundspeicher übernommen wird, d.h.: er ist nur }
  3475. {     bis zum nächsten Animationszyklus (= Aufruf von ANIMATE)     }
  3476. {     sichtbar! (Deshalb ist es sinnvoll, diese Routine NACH Aufruf}
  3477. {     von ANIMATE auszuführen, da der gezeichnete Punkt sonst so-  }
  3478. {     fort wieder verschwindet!)                                   }
  3479. {     Sinnvolle Werte für "pa" sind nur 0 und 1 (und evtl. BACK-   }
  3480. {     GNDPAGE, wenn der Hintergrundmodus STATIC benutzt wird), es  }
  3481. {     findet jedoch keine Überprüfung statt!}
  3482. ASM
  3483.  CMP pa,BACKGNDPAGE
  3484.  JNE @doit
  3485.  PUSH x
  3486.  PUSH y
  3487.  PUSH WORD PTR color
  3488.  CALL BackgroundPutPixel
  3489.  JMP @offscrn
  3490.  
  3491. @doit:
  3492.  CMP WinClip,TRUE
  3493.  JE @goClip
  3494.  
  3495.  MOV DI,y
  3496.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3497.  JS @offscrn
  3498.  CMP DI,YMAX
  3499.  JG @offscrn
  3500.  MOV BX,x
  3501.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3502.  JS @offscrn
  3503.  CMP BX,XMAX
  3504.  JG @offscrn
  3505.  SHL DI,1
  3506.  MOV DI,CS:[OFFSET gadr + DI]
  3507.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3508.  MOV AX,BX
  3509.  SHR AX,1
  3510.  SHR AX,1
  3511.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3512.  AND BX,3
  3513.  MOV AH,[OFFSET TranslateTab + BX]
  3514.  MOV AL,2
  3515.  MOV DX,3C4h
  3516.  MOV BX,pa    {BX = Grafikseite}
  3517.  SHL BX,1
  3518.  MOV ES,[BX +OFFSET Segment_Adr+StartIndex*2]
  3519.  
  3520.  CLI
  3521.  OUT DX,AX
  3522.  MOV AL,color
  3523.  MOV ES:[DI],AL  {ab 386 schneller als STOSB!}
  3524.  STI
  3525.  JMP @ende
  3526.  
  3527. @goClip: {hierher, wenn Clipping auf Fenster}
  3528.  MOV DI,y
  3529.  SUB DI,StartVirtualY   {y in absolute Koordinaten umrechnen}
  3530.  CMP DI,WinYMIN
  3531.  JL @offscrn
  3532.  CMP DI,WinYMAX
  3533.  JG @offscrn
  3534.  MOV BX,x
  3535.  SUB BX,StartVirtualX   {x in absolute Koordinaten umrechnen}
  3536.  CMP BX,WinXMIN
  3537.  JL @offscrn
  3538.  CMP BX,WinXMAX
  3539.  JG @offscrn
  3540.  SHL DI,1
  3541.  MOV DI,CS:[OFFSET gadr + DI]
  3542.  {DI = Y * LINESIZE, BX = X, Koordinaten zulässig}
  3543.  MOV AX,BX
  3544.  SHR AX,1
  3545.  SHR AX,1
  3546.  ADD DI,AX    {DI = Y * LINESIZE + (X SHR 2) }
  3547.  AND BX,3
  3548.  MOV AH,[OFFSET TranslateTab + BX]
  3549.  MOV AL,2
  3550.  MOV DX,3C4h
  3551.  MOV BX,pa    {BX = Grafikseite}
  3552.  SHL BX,1
  3553.  MOV ES,[BX +OFFSET Segment_Adr+StartIndex*2]
  3554.  
  3555.  CLI
  3556.  OUT DX,AX
  3557.  MOV AL,color
  3558.  MOV ES:[DI],AL  {ab 386 schneller als STOSB!}
  3559.  STI
  3560. @offscrn:
  3561. @ende:
  3562. END;
  3563.  
  3564. PROCEDURE LoadFont(s:STRING);
  3565. { in: s = Name der zu ladenden Fontdatei, '' für: interner Font}
  3566. {     FontType = Typ des aktuellen Fonts}
  3567. {out: CurrentFont=Zeiger auf neuen Font }
  3568. {     FontType = Typ des geladenen Fonts}
  3569. {     FontHeight=dessen Höhe in Zeilen  }
  3570. {     FontWidth =dessen Breite in Pixeln}
  3571. {     FontProportion = TagProportional, falls Proportionalschrift}
  3572. {     FontWidthTable[] = Fontweite für jeden Buchstaben}
  3573. {rem: Da initial "ResetToInternalFont" aufgerufen wurde, hat FontType}
  3574. {     bei Aufruf dieser Routine _immer_ einen definierten Wert!      }
  3575.  
  3576.     PROCEDURE ResetToInternalFont;
  3577.     VAR i,j:BYTE;
  3578.     BEGIN
  3579.      IF CurrentFont<>NIL  {allererster Aufruf?}
  3580.       THEN BEGIN {nein!}
  3581.             IF FontType=TagMonoFont
  3582.              THEN FreeMem(CurrentFont,SizeOf(MonoFont))
  3583.             ELSE IF FontType=TagColorFont
  3584.              THEN FreeMem(CurrentFont,SizeOf(ColorFont))
  3585.            END;
  3586.      IF MaxAvail<SizeOf(MonoFont)
  3587.       THEN BEGIN {nicht mal genug Speicher für internen Font!}
  3588.             Error:=Err_NotEnoughMemory;
  3589.             exit
  3590.            END;
  3591.      GetMem(CurrentFont,SizeOf(MonoFont));
  3592.      FontType:=TagMonoFont;
  3593.      FontWidth:=6;
  3594.      FontHeight:=6;
  3595.      FontProportion:=0;
  3596.      FillChar(FontWidthTable,SizeOf(FontWidthTable),FontWidth);
  3597.      FOR i:=0 TO 255 DO
  3598.       FOR j:=0 TO FontHeight-1 DO
  3599.        MonoFont(CurrentFont^)[i][j]:=internalFont[i][j]
  3600.     END;
  3601.  
  3602. VAR f:FileOfByte;
  3603. CONST Tag:STRING='FNT';
  3604. VAR Header:STRING[3];
  3605.     size,i,j:WORD;
  3606.     newFontWidth,newFontHeight,newFontType,newFontProp:BYTE;
  3607.     tempName:STRING;
  3608. BEGIN
  3609.  IF s=''
  3610.   THEN BEGIN {umschalten auf internen Font}
  3611.         ResetToInternalFont;
  3612.         exit
  3613.        END;
  3614.  
  3615.  tempName:=FindFile(s);
  3616.  IF tempName<>'' THEN s:=tempName;
  3617.  
  3618.  {Fontdatei öffnen und Header auslesen:}
  3619.  _Assign(f,s);
  3620.  {$I-}
  3621.  _Reset(f);
  3622.  Header[0]:=CHAR(Length(Tag));
  3623.  _BlockRead(f,Header[1],Length(Tag));
  3624.  _BlockRead(f,newFontWidth,1);
  3625.  _BlockRead(f,newFontHeight,1);
  3626.  _BlockRead(f,newFontType,1);
  3627.  {$IFDEF IOcheck} {$I+} {$ENDIF}
  3628.  
  3629.  IF (IOresult<>0) OR (CompressError<>CompressErr_NoError)
  3630.   THEN BEGIN
  3631.         {$I-}
  3632.         _Close(f);
  3633.         {$IFDEF IOcheck} {$I+} {$ENDIF}
  3634.         Error:=Err_FileIO;
  3635.         CompressError:=CompressErr_NoError;
  3636.         exit;
  3637.        END;
  3638.  
  3639.  newFontProp:=newFontType AND TagProportional; {<>0, falls proportional}
  3640.  newFontType:=newFontType AND Pred(TagProportional);
  3641.  
  3642.  size:=Length(Tag)+3; {Länge des Headers}
  3643.  IF newFontType=TagMonoFont
  3644.   THEN inc(size,((newFontWidth+7) SHR 3)*newFontHeight SHL 8)
  3645.   ELSE inc(size,newFontWidth*newFontHeight SHL 8); {256 Zeichen}
  3646.  
  3647.  IF newFontProp=TagProportional THEN inc(size,256); {Fontweiten}
  3648.  
  3649.  IF (Header<>Tag) OR
  3650.     ( (newFontType<>TagMonoFont) AND (newFontType<>TagColorFont) ) OR
  3651.     (newFontWidth>MaxFontWidth) OR
  3652.     (newFontHeight>MaxFontHeight) OR
  3653.     (_FileSize(f)<>size)
  3654.   THEN BEGIN {kein FONT-File}
  3655.         Error:=Err_NoFont;
  3656.         {$I-}
  3657.         _Close(f);
  3658.         {$IFDEF IOcheck} {$I+} {$ENDIF}
  3659.         CompressError:=CompressErr_NoError;
  3660.         exit
  3661.        END;
  3662.  
  3663.  IF newFontType=TagMonoFont
  3664.   THEN size:=SizeOf(MonoFont)
  3665.   ELSE size:=SizeOf(ColorFont);
  3666.  
  3667.  {Nun alten Speicher freigeben und neuen besorgen:}
  3668.  IF FontType<>newFontType
  3669.   THEN BEGIN {nur nötig, wenn alter und neuer Font verschieden sind}
  3670.         IF FontType=TagMonoFont THEN FreeMem(CurrentFont,SizeOf(MonoFont))
  3671.         ELSE {FontType=TagColorFont} FreeMem(CurrentFont,SizeOf(ColorFont));
  3672.         IF MaxAvail<size
  3673.          THEN BEGIN {nicht genug Speicher}
  3674.                Error:=Err_NotEnoughMemory;
  3675.                ResetToInternalFont;
  3676.                {$I-}
  3677.                _Close(f);
  3678.                {$IFDEF IOcheck} {$I+} {$ENDIF}
  3679.                exit
  3680.               END;
  3681.         GetMem(CurrentFont,size)
  3682.        END;
  3683.  
  3684.  FontWidth     :=newFontWidth;
  3685.  FontHeight    :=newFontHeight;
  3686.  FontType      :=newFontType;
  3687.  FontProportion:=newFontProp;
  3688.  
  3689.  IF FontType=TagMonoFont
  3690.   THEN BEGIN
  3691.         FOR i:=0 TO 255 DO
  3692.          BEGIN
  3693.           Fillchar(MonoFont (CurrentFont^)[i],SizeOf(MonoFontChar),0);
  3694.           FOR j:=0 TO FontHeight-1 DO
  3695.            {$I-}
  3696.            _BlockRead(f,MonoFont(CurrentFont^)[i][j],(FontWidth+7) SHR 3);
  3697.            {$IFDEF IOcheck} {$I+} {$ENDIF}
  3698.          END;
  3699.        END
  3700.   ELSE FOR i:=0 TO 255 DO
  3701.         FOR j:=0 TO FontHeight-1 DO
  3702.          {$I-}
  3703.          _BlockRead(f,ColorFont(CurrentFont^)[i][j],FontWidth);
  3704.          {$IFDEF IOcheck} {$I+} {$ENDIF}
  3705.  {$I-}
  3706.  IF FontProportion=TagProportional
  3707.   THEN _BlockRead(F,FontWidthTable,SizeOf(FontWidthTable))
  3708.   ELSE FillChar(FontWidthTable,SizeOf(FontWidthTable),FontWidth);
  3709.  _Close(f);
  3710.  {$IFDEF IOcheck} {$I+} {$ENDIF}
  3711.  CompressError:=CompressErr_NoError;  {evtl. Compress-Fehler zurücksetzen}
  3712. END;
  3713.  
  3714. FUNCTION OutTextLength(s:STRING):WORD;
  3715. { in: s = Textstring}
  3716. {     Font* = Fontbeschreibungsdaten}
  3717. {out: Breite des auszugebenden Textes in _Pixeln_; hierbei werden}
  3718. {     Proportionalfonts korrekt behandelt}
  3719. VAR i:BYTE;
  3720.     temp:WORD;
  3721. BEGIN
  3722.  IF FontProportion=TagProportional
  3723.   THEN BEGIN
  3724.         temp:=0;
  3725.         FOR i:=1 TO length(s) DO inc(temp,FontWidthTable[BYTE(s[i])]);
  3726.        END
  3727.   ELSE temp:=FontWidth*length(s);
  3728.  OutTextLength:=temp
  3729. END;
  3730.  
  3731. PROCEDURE OutTextXY(x,y:INTEGER; pa:WORD; s:STRING);
  3732. { in: (x,y)  = (virtuelle) Startkoordinaten des auszugebenden Textes}
  3733. {     s      = auszugebender Textstring                             }
  3734. {     pa     = Grafikseite, auf der der Text ausgegeben werden soll }
  3735. {     GraphTextColor=Textfarbe                                      }
  3736. {     GraphTextBackground=Farbe für Texthintergrund;ist dieser Wert }
  3737. {            =GraphTextColor, so werden nur die Textpixel gezeichnet}
  3738. {             und die umgebenden Pixel unverändert gelassen (=nor-  }
  3739. {             males Verhalten von TurboPascal's OutText-Routinen!)  }
  3740. {     GraphTextOrientation="vertical" oder "horizontal"             }
  3741. {     StartVirtualX,StartVirtualY = linke obere Bildschirmecke      }
  3742. {     Font* = Fontbeschreibungsdaten}
  3743. {out: Text wurde auf dem Bildschirm ausgegeben                      }
  3744. VAR offs,z,bit,i,CharWidth:BYTE;
  3745.     data1:MonoFontchar;
  3746.     data2:ColorFontChar;
  3747.     b:WORD;
  3748. BEGIN
  3749.  IF (pa<0) OR (pa>SCROLLPAGE)
  3750.   THEN BEGIN
  3751.         Error:=Err_InvalidPageNumber;
  3752.         exit
  3753.        END;
  3754.  IF (pa=BACKGNDPAGE) AND EMSused
  3755.   THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  3756.  offs:=MaxFontWidth-FontWidth;
  3757.  IF (FontType=TagMonoFont)
  3758.   THEN FOR i:=1 TO Length(s) DO
  3759.         BEGIN
  3760.          data1:=MonoFont(CurrentFont^)[BYTE(s[i])];
  3761.          CharWidth:=FontWidthTable[BYTE(s[i])];
  3762.          FOR z:=0 TO FontHeight-1 DO
  3763.           BEGIN
  3764.            b:=WORD(data1[z]);
  3765.            FOR bit:=0 TO CharWidth-1 DO
  3766.             IF b and FontMask[bit+offs]<>0
  3767.              THEN PagePutPixel(x+bit,y+z,GraphTextColor,pa)
  3768.              ELSE IF (GraphTextColor<>GraphTextBackground)
  3769.                    THEN PagePutPixel(x+bit,y+z,GraphTextBackground,pa);
  3770.           END;
  3771.          IF GraphTextOrientation=horizontal
  3772.           THEN INC(x,CharWidth)
  3773.           ELSE INC(y,FontHeight);
  3774.         END
  3775.   ELSE FOR i:=1 TO Length(s) DO
  3776.         BEGIN
  3777.          data2:=ColorFont(CurrentFont^)[BYTE(s[i])];
  3778.          CharWidth:=FontWidthTable[BYTE(s[i])];
  3779.          FOR z:=0 TO FontHeight-1 DO
  3780.           FOR bit:=0 TO CharWidth-1 DO
  3781.            BEGIN
  3782.             b:=data2[z][bit];
  3783.             IF b<>0 THEN PagePutPixel(x+bit,y+z,b,pa)
  3784.             ELSE IF (GraphTextColor<>GraphTextBackground)
  3785.                    THEN PagePutPixel(x+bit,y+z,GraphTextBackground,pa);
  3786.            END;
  3787.          IF GraphTextOrientation=horizontal
  3788.           THEN INC(x,CharWidth)
  3789.           ELSE INC(y,FontHeight);
  3790.         END
  3791. END;
  3792.  
  3793. PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
  3794. {rem: Wie OutTextXY(), aber es wird in den Hintergrund geschrieben und}
  3795. {     nicht in die durch PAGEADR spezifizierte Seite!                 }
  3796. {     Da als Hintergrundseite BACKGNDADR verwendet wird, ist die      }
  3797. {     Routine nur für den Hintergrundmodus STATIC sinnvoll!}
  3798. VAR offs,z,bit,i,CharWidth:BYTE;
  3799.     data1:MonoFontchar;
  3800.     data2:ColorFontChar;
  3801.     b:WORD;
  3802. BEGIN
  3803.  IF EMSused
  3804.   THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  3805.  offs:=MaxFontWidth-FontWidth;
  3806.  IF (FontType=TagMonoFont)
  3807.   THEN FOR i:=1 TO Length(s) DO
  3808.         BEGIN
  3809.          data1:=MonoFont(CurrentFont^)[BYTE(s[i])];
  3810.          CharWidth:=FontWidthTable[BYTE(s[i])];
  3811.          FOR z:=0 TO FontHeight-1 DO
  3812.           BEGIN
  3813.            b:=WORD(data1[z]);
  3814.            FOR bit:=0 TO CharWidth-1 DO
  3815.             IF b and FontMask[bit+offs]<>0
  3816.              THEN BackgroundPutPixel(x+bit,y+z,GraphTextColor)
  3817.              ELSE IF (GraphTextColor<>GraphTextBackground)
  3818.                    THEN BackgroundPutPixel(x+bit,y+z,GraphTextBackground);
  3819.           END;
  3820.          IF GraphTextOrientation=horizontal
  3821.           THEN INC(x,CharWidth)
  3822.           ELSE INC(y,FontHeight);
  3823.         END
  3824.   ELSE FOR i:=1 TO Length(s) DO
  3825.         BEGIN
  3826.          data2:=ColorFont(CurrentFont^)[BYTE(s[i])];
  3827.          CharWidth:=FontWidthTable[BYTE(s[i])];
  3828.          FOR z:=0 TO FontHeight-1 DO
  3829.           FOR bit:=0 TO CharWidth-1 DO
  3830.            BEGIN
  3831.             b:=data2[z][bit];
  3832.             IF b<>0 THEN BackgroundPutPixel(x+bit,y+z,b)
  3833.             ELSE IF (GraphTextColor<>GraphTextBackground)
  3834.                    THEN BackgroundPutPixel(x+bit,y+z,GraphTextBackground);
  3835.            END;
  3836.          IF GraphTextOrientation=horizontal
  3837.           THEN INC(x,CharWidth)
  3838.           ELSE INC(y,FontHeight);
  3839.         END
  3840. END;
  3841.  
  3842. PROCEDURE MakeTextSprite(s:STRING; nr:WORD);
  3843. { in: s  = in ein Sprite umzuwandelnder Text}
  3844. {     nr = SpriteLADEnummer für das zu generierende Sprite}
  3845. {     Font*, CurrentFont = aktueller Font}
  3846. {     GraphTextOrientation = Fontausrichtung}
  3847. {out: Sprite?[nr] wurde so definiert, daß es den Textinhalt von "s" als}
  3848. {     Sprite enthält}
  3849. {rem: Die Routine verhält sich wie LoadSprite()}
  3850. CONST DefaultHeader:SpriteHeader=
  3851.        (Zeiger_auf_Plane:(0,0,0,0);
  3852.         Breite_in_4er_Gruppen:0;
  3853.         Hoehe_in_Zeilen:0;
  3854.         Translate:(1,2,4,8);
  3855.         SpriteLength:0;
  3856.         Dummy:(0,0,0,0,0,0,0,0,0,0);
  3857.         Kennung:'KR';
  3858.         Version:1;
  3859.         Modus:Display_NORMAL;
  3860.         ZeigerL:0;
  3861.         ZeigerR:0;
  3862.         ZeigerO:0;
  3863.         ZeigerU:0
  3864.        );
  3865. VAR header:SpriteHeader;
  3866.     p1,p2:POINTER;
  3867.     segm,offs,b:WORD;
  3868.     x,y,i:INTEGER;
  3869.     z,wert,bit,CharWidth:BYTE;
  3870.     data1:MonoFontchar;
  3871.     data2:ColorFontChar;
  3872. BEGIN
  3873.  IF (nr=0) or (nr>LoadMAX)
  3874.   THEN BEGIN
  3875.         Error:=Err_InvalidSpritenumber;
  3876.         Exit
  3877.        END;
  3878.  header:=DefaultHeader; {Schablone übernehmen}
  3879.  WITH header DO
  3880.   BEGIN
  3881.    IF GraphTextOrientation=horizontal
  3882.     THEN BEGIN {Sprite ist nur 1 Charzelle hoch}
  3883.           Hoehe_in_Zeilen:=FontHeight;
  3884.           Breite_in_4er_Gruppen:=(OutTextLength(s)+3) SHR 2
  3885.          END
  3886.     ELSE BEGIN {Sprite ist nur 1 Charzelle breit}
  3887.           Hoehe_in_Zeilen:=FontHeight*Length(s);
  3888.           Breite_in_4er_Gruppen:=(FontWidth+3) SHR 2
  3889.          END;
  3890.    SpriteLength:=Kopf+  {Spriteheader-Größe}
  3891.                  2*(2*Hoehe_in_Zeilen)+  {li. & re. Begrenzungen}
  3892.                  2*(2*4*Breite_in_4er_Gruppen)+  {ob. & un. Begrenzungen}
  3893.                  Breite_in_4er_Gruppen*4*Hoehe_in_Zeilen;  {Data}
  3894.    IF (Breite_in_4er_Gruppen*Hoehe_in_Zeilen=0)
  3895.       OR (SpriteLength>65521-15) {GetMem-Obergrenze=65521}
  3896.     THEN BEGIN
  3897.           Error:=Err_NoSprite;
  3898.           Exit
  3899.          END;
  3900.    {noch genug Platz da?}
  3901.    IF (Header.SpriteLength+15>MaxAvail+SPRITESIZE[nr])
  3902.     THEN BEGIN
  3903.           Error:=Err_NotEnoughMemory;
  3904.           Exit
  3905.          END
  3906.     ELSE FreeSpriteMem(nr);  {evtl. alten Speicher freigeben}
  3907.  
  3908.    getmem(p1,Header.SpriteLength+15);       {genug Platz reservieren}
  3909.    SPRITESIZE[nr]:=Header.SpriteLength+15;
  3910.    SPRITEPTR [nr]:=p1;
  3911.    IF (LONGINT(p1) mod 16)=0
  3912.     THEN p2:=p1                             {p2 auf Segmentgrenze bringen}
  3913.     ELSE LONGINT(p2):=LONGINT(p1) + (16-LONGINT(p1) mod 16);
  3914.    segm:=LONGINT(p2) SHR 16 +(LONGINT(p2) AND 65535) SHR 4;
  3915.    SPRITEAD[nr]:=segm;
  3916.  
  3917.    ZeigerL:=Kopf;
  3918.    ZeigerR:=ZeigerL+Hoehe_in_Zeilen*2;
  3919.    ZeigerO:=ZeigerR+Hoehe_in_Zeilen*2;
  3920.    ZeigerU:=ZeigerO+(Breite_in_4er_Gruppen*4)*2;
  3921.    FOR i:=0 TO 3 DO
  3922.     Zeiger_auf_Plane[i]:=ZeigerU+(Breite_in_4er_Gruppen*4)*2+
  3923.                          i*Breite_in_4er_Gruppen*Hoehe_in_Zeilen;
  3924.    FOR i:=0 TO Hoehe_in_Zeilen-1 DO
  3925.     BEGIN
  3926.      MEMW[segm:ZeigerL +i SHL 1]:=+16000;
  3927.      MEMW[segm:ZeigerR +i SHL 1]:=WORD(-16000);
  3928.     END;
  3929.    FOR i:=0 TO Breite_in_4er_Gruppen*4-1 DO
  3930.     BEGIN
  3931.      MEMW[segm:ZeigerO +i SHL 1]:=+16000;
  3932.      MEMW[segm:ZeigerU +i SHL 1]:=WORD(-16000);
  3933.     END;
  3934.  
  3935.    MOVE(Header,p2^,Kopf);  {Spriteheader auf Heap bringen}
  3936.  
  3937.    {jetzt Spritedaten berechnen: dazu Pixel in Speicher "zeichnen"}
  3938.    offs:=MaxFontWidth-FontWidth; x:=0; y:=0;
  3939.    IF (FontType=TagMonoFont)
  3940.     THEN FOR i:=1 TO Length(s) DO
  3941.           BEGIN
  3942.            data1:=MonoFont(CurrentFont^)[BYTE(s[i])];
  3943.            CharWidth:=FontWidthTable[BYTE(s[i])];
  3944.            FOR z:=0 TO FontHeight-1 DO
  3945.             BEGIN
  3946.              b:=WORD(data1[z]);
  3947.              FOR bit:=0 TO CharWidth-1 DO
  3948.               BEGIN
  3949.                IF b and FontMask[bit+offs]<>0
  3950.                 THEN wert:=GraphTextColor
  3951.                 ELSE IF (GraphTextColor<>GraphTextBackground)
  3952.                       THEN wert:=GraphTextBackground
  3953.                       ELSE wert:=0;
  3954.         {Punkt (a,b) -> b*Breite_in_4er_Gruppen+(x div 4) auf Plane x mod 4}
  3955.                MEM[segm:Zeiger_auf_Plane[(x+bit) AND 3]+
  3956.                         (y+z)*Breite_in_4er_Gruppen+
  3957.                         (x+bit) SHR 2]:=wert;
  3958.                IF wert<>0
  3959.             THEN BEGIN {Grenzen evtl. neu berechnen}
  3960.                       IF x+bit<INTEGER(MEMW[segm:ZeigerL +(y+z) SHL 1])
  3961.                        THEN MEMW[segm:ZeigerL +(y+z) SHL 1]:=x+bit;
  3962.                       IF x+bit>INTEGER(MEMW[segm:ZeigerR +(y+z) SHL 1])
  3963.                        THEN MEMW[segm:ZeigerR +(y+z) SHL 1]:=x+bit;
  3964.                       IF y+z<INTEGER(MEMW[segm:ZeigerO +(x+bit) SHL 1])
  3965.                        THEN MEMW[segm:ZeigerO +(x+bit) SHL 1]:=y+z;
  3966.                       IF y+z>INTEGER(MEMW[segm:ZeigerU +(x+bit) SHL 1])
  3967.                        THEN MEMW[segm:ZeigerU +(x+bit) SHL 1]:=y+z;
  3968.                      END;
  3969.               END;
  3970.             END;
  3971.            IF GraphTextOrientation=horizontal
  3972.             THEN INC(x,CharWidth)
  3973.             ELSE INC(y,FontHeight);
  3974.           END
  3975.     ELSE FOR i:=1 TO Length(s) DO
  3976.           BEGIN
  3977.            data2:=ColorFont(CurrentFont^)[BYTE(s[i])];
  3978.            CharWidth:=FontWidthTable[BYTE(s[i])];
  3979.            FOR z:=0 TO FontHeight-1 DO
  3980.             FOR bit:=0 TO CharWidth-1 DO
  3981.              BEGIN
  3982.               b:=data2[z][bit];
  3983.               IF b<>0 THEN wert:=b
  3984.               ELSE IF (GraphTextColor<>GraphTextBackground)
  3985.                     THEN wert:=GraphTextBackground
  3986.                     ELSE wert:=0;
  3987.        {Punkt (a,b) -> b*Breite_in_4er_Gruppen+(x div 4) auf Plane x mod 4}
  3988.               MEM[segm:Zeiger_auf_Plane[(x+bit) AND 3]+
  3989.                        (y+z)*Breite_in_4er_Gruppen+
  3990.                        (x+bit) SHR 2]:=wert;
  3991.  
  3992.               IF wert<>0
  3993.            THEN BEGIN {Grenzen evtl. neu berechnen}
  3994.                      IF x+bit<INTEGER(MEMW[segm:ZeigerL +(y+z) SHL 1])
  3995.                       THEN MEMW[segm:ZeigerL +(y+z) SHL 1]:=x+bit;
  3996.                      IF x+bit>INTEGER(MEMW[segm:ZeigerR +(y+z) SHL 1])
  3997.                       THEN MEMW[segm:ZeigerR +(y+z) SHL 1]:=x+bit;
  3998.                      IF y+z<INTEGER(MEMW[segm:ZeigerO +(x+bit) SHL 1])
  3999.                       THEN MEMW[segm:ZeigerO +(x+bit) SHL 1]:=y+z;
  4000.                      IF y+z>INTEGER(MEMW[segm:ZeigerU +(x+bit) SHL 1])
  4001.                       THEN MEMW[segm:ZeigerU +(x+bit) SHL 1]:=y+z;
  4002.                     END;
  4003.              END;
  4004.            IF GraphTextOrientation=horizontal
  4005.             THEN INC(x,CharWidth)
  4006.             ELSE INC(y,FontHeight);
  4007.           END
  4008.  
  4009.   END; {of WITH}
  4010.  
  4011. END;
  4012.  
  4013. FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN; ASSEMBLER;
  4014. { in: s1,s2 = Spritepositionsnummern zweier Sprites}
  4015. {     SpriteN[s1],SpriteX[s1],SpriteY[s1] = Spritedaten von Sprite s1    }
  4016. {     SpriteN[s2],SpriteX[s2],SpriteY[s2] = Spritedaten von Sprite s2    }
  4017. {out: TRUE/FALSE für "Sprites kollidieren"/"Sprites kollidieren nicht"   }
  4018. {rem: Diese Überprüfung geschieht punktgenau und ist unabhängig davon,   }
  4019. {     ob die Sprites z.Z. gerade sichtbar sind oder nicht.               }
  4020. {     Inaktive Sprites (SpriteN[s?]=0) können nicht miteinander kollid.  }
  4021. {     Ein Sprite kann nicht mit sich selbst kollidieren (s1=s2 -> FALSE) }
  4022. ASM
  4023.      MOV SI,s1               {1.Parameter s1 vom Stack holen}
  4024.      MOV DI,s2               {2.Parameter s2 vom Stack holen}
  4025.      CMP SI,DI
  4026.      JE  @NOHIT1             {Sprite kann sich nicht selbst treffen}
  4027.      SHL SI,1
  4028.      mov cx,[SI + OFFSET SpriteN]
  4029.      jcxz @NOHIT1            {Sprite <>0, d.h.: überhaupt aktiv?}
  4030.      SHL DI,1
  4031.      MOV BX,[DI + OFFSET SpriteN]
  4032.      OR  BX,BX               {dto. für anderes Sprite}
  4033.      JNE @PRUEF2
  4034.    @NOHIT1:
  4035.      JMP @NOHIT7             {inaktive Sprites können auch nicht}
  4036.                              {kollidieren -> FALSE zurückgeben  }
  4037. {hier: SI (DI) = Zeiger auf 1. (2.) Sprite in ?WRTD[..] ,}
  4038. {      CX (BX) = Spritenummer von Sprite 1 (2)           }
  4039. {(etwas später wird dann DS (ES) = Segment der Spr.daten von Spr.1 (2) )}
  4040.    @PRUEF2:
  4041.      MOV AX,[SI + OFFSET SpriteY]
  4042.      MOV DX,[DI + OFFSET SpriteY]
  4043.      mov si,[SI + OFFSET SpriteX]  {SI = x1}
  4044.      mov di,[DI + OFFSET SpriteX]  {DI = x2}
  4045.      shl bx,1                      {BX = Spritenummer2 * 2}
  4046.      mov es,[BX + OFFSET SPRITEAD] {ES = Segment der Spritedaten2}
  4047.      mov bx,cx                     {(CX = Spritenummer1)}
  4048.      shl bx,1                      {BX = Spritenummer1 * 2}
  4049.      MOV ds,[BX + OFFSET SPRITEAD]
  4050.  
  4051.      mov [y1],ax
  4052.      mov [y2],dx
  4053.      sub dx,ax
  4054.      mov CS:WORD PTR @y2_y1+1,dx
  4055.      mov [x1],si
  4056.      mov [x2],di
  4057.      mov dx,di
  4058.      sub dx,si
  4059.      mov CS:WORD PTR @x2_x1+1,dx
  4060.      mov ax,es:[Left]              {AX = Zeiger auf linke Randdaten}
  4061.      mov CS:WORD PTR @lirand2+1,ax
  4062.      mov ax,es:[Right]             {AX = Zeiger auf rechte Randdaten}
  4063.      mov CS:WORD PTR @rerand2+1,ax
  4064.      mov ax,es:[Top]               {AX = Zeiger auf obere Randdaten}
  4065.      mov CS:WORD PTR @orand2+1,ax
  4066.      mov ax,es:[Bottom]            {AX = Zeiger auf untere Randdaten}
  4067.      mov CS:WORD PTR @urand2+1,ax
  4068.      mov ax,es:[Breite]            {AX = max. Breite in 4er-Gruppen}
  4069.      shl al,1
  4070.      shl al,1
  4071.      mov CS:WORD PTR @breite2+1,ax {*4 = Breite in Punkten}
  4072.      mov ax,es:[Hoehe]
  4073.      mov CS:WORD PTR @hoehe2+1,ax  {Höhe von Sprite2 in Punkten}
  4074.  
  4075.      MOV AX,[Left]                 {AX = Zeiger auf linke Randdaten}
  4076.      MOV CS:WORD PTR @LIRAND1+1,AX
  4077.      MOV AX,[Right]                {AX = Zeiger auf rechte Randdaten}
  4078.      MOV CS:WORD PTR @RERAND1+1,AX
  4079.      MOV AX,[Top]                  {AX = Zeiger auf obere Randdaten}
  4080.      MOV CS:WORD PTR @ORAND1+1,AX
  4081.      MOV AX,[Bottom]               {AX = Zeiger auf untere Randdaten}
  4082.      MOV CS:WORD PTR @URAND1+1,AX
  4083.      MOV BX,[Breite]               {BX = max. Breite in 4er-Gruppen}
  4084.      SHL BX,1
  4085.      SHL BX,1                      {*4 = Breite in Punkten}
  4086.      MOV CS:WORD PTR @BREITE1+2,BX
  4087.  
  4088.      lea bx,[si+bx-1]              {BX := x1 + breite1 - 1  (=x1last)}
  4089.    @breite2:
  4090.      mov bp,1234h                  {Dummywert}
  4091.      mov cx,bp                     {CX = breite2 brauchen wir später nochmal}
  4092.      lea bp,[di+bp-1]              {BP := x2 + breite2 - 1  (=x2last)}
  4093.      cmp bx,bp
  4094.      jle @noex1
  4095.      mov bp,bx
  4096.    @noex1:                         {hier: BP = max(x1last,x2last)  (=maxx)}
  4097.      cmp si,di
  4098.      jle @X1_klgl_X2
  4099.      xchg si,di
  4100.    @X1_klgl_X2:                    {hier: SI = min(x1,x2)  (=minx)}
  4101.      stc
  4102.      sbb si,bp                     {SI := minx - maxx - 1 = - (maxx - minx + 1)}
  4103.    @breite1:
  4104.      add cx,1234h                  {(Dummywert)  CX := breite1 + breite2}
  4105.      add cx,si                     {CX := breite1 + breite2 - (maxx - minx + 1)}
  4106.      dec cx {CX := breite1 + breite2 - (maxx - minx + 1) - 1 (=ueberlappx - 1)}
  4107.      js @NOHIT2                    {kein Treffer, wenn ueberlappx <= 0}
  4108.      mov [ueberlappx_1],cx
  4109.  
  4110.      mov ax,[Hoehe]
  4111.      mov bx,ax                     {BX := hoehe1}
  4112.      mov di,[y1]                   {DI := y1}
  4113.      add ax,di                     {AX := y1 + hoehe1}
  4114.      dec ax                        {AX := y1 + hoehe1 - 1  (=y1last)}
  4115.    @hoehe2:
  4116.      mov si,1234h
  4117.      mov dx,[y2]
  4118.      add dx,si                     {DX := y2 + hoehe2}
  4119.      dec dx                        {DX := y2 + hoehe2 - 1  (=y2last)}
  4120.      cmp ax,dx
  4121.      jge @noex2
  4122.      mov ax,dx
  4123.    @noex2:                         {hier: AX = max(y1last,y2last)  (=maxy)}
  4124.      mov dx,[y2]
  4125.      cmp di,dx                     {(DI = y1)}
  4126.      jle @noex3
  4127.      mov di,dx
  4128.    @noex3:                         {hier: DI = min(y1,y2)  (=miny)}
  4129.      sub di,ax                     {DI := miny - maxy = - (maxy - miny)}
  4130.      lea ax,[bx+si-2]              {AX := hoehe1 + hoehe2 - 2}
  4131.      add ax,di {AX := hoehe1 + hoehe2 - (maxy - miny + 1) - 1 (=ueberlappy - 1)}
  4132.      js @NOHIT2                    {kein Treffer, wenn ueberlappy <= 0}
  4133.      mov [ueberlappy_1],ax
  4134.  
  4135. {hier: AX = ueberlappy - 1, CX = ueberlappx - 1}
  4136.    @x2_x1:
  4137.      mov dx,1234h                  {Dummywert}
  4138.      xor bx,bx                     {ab jetzt: BX = 0 !}
  4139.      or dx,dx
  4140.      js @X2_X1_kl_0                {if x2 - x1 >= 0 then...}
  4141.      mov [hit2xfirst],bx           {...hit2xfirst := 0}
  4142.      mov [hit1xfirst],dx           {...hit1xfirst := x2 - x1}
  4143.      jmp @Yhits    {SHORT}
  4144.  
  4145. {Sprungleiste für NOHIT (paßt hier gut hin)}
  4146.    @NOHIT2:
  4147.      JMP @NOHIT7
  4148.  
  4149. {jetzt wieder normales Programm}
  4150.    @X2_X1_kl_0:                    {else (x2 - x1 < 0)...}
  4151.      mov [hit1xfirst],bx           {...hit1xfirst := 0}
  4152.      neg dx                        {DX := x1 - x2}
  4153.      mov [hit2xfirst],dx           {...hit2xfirst := x1 - x2}
  4154.  
  4155.    @Yhits:                         {hier: AX = ueberlappy - 1}
  4156.    @y2_y1:
  4157.      mov dx,1234h                  {Dummywert}
  4158.      or dx,dx
  4159.      js @Y2_Y1_kl_0                {if y2 - y1 >= 0 then...}
  4160.      mov [hit2yfirst],bx           {...hit2yfirst := 0}
  4161.      mov [hit1yfirst],dx           {...hit1yfirst := y2 - y1}
  4162.      jmp @iterate  {SHORT}
  4163.    @Y2_Y1_kl_0:                    {else (y2 - y1 < 0)...}
  4164.      mov [hit1yfirst],bx           {...hit1yfirst := 0}
  4165.      neg dx                        {DX := y1 - y2}
  4166.      mov [hit2yfirst],dx           {...hit2yfirst := y1 - y2}
  4167.  
  4168. {Nun werden iterativ die überlappenden Zeilen und Spalten exakt geprüft}
  4169.    @iterate:
  4170.      mov cx,[ueberlappy_1]         {Anzahl der zu vergleichenden Zeilen -1}
  4171.      shl cx,1                      {*2, da Word-Werte!}
  4172.    @lirand1:
  4173.      mov si,1234h                  {Dummywert}
  4174.    @lirand2:
  4175.      mov di,1234h                  {Dummywert}
  4176.    @rerand1:
  4177.      mov bx,1234h                  {Dummywert}
  4178.    @rerand2:
  4179.      mov bp,1234h                  {Dummywert}
  4180.      sub bx,si                     {BX := rerand1 - lirand1}
  4181.      sub bp,di                     {BP := rerand2 - lirand2}
  4182.      mov ax,[hit1yfirst]
  4183.      shl ax,1
  4184.      add si,ax                  {SI := 1.Zeile, in der Sp.1 mit Sp.2 überlappt}
  4185.      mov ax,[hit2yfirst]
  4186.      shl ax,1
  4187.      add di,ax                  {DI := 1.Zeile, in der Sp.2 mit Sp.1 überlappt}
  4188.      add si,cx                  {dto., letzte Zeile}
  4189.      add di,cx
  4190.    @one_line:
  4191.      mov ax,[si]                   {DS:AX := x1li[Zeile]}
  4192.      mov dx,es:[di]                {ES:DX := x2li[Zeile]}
  4193.      add ax,[x1]                   {AX := x1li[Zeile] + x1  (=c)}
  4194.      add dx,[x2]                   {DX := x2li[Zeile] + x2  (=d)}
  4195.      cmp ax,dx
  4196.      jge @C_grgl_D
  4197.      mov ax,dx
  4198.    @C_grgl_D:                      {hier: AX = max(c,d)}
  4199.      mov cx,[si+bx]                {DS:CX := x1re[Zeile]}
  4200.      mov dx,es:[di+bp]             {ES:DX := x2re[Zeile]}
  4201.      add cx,[x1]                   {CX := x1re[Zeile] + x1  (=a)}
  4202.      add dx,[x2]                   {DX := x2re[Zeile] + x2  (=b)}
  4203.      cmp cx,dx
  4204.      jle @A_klgl_B
  4205.      mov cx,dx
  4206.    @A_klgl_B:                      {hier: CX = min(a,b)}
  4207.      cmp cx,ax                     {min(a,b) >= max(c,d) ?}
  4208.      jge @found_Xhit               {ja: Treffer in X-Richtung gefunden!}
  4209.      dec si                        {nächste Zeile (-> Word-Werte!)}
  4210.      dec si
  4211.      dec di
  4212.      dec di
  4213.      dec WORD PTR [ueberlappy_1]
  4214.      jns @one_line
  4215. {kein Treffer in X-Richtung -> überhaupt kein Treffer!}
  4216.      jmp @NOHIT7
  4217.  
  4218. {ansonsten: Treffer in X-Ri., jetzt noch Y-Ri. prüfen (analog zu oben) und  }
  4219. {"Treffer!" nur dann ausgeben, falls auch mind. 1 Treffer in Y-Ri. existiert}
  4220.    @found_Xhit:
  4221.      mov cx,[ueberlappx_1]         {Anzahl der zu vergleichenden Spalten -1}
  4222.      shl cx,1                      {*2, da Word-Werte!}
  4223.    @orand1:
  4224.      mov si,1234h                  {Dummywert}
  4225.    @orand2:
  4226.      mov di,1234h                  {Dummywert}
  4227.    @urand1:
  4228.      mov bx,1234h                  {Dummywert}
  4229.    @urand2:
  4230.      mov bp,1234h                  {Dummywert}
  4231.      sub bx,si                     {BX := urand1 - orand1}
  4232.      sub bp,di                     {BP := urand2 - orand2}
  4233.      mov ax,[hit1xfirst]
  4234.      shl ax,1                      {*2, da Word-Werte!}
  4235.      add si,ax                     {SI := orand1 + 2 * hit1xfirst}
  4236.      mov ax,[hit2xfirst]
  4237.      shl ax,1                      {*2, da Word-Werte!}
  4238.      add di,ax                     {DI := orand2 + 2 * hit2xfirst}
  4239.      add si,cx
  4240.      add di,cx
  4241.    @one_column: mov ax,[si]        {AX := y1ob[Spalte]}
  4242.      cmp ax,16000                  {Dummywert für "leere Spalte"?}
  4243.      je @next_column               {ja, also sicherlich kein Treffer}
  4244.      mov dx,es:[di]                {DX := y2ob[Spalte]}
  4245.      cmp dx,16000                  {auch 2.Sprite prüfen: "leere Spalte"?}
  4246.      je @next_column               {ja, kein Treffer}
  4247.      add ax,[y1]                   {AX := y1ob + y1  (=c)}
  4248.      add dx,[y2]                   {DX := y2ob + y2  (=d)}
  4249.      cmp ax,dx
  4250.      jge @C_grgl_D2
  4251.      mov ax,dx
  4252.    @C_grgl_D2:                     {hier: AX = max(c,d)}
  4253.      mov cx,[si+bx]                {DS:CX := y1un[Spalte]}
  4254.      mov dx,es:[di+bp]             {ES:DX := y2un[Spalte]}
  4255.      add cx,[y1]                   {CX := y1un + y1  (=a)}
  4256.      add dx,[y2]                   {DX := y2un + y2  (=b)}
  4257.      cmp cx,dx
  4258.      jle @A_klgl_B2
  4259.      mov cx,dx
  4260.    @A_klgl_B2:                     {hier: CX = min(a,b)}
  4261.      cmp cx,ax                     {min(a,b) >= max(c,d) ?}
  4262.      jge @HIT2                     {ja: Treffer gefunden!}
  4263.    @next_column:
  4264.      dec si                        {nein, nächste Spalte (-> Word-Werte!)}
  4265.      dec si
  4266.      dec di
  4267.      dec di
  4268.      dec WORD PTR [ueberlappx_1]
  4269.      jns @one_column
  4270.  
  4271.    @NOHIT7:
  4272.      XOR AX,AX                     {als Ergebnis 0 = FALSE zurückgeben}
  4273.      JMP @TREFF_END  {SHORT}
  4274.    @HIT2:
  4275.      MOV AX,1                      {als Ergebnis 1 = TRUE zurückgeben}
  4276.  
  4277.    @TREFF_END:
  4278.    {$IFOPT G+}
  4279.      mov bp,sp                     {nur nötig für Compilerschalter G+!}
  4280.    {$ENDIF}
  4281.      mov dx,seg @DATA              {sonst wird BP von TP wiederhergestellt}
  4282.      mov ds,dx
  4283. END;
  4284.  
  4285. PROCEDURE SetSplitIndex(number:INTEGER);
  4286. { in: number = Index-Nummer des Sprites, bis zu dem die Sprites nicht}
  4287. {              auf das Animationsfenster zurechtgeklippt werden}
  4288. {out: - }
  4289. {rem: Nach Aufruf dieser Routine werden SpriteN[0..number] nicht- und}
  4290. {     SpriteN[number+1..NMAX] auf das Animationsfenster zurechtgeclipt}
  4291. {     Ist number <0 oder >NMAX, so werden alle Sprites geclipt!}
  4292. BEGIN
  4293.  IF number>NMAX THEN number:=-1;
  4294.  SplitIndex:=number;
  4295.  SplitIndex_mal2:=number*2
  4296. END;
  4297.  
  4298. FUNCTION GetSplitIndex:INTEGER;
  4299. { in: - }
  4300. {out: Momentan gesetzter Wert für SplitIndex}
  4301. BEGIN
  4302.  GetSplitIndex:=SplitIndex
  4303. END;
  4304.  
  4305. PROCEDURE SetAnimateWindow(x1,y1,x2,y2:INTEGER);
  4306. { in: (x1,y1) = linke obere Ecke des zu setzenden Animationsbereiches}
  4307. {     (x2,y2) = dto., rechte untere Ecke}
  4308. {out: Win* wurden entsprechend neugesetzt}
  4309. {     BWin* = Backups der wichtigsten Werte}
  4310. {rem: Die Punkte müssen in absoluten Koordinaten angegeben werden}
  4311. {     Das Fenster muß eine Mindestgröße von 32x32 Punkten haben, }
  4312. {     x1 und x2-x1+1 müssen Vielfache von 4 sein (oder werden darauf gebracht)!}
  4313. BEGIN
  4314.  x1:=x1 AND $FFFC;  {x1 auf Vielfaches von 4 bringen}
  4315.  WinXMIN:=x1; WinXMINdiv4:=x1 SHR 2;
  4316.  WinYMIN:=y1; WinYMIN_mul_LINESIZE:=y1*LINESIZE;
  4317.  WinYMINmLINESIZEaWinXMINdiv4:=WinYMIN_mul_LINESIZE+WinXMINdiv4;
  4318.  WinWidth :=succ(x2-x1) AND $FFFC;  {Weite auf Vielfaches von 4 bringen}
  4319.  WinHeight:=succ(y2-y1);
  4320.  WinXMAX:=WinXMIN+WinWidth-1;
  4321.  WinYMAX:=WinYMIN+WinHeight-1;
  4322.  WinWidthDiv4:=WinWidth SHR 2;
  4323.  WinLowerRight:=(XMAX-WinXMAX) SHR 2 + (YMAX-WinYMAX)*LINESIZE;
  4324.  IF (WinXMIN<0) OR (WinYMIN<0) OR (WinWidth<32) OR (WinHeight<32) OR
  4325.     (WinXMAX>XMAX) OR (WinYMAX>YMAX)
  4326.   THEN Error:=Err_InvalidCoordinates;
  4327.  
  4328.  BWinXMIN:=WinXMIN; {Backups anlegen}
  4329.  BWinYMIN:=WinYMIN;
  4330.  BWinXMAX:=WinXMAX;
  4331.  BWinYMAX:=WinYMAX;
  4332.  BWinLowerRight:=WinLowerRight;
  4333.  BWinYMIN_mul_LINESIZE:=WinYMIN_mul_LINESIZE
  4334. END;
  4335.                                      
  4336. PROCEDURE Animate;
  4337. { in: PAGEADR = aktuelle Grafikseite(nadresse),auf der gezeichnet werden soll}
  4338. {     BACKGNDADR = Hintergrundseite(nadresse) }
  4339. {     BACKGROUNDMODE = STATIC/SCROLLING für festen/scrollbaren Hintergrund}
  4340. {     SpriteN[] = Spritenummern der darzustellenden Sprites }
  4341. {     SpriteX[],SpriteY[] = deren zugehörigen (virtuellen) Koordinaten}
  4342. {     StartVirtualX,StartVirtualY = obere linke Bildschirmecke  }
  4343. {     (PAGE = aktuell dargestellte Grafikseite) }
  4344. {     Win* = Abmessungen des Hintergrundwindows }
  4345. {out: PAGE = 0/1, wenn PAGE vorher 1/0 war }
  4346. {     PAGEADR = neue, aktuelle Grafikseite(nadresse) }
  4347. {rem: Animate löscht den Inhalt der alten Grafik (mithilfe der Hintergrund-  }
  4348. {     seite), zeichnet alle sichtbaren Sprites, synchronisiert auf das dis-  }
  4349. {     playenable-Signal und schaltet dann auf die so fertiggestellte Seite um}
  4350. VAR leftcut,rightcut,topcut,bottomcut:WORD;
  4351.     {x,y,} xtil,ytil,actindex:INTEGER;
  4352.  
  4353.     KachelnWegLinks,KachelnWegOben,
  4354.     innerTilesX,innerTilesY,
  4355.     stepX1,stepX2,
  4356.     Xoffscreen,Yoffscreen,
  4357.     counter,
  4358.     Korrektur,
  4359.     BytesPerPlane,LINESIZE_sub_BytesPerPlane,
  4360.     leftcutDIV4,
  4361.     tempActIndex,tempDI,tempXtil,tempYtil,{tempX,tempY,}
  4362.     oldActIndex,oldDI,
  4363.     StartWritePlane,StartLesePlane: INTEGER;
  4364. BEGIN
  4365.  IF EMSused AND (BackgroundMode=STATIC)
  4366.   THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  4367.  ASM
  4368.     CLD
  4369.     {zuerst das Hintergrundbild auf die aktuelle Seite kopieren:}
  4370.     CMP BackgroundMode,STATIC   {welcher Hintergrundmodus?}
  4371.     JE @static_bckgnd
  4372.     JMP @scrolling_bckgnd
  4373.  
  4374.   @static_bckgnd:
  4375.     MOV BX,WinHeight
  4376.     MOV DX,WinWidth
  4377.     MOV SI,WinYMINmLINESIZEaWinXMINdiv4  {1.Start/Zieladresse}
  4378.     MOV DI,SI
  4379.  
  4380.     MOV ES,PAGEADR              {Grafikseite mit Hintergrundmuster füllen}
  4381.     CMP UpdateOuterArea,0       {äußeren Hintergrund updaten?}
  4382.     MOV DS,BACKGNDADR
  4383.  
  4384.     je @skip_outer
  4385.     {inneren und äußeren Hintergrund zugleich erledigen}
  4386.     xor si,si
  4387.     xor di,di
  4388.  
  4389.     mov ax,0102h
  4390.     mov dx,3c4h
  4391.     mov bx,pagesize/2
  4392.     out dx,ax {Schreibplane 0}
  4393.     mov cx,bx
  4394.     rep movsw
  4395.     mov ah,2
  4396.     out dx,ax {Schreibplane 1}
  4397.     mov cx,bx
  4398.     xor di,di
  4399.     rep movsw
  4400.     mov ah,4
  4401.     out dx,ax {Schreibplane 2}
  4402.     mov cx,bx
  4403.     xor di,di
  4404.     rep movsw
  4405.     mov ah,8
  4406.     out dx,ax {Schreibplane 3}
  4407.     mov cx,bx
  4408.     xor di,di
  4409.     rep movsw
  4410.     mov ax,seg @data
  4411.     mov ds,ax
  4412.     dec UpdateOuterArea
  4413.     jmp @sprites_zeichnen
  4414.    @skip_outer:  {nur Inneres zeichnen}
  4415.  
  4416.     PUSH BP
  4417.  
  4418.     CMP DX,XMAX+1               {Fenster durchgehend von links nach rechts?}
  4419.     JNE @innen
  4420.  
  4421.     MOV AX,0102h
  4422.     MOV DX,3C4h
  4423.     SHL BX,1                    {ja, kann mit einem REP MOVSB erledigt werden}
  4424.     MOV BX,CS:[OFFSET gadr + BX]   {BX := WinHeight * LINESIZE}
  4425.     MOV BP,PAGESIZE
  4426.     SUB BP,BX
  4427.  
  4428.     OUT DX,AX  {Schreibplane 0 anwählen}
  4429.     MOV CX,BX
  4430.     SHR CX,1
  4431.     REP MOVSW
  4432.     ADC CX,CX
  4433.     REP MOVSB
  4434.     SUB DI,BX  {DI zurückstellen}
  4435.     ADD SI,BP  {SI auf nächste "Plane" setzen}
  4436.  
  4437.     MOV AH,2
  4438.     OUT DX,AX  {Schreibplane 1 anwählen}
  4439.     MOV CX,BX
  4440.     SHR CX,1
  4441.     REP MOVSW
  4442.     ADC CX,CX
  4443.     REP MOVSB
  4444.     SUB DI,BX  {DI zurückstellen}
  4445.     ADD SI,BP  {SI auf nächste "Plane" setzen}
  4446.  
  4447.     MOV AH,4
  4448.     OUT DX,AX  {Schreibplane 2 anwählen}
  4449.     MOV CX,BX
  4450.     SHR CX,1
  4451.     REP MOVSW
  4452.     ADC CX,CX
  4453.     REP MOVSB
  4454.     SUB DI,BX  {DI zurückstellen}
  4455.     ADD SI,BP  {SI auf nächste "Plane" setzen}
  4456.  
  4457.     MOV AH,8
  4458.     OUT DX,AX  {Schreibplane 3 anwählen}
  4459.     MOV CX,BX
  4460.     SHR CX,1
  4461.     REP MOVSW
  4462.     ADC CX,CX
  4463.     REP MOVSB
  4464.  
  4465.     jmp @end_static
  4466.  
  4467.   @innen:
  4468.     SHR DX,1
  4469.     SHR DX,1                    {DX := Bytes je Zeile}
  4470.     MOV BP,LINESIZE
  4471.     SUB BP,DX                   {BP := Offset zur nächsten Zeile}
  4472.     MOV BH,DL
  4473.     XOR CH,CH
  4474.  
  4475.     MOV AX,0102h
  4476.     MOV DX,3C4h
  4477.     OUT DX,AX    {Schreibplane 0 anwählen}
  4478.  
  4479.     MOV AH,BL
  4480.  
  4481.     PUSH SI
  4482.     PUSH DI
  4483.   @loop_innen1:
  4484.     MOV CL,BH
  4485.     SHR CX,1
  4486.     REP MOVSW
  4487.     ADC CX,CX
  4488.     REP MOVSB                   {eine Zeile übertragen}
  4489.     ADD SI,BP                   {auf nächste Zeile positionieren}
  4490.     ADD DI,BP
  4491.     DEC BL                      {eine Zeile fertig}
  4492.     JNZ @loop_innen1
  4493.     POP DI
  4494.     POP SI
  4495.  
  4496.     ADD SI,PAGESIZE
  4497.     PUSH SI
  4498.     PUSH DI
  4499.     MOV BL,AH
  4500.     MOV AH,02h
  4501.     OUT DX,AX
  4502.     MOV AH,BL
  4503.   @loop_innen2:
  4504.     MOV CL,BH
  4505.     SHR CX,1
  4506.     REP MOVSW
  4507.     ADC CX,CX
  4508.     REP MOVSB                   {eine Zeile übertragen}
  4509.     ADD SI,BP                   {auf nächste Zeile positionieren}
  4510.     ADD DI,BP
  4511.     DEC BL                      {eine Zeile fertig}
  4512.     JNZ @loop_innen2
  4513.     POP DI
  4514.     POP SI
  4515.  
  4516.     ADD SI,PAGESIZE
  4517.     PUSH SI
  4518.     PUSH DI
  4519.     MOV BL,AH
  4520.     MOV AH,04h
  4521.     OUT DX,AX
  4522.     MOV AH,BL
  4523.   @loop_innen3:
  4524.     MOV CL,BH
  4525.     SHR CX,1
  4526.     REP MOVSW
  4527.     ADC CX,CX
  4528.     REP MOVSB                   {eine Zeile übertragen}
  4529.     ADD SI,BP                   {auf nächste Zeile positionieren}
  4530.     ADD DI,BP
  4531.     DEC BL                      {eine Zeile fertig}
  4532.     JNZ @loop_innen3
  4533.     POP DI
  4534.     POP SI
  4535.  
  4536.     ADD SI,PAGESIZE
  4537.     MOV BL,AH
  4538.     MOV AH,08h
  4539.     OUT DX,AX
  4540.     MOV AH,BL
  4541.   @loop_innen4:
  4542.     MOV CL,BH
  4543.     SHR CX,1
  4544.     REP MOVSW
  4545.     ADC CX,CX
  4546.     REP MOVSB                   {eine Zeile übertragen}
  4547.     ADD SI,BP                   {auf nächste Zeile positionieren}
  4548.     ADD DI,BP
  4549.     DEC BL                      {eine Zeile fertig}
  4550.     JNZ @loop_innen4
  4551.  
  4552.   @end_static:
  4553.     POP BP
  4554.     MOV AX,SEG @DATA
  4555.     MOV DS,AX
  4556.  
  4557.     JMP @Sprites_zeichnen
  4558.  
  4559.   {---------------------------------}
  4560.  
  4561.   @scrolling_bckgnd:      {ab hier: Hintergrund aus Kacheln zusammensetzen}
  4562.  
  4563.    {müssen wir das Äußere um das Animationswindow neuzeichnen?}
  4564.     cmp UpdateOuterArea,0
  4565.     je @old_scrolling_bckgnd
  4566.     {ja, aber vielleicht gar nichts zum zeichnen da?:}
  4567.     mov bx,WinHeight
  4568.     dec bx
  4569.     mov bh,bl
  4570.     mov bl,WinWidthDiv4
  4571.     mov ax,WinYMINmLINESIZEaWinXMINdiv4
  4572.     {BL=WinWidthDiv4, BH=WinHeight-1, AX=WinYMIN*LINESIZE+WinXMIN DIV 4}
  4573.     or ax,ax      {linke obere Ecke des Windows = Punkt (0,0)?}
  4574.     jne @do_outer {nein: äußerer Rand zu zeichnen}
  4575.     cmp bx,YMAX SHL 8 +LINESIZE  {rechte untere Ecke = Punkt (XMAX,YMAX)?}
  4576.     je @old_scrolling_bckgnd  {ja, also kein äußerer Rand zu zeichnen}
  4577.  
  4578.   @do_outer: {jetzt äußeren Rand zeichnen}
  4579.     {╔════════════════╗ Einteilung des äußeren Randes in 3 Regionen}
  4580.     {║1111111111111111║ }
  4581.     {║1111111111111111║ }
  4582.     {║111┌───────┐2222║ }
  4583.     {║222│       │2222║ }
  4584.     {║222│       │2222║ }
  4585.     {║222└───────┘3333║ }
  4586.     {║3333333333333333║ }
  4587.     {║3333333333333333║ }
  4588.     {╚════════════════╝ }
  4589.  
  4590.     {BL=WinWidthDiv4, BH=WinHeight-1, AX=WinYMIN*LINESIZE+WinXMIN DIV 4}
  4591.     push bp
  4592.     push WinLowerRight
  4593.     mov bp,ax
  4594.     mov es,PAGEADR
  4595.     mov ds,BACKGNDADR
  4596.     mov dx,3C4h
  4597.     mov ax,0102h {Schreibplane 0}
  4598.     out dx,ax
  4599.     xor si,si   {Region 1 beginnt bei Offset 0}
  4600.     xor di,di
  4601.     mov cx,bp
  4602.     shr cx,1
  4603.     rep movsw
  4604.     adc cx,cx
  4605.     rep movsb
  4606.  
  4607.     mov ah,2    {Schreibplane 1}
  4608.     out dx,ax
  4609.     mov si,1*PAGESIZE
  4610.     xor di,di
  4611.     mov cx,bp
  4612.     shr cx,1
  4613.     rep movsw
  4614.     adc cx,cx
  4615.     rep movsb
  4616.  
  4617.     mov ah,4    {Schreibplane 2}
  4618.     out dx,ax
  4619.     mov si,2*PAGESIZE
  4620.     xor di,di
  4621.     mov cx,bp
  4622.     shr cx,1
  4623.     rep movsw
  4624.     adc cx,cx
  4625.     rep movsb
  4626.  
  4627.     mov ah,8    {Schreibplane 3}
  4628.     out dx,ax
  4629.     mov si,3*PAGESIZE
  4630.     xor di,di
  4631.     mov cx,bp
  4632.     shr cx,1
  4633.     rep movsw
  4634.     adc cx,cx
  4635.     rep movsb
  4636.  
  4637.     mov ah,1
  4638.     out dx,ax
  4639.  
  4640.     mov al,bl
  4641.     cbw       {AX:=WinWidth DIV 4; geht, weil WinWidth DIV 4 < 128 !}
  4642.     mov dl,LINESIZE
  4643.     sub dl,al {DL:=LINESIZE-WinWidth DIV 4}
  4644.     jz @region3 {Fenster geht von links nach rechts durch?}
  4645.     mov dh,bh {DH:=WinHeight-1}
  4646.     or dh,dh
  4647.     jz @region3 {Fenster nur 1 Zeile hoch?}
  4648.     xor ch,ch
  4649.     mov bx,ax
  4650.     add di,ax
  4651.     mov si,di {Zieladresse=Startadresse wg. Speicherlayout}
  4652.  
  4653.     mov bp,di
  4654.     push dx
  4655.   @region2a:
  4656.     {DL = Breite einer Zeile des Animationsfensters}
  4657.     {DH = WinHeight-1}
  4658.     {BX = WinWidth DIV 4 = Breite des Animations-Fensters DIV 4}
  4659.     {CX:=Breite einer Zeile vom rechten Rand des Animationsfensters zum}
  4660.     {linken Rand des Animationsfensters in der nächsten Zeile:}
  4661.     mov cl,dl  {ch=0}
  4662.     shr cx,1
  4663.     rep movsw
  4664.     adc cx,cx
  4665.     rep movsb
  4666.     add si,bx
  4667.     add di,bx
  4668.     dec dh
  4669.     jnz @region2a
  4670.  
  4671.     mov ax,0202h
  4672.     mov dx,3c4h
  4673.     out dx,ax
  4674.     pop dx
  4675.     push dx
  4676.     mov si,bp {SI=DI=BP für Plane #0, SI=DI+1*PAGESIZE für Plane #1, etc.}
  4677.     add si,1*PAGESIZE
  4678.     mov di,bp
  4679.   @region2b:
  4680.     mov cl,dl  {ch=0}
  4681.     shr cx,1
  4682.     rep movsw
  4683.     adc cx,cx
  4684.     rep movsb
  4685.     add si,bx
  4686.     add di,bx
  4687.     dec dh
  4688.     jnz @region2b
  4689.  
  4690.     mov ah,04h
  4691.     mov dx,3c4h
  4692.     out dx,ax
  4693.     pop dx
  4694.     push dx
  4695.     mov si,bp
  4696.     add si,2*PAGESIZE
  4697.     mov di,bp
  4698.   @region2c:
  4699.     mov cl,dl  {ch=0}
  4700.     shr cx,1
  4701.     rep movsw
  4702.     adc cx,cx
  4703.     rep movsb
  4704.     add si,bx
  4705.     add di,bx
  4706.     dec dh
  4707.     jnz @region2c
  4708.  
  4709.     mov ah,08h
  4710.     mov dx,3c4h
  4711.     out dx,ax
  4712.     pop dx
  4713.     mov si,bp
  4714.     add si,3*PAGESIZE
  4715.     mov di,bp
  4716.   @region2d:
  4717.     mov cl,dl  {ch=0}
  4718.     shr cx,1
  4719.     rep movsw
  4720.     adc cx,cx
  4721.     rep movsb
  4722.     add si,bx
  4723.     add di,bx
  4724.     dec dh
  4725.     jnz @region2d
  4726.  
  4727.  
  4728.   @region3:
  4729.     pop cx  {CX:=WinLowerRight}
  4730.     jcxz @endregion3 {keine Region 3 zum zeichnen}
  4731.     mov bx,cx  {Kopie davon nach BX}
  4732.     mov si,PAGESIZE
  4733.     sub si,cx  {Startadresse von Region 3}
  4734.     mov di,si  {Zieladresse:=Startadresse (geht wg. Speicherlayout!)}
  4735.     mov bp,di  {Zieladresse merken }
  4736.     mov ax,0102h
  4737.     mov dx,3c4h
  4738.     out dx,ax
  4739.     shr cx,1
  4740.     rep movsw
  4741.     adc cx,cx
  4742.     rep movsb
  4743.  
  4744.     mov ah,2
  4745.     out dx,ax
  4746.     mov cx,bx
  4747.     mov di,bp
  4748.     sub si,cx  {SI auf alten Wert zurück- und dann eine "Seite" weitersetzen}
  4749.     add si,PAGESIZE
  4750.     shr cx,1
  4751.     rep movsw
  4752.     adc cx,cx
  4753.     rep movsb
  4754.  
  4755.     mov ah,4
  4756.     out dx,ax
  4757.     mov cx,bx
  4758.     mov di,bp
  4759.     sub si,cx  {SI auf alten Wert zurück- und dann eine "Seite" weitersetzen}
  4760.     add si,PAGESIZE
  4761.     shr cx,1
  4762.     rep movsw
  4763.     adc cx,cx
  4764.     rep movsb
  4765.  
  4766.     mov ah,8
  4767.     out dx,ax
  4768.     mov cx,bx
  4769.     mov di,bp
  4770.     sub si,cx  {SI auf alten Wert zurück- und dann eine "Seite" weitersetzen}
  4771.     add si,PAGESIZE
  4772.     shr cx,1
  4773.     rep movsw
  4774.     adc cx,cx
  4775.     rep movsb
  4776.  
  4777.   @endregion3:
  4778.     pop bp
  4779.     mov ax,seg @data
  4780.     mov ds,ax
  4781.     dec UpdateOuterArea  {UpdateOuterArea rücksetzen}
  4782.  
  4783.   @old_scrolling_bckgnd:
  4784.  
  4785.   MOV DI,$F  {häufig benutzte Konstanten}
  4786.   MOV CX,4
  4787.  
  4788.  {#Kacheln, die links _echt_ weggeschnitten sind:}
  4789.  {IF StartVirtualX+WinXMIN-BackX1<0
  4790.    THEN KachelnWegLinks:=(StartVirtualX+WinXMIN-BackX1-15) DIV 16
  4791.    ELSE KachelnWegLinks:=(StartVirtualX+WinXMIN-BackX1) DIV 16;}
  4792.   MOV AX,StartVirtualX
  4793.   ADD AX,WinXMIN
  4794.   SUB AX,BackX1
  4795.   MOV BX,AX     {BX = StartVirtualX + WinXMIN - BackX1}
  4796.   SAR AX,CL
  4797.   MOV KachelnWegLinks,AX
  4798.  
  4799.  {Punkte, die der 1. (teilweise) sichtbaren Kachel links weggeschnitten sind:}
  4800.  {eigentlich: leftcut := ((StartVirtualX + WinXMIN - BackX1) MOD 16) AND $F  }
  4801.  {aber das ist äquivalent zu:}
  4802.  {leftcut := (StartVirtualX + WinXMIN - BackX1) AND $F}
  4803.  {das "AND $F" ist wg. evtl. underflow <0}
  4804.   AND BX,DI
  4805.   MOV leftcut,BX
  4806.  
  4807.   MOV AX,BX
  4808.   SHR AX,1
  4809.   SHR AX,1
  4810.   MOV leftcutDIV4,AX
  4811.  
  4812.  {Start-Leseplane links geschnittener Kacheln berechnen: leftcut AND 3}
  4813.  { Dann wird die zugehörige Maske daraus errechnet }
  4814.   MOV AH,BL      {BL = leftcut}
  4815.   AND AH,3
  4816.   MOV AL,4
  4817.   MOV StartLesePlane,AX   {muß im Stacksegment liegen!}
  4818.  
  4819.  {dto., für letzte (teilweise) sichtbare Kachel rechts}
  4820.  {eigentlich: rightcut := (16 - leftcut - (WinWidth MOD 16)) AND $F, aber s.o.!}
  4821.  {rightcut := (16 - leftcut - WinWidth) AND $F}
  4822.  {das "AND $F" ist wg. evtl. underflow <0}
  4823.   NEG BX     {BX=-leftcut}
  4824.   MOV SI,WinWidth
  4825.   MOV AX,16
  4826.   ADD AX,BX
  4827.   SUB AX,SI
  4828.   AND AX,DI  {AX = (16 - leftcut - WinWidth) AND $F}
  4829.   MOV rightcut,AX
  4830.  
  4831.  {#_ganzer_ Kacheln im Inneren des Windows je Zeile:}
  4832.  {innerTilesX:=(WinWidth - (-rightcut AND $F) - (-leftcut AND $F)) SHR 4;}
  4833.   NEG AX
  4834.   AND AX,DI
  4835.   AND BX,DI
  4836.   SUB SI,AX
  4837.   SUB SI,BX
  4838.   SHR SI,CL
  4839.   MOV innerTilesX,SI
  4840.  
  4841.  {stepX1=Additionsfaktor, um von rechtester (evtl. nur teilweise) sichtbaren}
  4842.  {       Kachel einer Zeile zur 1. _links nicht geschnittenen_ Kachel der   }
  4843.  {       nächsten Zeile zu kommen}
  4844.  {stepX2=dto., aber zur 1. (evtl. nur teilweise) sichtbaren Kachel der      }
  4845.  {       nächsten Zeile}
  4846.  {stepX1 := XTiles -(innerTilesX);}
  4847.  {stepX2 := stepX1;}
  4848.  {IF leftcut<>0 THEN dec(stepX2)} {stepX2 = XTiles -(innerTiles + (leftcut<>0))}
  4849.   MOV DX,XTiles
  4850.   MOV AX,DX
  4851.   SUB AX,SI
  4852.   MOV stepX1,AX
  4853.   OR BX,BX      {für den geg. Bereich gilt: leftcut = 0 <-> -leftcut and $F=0}
  4854.   JE @nodec
  4855.   DEC AX
  4856.  @nodec:
  4857.   MOV stepX2,AX
  4858.  
  4859.  
  4860.  {Start-Writeplane links nicht geschnittener Kacheln berechnen:}
  4861.  { (WinXMIN - leftcut) AND 3, wird hier berechnet per }
  4862.  { (WinXMIN - (leftcut AND $F) AND 3}
  4863.  { Dann wird die zugehörige Maske daraus errechnet }
  4864.   ADD BX,WinXMIN
  4865.   AND BX,3
  4866.   MOV AH,CS:[OFFSET CS_TranslateTab +BX]
  4867.   MOV AL,2
  4868.   MOV StartWritePlane,AX  {muß im Stacksegment liegen!}
  4869.  
  4870.  
  4871.  {nun analog für Y-Richtung:}
  4872.  {IF StartVirtualY+WinYMIN-BackY1<0
  4873.   THEN KachelnWegOben := (StartVirtualY + WinYMIN - BackY1 - 15) DIV 16
  4874.   ELSE KachelnWegOben := (StartVirtualY + WinYMIN - BackY1) DIV 16;}
  4875.   MOV AX,StartVirtualY
  4876.   ADD AX,WinYMIN
  4877.   SUB AX,BackY1
  4878.   MOV BX,AX
  4879.   SAR AX,CL
  4880.   MOV KachelnWegOben,AX
  4881.  
  4882.  {Index der 1. (evtl. nur teilweise) sichtbaren Kachel berechnen:}
  4883.  {actIndex := KachelnWegOben * XTiles + KachelnWegLinks + 1;}
  4884.  {Die "+1" ist, da BackTile[]-Zählung bei 0 beginnt, um BackTile[0]}
  4885.  {als OffscreenTile freizuhalten!}
  4886.   IMUL DX
  4887.   ADD AX,KachelnWegLinks
  4888.   INC AX
  4889.   MOV actIndex,AX
  4890.  
  4891.  {topcut := (StartVirtualY + WinYMIN - BackY1) AND $F;}
  4892.   AND BX,DI
  4893.   MOV topcut,BX
  4894.  
  4895.  {bottomcut := (16 - topcut - WinHeight) AND $F;}
  4896.   NEG BX
  4897.   MOV SI,WinHeight
  4898.   MOV AX,16
  4899.   ADD AX,BX
  4900.   SUB AX,SI
  4901.   AND AX,DI
  4902.   MOV bottomcut,AX
  4903.  
  4904.  {innerTilesY := (WinHeight - (-bottomcut AND $F) - (-topcut AND $F)) SHR 4;}
  4905.   NEG AX
  4906.   AND AX,DI
  4907.   AND BX,DI
  4908.   SUB SI,AX
  4909.   SUB SI,BX
  4910.   SHR SI,CL
  4911.   MOV innerTilesY,SI
  4912.  
  4913.   {---Jetzt zeichnen!---}
  4914.  
  4915.   { entfällt, da bereits in SetAnimateWindow() gesetzt:
  4916.   MOV AX,WinXMIN
  4917.   (* MOV x,AX *)
  4918.   MOV BX,WinYMIN
  4919.   (* MOV y,BX *)
  4920.   SHR AX,1
  4921.   SHR AX,1
  4922.   MOV WinXMINdiv4,AX
  4923.   SHL BX,1
  4924.   MOV BX,CS:[OFFSET GADR +BX]
  4925.   MOV WinYMIN_mul_LINESIZE,BX
  4926.   ADD AX,BX
  4927.   MOV WinYMINmLINESIZEaWinXMINdiv4,AX
  4928.   }
  4929.  
  4930.   MOV AX,KachelnWegLinks
  4931.   MOV xtil,AX
  4932.   MOV CX,AX  {CX = Kopie von KachelnWegLinks = xtil}
  4933.   MOV AX,KachelnWegOben
  4934.   MOV ytil,AX
  4935.  
  4936.   MOV ES,PAGEADR  {ein für allemal!}
  4937.   MOV BX,leftcut
  4938.   {Hier gilt: AX = ytil, BX = Leftcut, CX = xtil, ES = ^Grafiksegment}
  4939.   TEST BL,3      {leftcut MOD 4 = 0 ?}
  4940.   JZ @useMode1   {ja, Writemode1 nutzbar}
  4941.   JMP @useMode0  {nein, Writemode0 verwenden}
  4942.  
  4943.  @useMode1:
  4944.   {Wenn Fensterbreite Vielfaches von 4, leftcut mod 4=0 und linke Fenstergrenze}
  4945.   {auf Vielfaches von 4 fällt (dann ist damit auch rightcut mod 4=0) und       }
  4946.   {durchgehend Writemode1 anwendbar!}
  4947.   MOV AX,4105h   {Writemode1 einschalten}
  4948.   MOV DX,3CEh
  4949.   OUT DX,AX
  4950.   MOV AX,0F02h   {alle 4 Planes gleichzeitig ansprechen}
  4951.   MOV DX,3C4h
  4952.   OUT DX,AX
  4953.  
  4954.   CMP topcut,0      {IF ytil ε [0..YTiles(          }
  4955.   JE @m1SkipTopRow  { THEN DX = Yoffscreen := $FFFF }
  4956.   MOV AX,ytil       { ELSE DX = Yoffscreen := $0000 }
  4957.   CWD
  4958.   SUB AX,YTiles
  4959.   NOT AX
  4960.   OR AX,DX
  4961.   CWD
  4962.   NOT DX
  4963.   MOV Yoffscreen,DX
  4964.  
  4965.   MOV AX,WinXMINdiv4  {AX =richtiger Wert, falls gesprungen wird!}
  4966.   OR BX,BX   {BX=leftcut}
  4967.   JZ @m1SkipTopLeftCorner
  4968.  
  4969.   MOV SI,actIndex     {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
  4970.   AND SI,DX           { THEN SI := 0 }
  4971.   JZ @m1go1           { ELSE SI := actIndex }
  4972.   MOV AX,CX  {CX = xtil}
  4973.   CWD
  4974.   SUB AX,XTiles
  4975.   NOT AX
  4976.   OR AX,DX
  4977.   CWD
  4978.   NOT DX
  4979.   AND SI,DX
  4980.  
  4981.  @m1go1:
  4982.     {PROCEDURE DrawUpperLeftTile mit WriteMode1: }
  4983.     { in: WinXMIN,WinYMIN = Bildschirmkoordinaten}
  4984.     {     ES = ^Grafiksegment}
  4985.     {     SI = Kachelindex   }
  4986.     {     BX = leftcut       }
  4987.     {     CX = xtil          }
  4988.     {     leftcut MOD 4 = 0  }
  4989.     {     topcut, Win*, SCROLLADR,...}
  4990.     {out: ES = ^Grafiksegment}
  4991.     {rem: WriteMode1 ist bereits gesetzt und bleibt gesetzt}
  4992.     MOV AL,[OFFSET BackTile +SI]
  4993.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  4994.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  4995.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  4996.  
  4997.     MOV SI,topcut  {Dazu kommen die oben abgeschnittenen Zeilen:}
  4998.     MOV CX,16      {für jede Zeile 4 Bytes}
  4999.     SUB CX,SI      {CX := 16 - topcut = zu zeichnende Zeilen}
  5000.     SHL SI,1
  5001.     SHL SI,1
  5002.     ADD SI,AX      {SI = Zeiger auf erste zu kopierende Tile*zeile*}
  5003.  
  5004.     MOV AX,BX      {BX = leftcut}
  5005.     SHR AX,1       {SI um linken cutoff weitersetzen = leftcut DIV 4}
  5006.     SHR AX,1
  5007.     ADD SI,AX      {SI = Zeiger auf erstes zu kopierendes Tile*byte*}
  5008.  
  5009.     MOV DI,WinYMINmLINESIZEaWinXMINdiv4 {ES:DI = Zieladresse}
  5010.     MOV DS,SCROLLADR  {DS:SI = Quelladresse}
  5011.     {Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
  5012.     {verwendet werden!}
  5013.     PUSH BP        {wird beim Verlassen der Prozedur gebraucht!}
  5014.     MOV BP,16      {BP := (16 - leftcut) DIV 4 = Bytes je Kachelzeile}
  5015.     SUB BP,BX
  5016.     SHR BP,1
  5017.     SHR BP,1
  5018.  
  5019.     MOV AX,LINESIZE  {ist eine Konstante}
  5020.     SUB AX,BP
  5021.     MOV DX,4
  5022.     SUB DX,BP
  5023.     MOV BX,CX      {CX = Zeilenanzahl}
  5024.  
  5025.    @m1eineZeile4a1:
  5026.     MOV CX,BP
  5027.     REP MOVSB
  5028.     ADD DI,AX
  5029.     ADD SI,DX
  5030.     DEC BX
  5031.     JNZ @m1eineZeile4a1
  5032.  
  5033.     POP BP
  5034.     MOV AX,SEG @DATA
  5035.     MOV DS,AX
  5036.  
  5037.   {Auf nächste Kachel rechts davon positionieren:}
  5038.   INC actIndex
  5039.   INC xtil
  5040.  
  5041.   MOV AX,WinXMIN
  5042.   ADD AX,16
  5043.   SUB AX,leftcut
  5044.   (* MOV x,AX *)
  5045.   SHR AX,1
  5046.   SHR AX,1
  5047.  @m1SkipTopLeftCorner:
  5048.   ADD AX,WinYMIN_mul_LINESIZE
  5049.   MOV DI,AX   {ES:DI = ^Zieladresse}
  5050.  
  5051.   {Nun innerTilesX nur oben geschnittene Kacheln zeichnen:}
  5052.   MOV AX,innerTilesX
  5053.   OR AX,AX
  5054.   JBE @m1UpperInnerTilesDone
  5055.   MOV counter,AX
  5056.  
  5057.   MOV BX,16        {Korrekturfaktor, um DI eine Kachelzeile hoch und eine}
  5058.   SUB BX,topcut    {Kachelspalte weiterzusetzen}
  5059.   SHL BX,1
  5060.   MOV BX,CS:[OFFSET GADR +BX]
  5061.   NEG BX
  5062.   ADD BX,4         {BX := -(16 - topcut) * LINESIZE + 4}
  5063.  
  5064.  @m1repeat1:
  5065.   MOV SI,actIndex   {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
  5066.   AND SI,Yoffscreen { THEN SI := 0        }
  5067.   JZ @m1go2         { ELSE SI := actIndex }
  5068.   MOV AX,xtil
  5069.   CWD
  5070.   SUB AX,XTiles
  5071.   NOT AX
  5072.   OR AX,DX
  5073.   CWD
  5074.   NOT DX
  5075.   AND SI,DX
  5076.  @m1go2:
  5077.     {PROCEDURE DrawUpperInnerTile mit WriteMode1: }
  5078.     { in: ES:DI = ^Zieladresse}
  5079.     {     SI = Kachelindex    }
  5080.     {     BX = Zeilenkorrektur}
  5081.     {     leftcut MOD 4 = 0   }
  5082.     {     topcut, Win*, SCROLLADR,...}
  5083.     {out: ES:DI = ^Zieladresse der nächsten Kachel rechts davon}
  5084.     {rem: WriteMode1 ist bereits gesetzt und bleibt gesetzt}
  5085.     {     BX wird nicht verändert}
  5086.     MOV AL,[OFFSET BackTile +SI]
  5087.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  5088.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  5089.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  5090.  
  5091.     MOV SI,topcut  {Dazu kommen die oben abgeschnittenen Zeilen:}
  5092.     MOV CX,16      {für jede Zeile 4 Bytes}
  5093.     SUB CX,SI      {CX := 16 - topcut = zu zeichnende Zeilen}
  5094.     SHL SI,1
  5095.     SHL SI,1
  5096.     ADD SI,AX      {SI = Zeiger auf erste zu kopierende Tile*zeile*}
  5097.  
  5098.     MOV DX,DS
  5099.     MOV DS,SCROLLADR
  5100.     MOV AX,LINESIZE-4
  5101.  
  5102.    @m1eineZeile4b1:
  5103.     MOVSB
  5104.     MOVSB
  5105.     MOVSB
  5106.     MOVSB
  5107.     ADD DI,AX
  5108.     LOOP @m1eineZeile4b1
  5109.  
  5110.     {DI = ^Start der Zeile unterhalb der Kachel, jetzt auf Start der nächsten}
  5111.     {Kachel setzen:}
  5112.     {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  5113.     {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  5114.     {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  5115.     { ▀                      }
  5116.     MOV DS,DX         {kein POP BP nötig}
  5117.     ADD DI,BX
  5118.  
  5119.   {Auf nächste Kachel rechts davon positionieren:}
  5120.   INC actIndex
  5121.   INC xtil
  5122.   (* MOV AX,16 *)
  5123.   (* ADD x,AX *)
  5124.   DEC counter
  5125.   JNZ @m1repeat1
  5126.  
  5127.  @m1UpperInnerTilesDone:
  5128.   {ES:DI = ^erste Zeile der rechten oberen Eckkachel}
  5129.   CMP rightcut,0
  5130.   JE @m1SkipTopRightCorner
  5131.  
  5132.   MOV SI,actIndex   {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
  5133.   AND SI,Yoffscreen { THEN SI := 0        }
  5134.   JZ @m1go3         { ELSE SI := actIndex }
  5135.   MOV AX,xtil
  5136.   CWD
  5137.   SUB AX,XTiles
  5138.   NOT AX
  5139.   OR AX,DX
  5140.   CWD
  5141.   NOT DX
  5142.   AND SI,DX
  5143.  @m1go3:
  5144.     {PROCEDURE DrawUpperRightTile mit WriteMode1: }
  5145.     { in: ES:DI = ^Zieladresse}
  5146.     {     SI = Kachelindex    }
  5147.     {     rightcut MOD 4 = 0  }
  5148.     {     topcut, Win*, SCROLLADR,...}
  5149.     {out: ES = ^Grafiksegment }
  5150.     {rem: WriteMode1 ist bereits gesetzt und bleibt gesetzt}
  5151.     MOV AL,[OFFSET BackTile +SI]
  5152.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  5153.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  5154.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  5155.  
  5156.     MOV SI,topcut  {Dazu kommen die oben abgeschnittenen Zeilen:}
  5157.     MOV CX,16      {für jede Zeile 4 Bytes}
  5158.     SUB CX,SI      {CX := 16 - topcut = zu zeichnende Zeilen}
  5159.     SHL SI,1
  5160.     SHL SI,1
  5161.     ADD SI,AX      {SI = Zeiger auf erste zu kopierende Tile*zeile*}
  5162.  
  5163.     MOV AX,rightcut
  5164.     MOV DS,SCROLLADR  {DS:SI = Quelladresse}
  5165.     {Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
  5166.     {verwendet werden!}
  5167.     PUSH BP        {wird beim Verlassen der Prozedur gebraucht!}
  5168.     MOV BP,16      {BP := (16 - rightcut) DIV 4 = Bytes je Kachelzeile}
  5169.     SUB BP,AX
  5170.     SHR BP,1
  5171.     SHR BP,1
  5172.  
  5173.     MOV AX,LINESIZE
  5174.     SUB AX,BP
  5175.     MOV DX,4
  5176.     SUB DX,BP
  5177.     MOV BX,CX      {BX := zu zeichnende Zeilen}
  5178.  
  5179.    @m1eineZeile4c1:
  5180.     MOV CX,BP
  5181.     REP MOVSB
  5182.     ADD DI,AX
  5183.     ADD SI,DX
  5184.     DEC BX
  5185.     JNZ @m1eineZeile4c1
  5186.  
  5187.     POP BP
  5188.     MOV AX,SEG @DATA
  5189.     MOV DS,AX
  5190.  
  5191.  @m1SkipTopRightCorner:
  5192.   {Auf erste linke Kachel positionieren, die oben nicht mehr geschnitten ist:}
  5193.   MOV AX,stepx2
  5194.   ADD actIndex,AX
  5195.   (* MOV AX,WinXMIN *)
  5196.   (* MOV x,AX *)
  5197.   MOV AX,KachelnWegLinks
  5198.   MOV xtil,AX
  5199.   INC ytil
  5200.  
  5201.  @m1SkipTopRow:
  5202.   MOV AX,topcut  {IF topcut = 0 }
  5203.   NEG AX         { THEN AX = y := WinYMIN}
  5204.   JZ @m1l1       { ELSE AX = y := WinYMIN + (16 - topcut)}
  5205.   ADD AX,16
  5206.  @m1l1:
  5207.   ADD AX,WinYMIN
  5208.   (* MOV y,AX *)
  5209.  
  5210.   MOV DI,AX      {DI := y * LINESIZE +X DIV 4}
  5211.   SHL DI,1
  5212.   MOV DI,CS:[OFFSET GADR +DI]
  5213.   ADD DI,WinXMINdiv4
  5214.   {ES:DI = ^Zieladresse der 1.Kachel der 1.oben nicht geschnittenen Kachelzeile}
  5215.  
  5216.   CMP leftcut,0
  5217.   JZ @m1SkipLeftColumn
  5218.  
  5219.   MOV DX,16
  5220.   SUB DX,leftcut
  5221.   SHR DX,1
  5222.   SHR DX,1
  5223.   MOV AX,LINESIZE
  5224.   SUB AX,DX   {Korrekturfaktor für AX}
  5225.   MOV LINESIZE_sub_BytesPerPlane,AX
  5226.   MOV BytesPerPlane,DX  {Bytes zu moven}
  5227.  
  5228.   MOV AX,innerTilesY
  5229.   OR AX,AX
  5230.   JBE @m1LeftLoopDone
  5231.   MOV counter,AX
  5232.  
  5233.   PUSH actIndex
  5234.   (* PUSH y *)
  5235.   PUSH ytil
  5236.   PUSH DI
  5237.  
  5238.   MOV AX,xtil
  5239.   CWD
  5240.   SUB AX,XTiles
  5241.   NOT AX
  5242.   OR AX,DX
  5243.   CWD
  5244.   NOT DX
  5245.   MOV Xoffscreen,DX  {ist für Kachelspalte konstant}
  5246.   MOV BX,BytesPerPlane
  5247.  
  5248.  @m1repeat5:
  5249.   MOV SI,actIndex
  5250.   AND SI,Xoffscreen
  5251.   JZ @m1go11
  5252.   MOV AX,ytil
  5253.   CWD
  5254.   SUB AX,YTiles
  5255.   NOT AX
  5256.   OR AX,DX
  5257.   CWD
  5258.   NOT DX
  5259.   AND SI,DX
  5260.  @m1go11:
  5261.     {PROCEDURE DrawLeftTile mit WriteMode1: }
  5262.     { in: ES:DI = ^Zieladresse}
  5263.     {     SI = Kachelindex    }
  5264.     {     BX = BytesPerPlane  }
  5265.     {     leftcut MOD 4 = 0   }
  5266.     {     LINESIZE_sub_BytesPerPlane}
  5267.     {     leftcutDIV4, Win*, SCROLLADR,...}
  5268.     {out: ES:DI = ^Zieladresse der nächsten Kachel darunter}
  5269.     {rem: WriteMode1 ist bereits gesetzt und bleibt gesetzt }
  5270.     {     BX wird nicht verändert}
  5271.     MOV AL,[OFFSET BackTile +SI]
  5272.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  5273.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  5274.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  5275.     MOV SI,AX
  5276.  
  5277.     ADD SI,leftcutDIV4
  5278.     MOV AX,LINESIZE_sub_BytesPerPlane
  5279.     MOV DX,4
  5280.  
  5281.     SUB DX,BX
  5282.     MOV DS,SCROLLADR
  5283.  
  5284.     MOV CX,BX   {1.Zeile}
  5285.     REP MOVSB
  5286.     ADD SI,DX
  5287.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  5288.  
  5289.     MOV CX,BX   {2.Zeile}
  5290.     REP MOVSB
  5291.     ADD SI,DX
  5292.     ADD DI,AX
  5293.  
  5294.     MOV CX,BX   {3.Zeile}
  5295.     REP MOVSB
  5296.     ADD SI,DX
  5297.     ADD DI,AX
  5298.  
  5299.     MOV CX,BX   {4.Zeile}
  5300.     REP MOVSB
  5301.     ADD SI,DX
  5302.     ADD DI,AX
  5303.  
  5304.     MOV CX,BX   {5.Zeile}
  5305.     REP MOVSB
  5306.     ADD SI,DX
  5307.     ADD DI,AX
  5308.  
  5309.     MOV CX,BX   {6.Zeile}
  5310.     REP MOVSB
  5311.     ADD SI,DX
  5312.     ADD DI,AX
  5313.  
  5314.     MOV CX,BX   {7.Zeile}
  5315.     REP MOVSB
  5316.     ADD SI,DX
  5317.     ADD DI,AX
  5318.  
  5319.     MOV CX,BX   {8.Zeile}
  5320.     REP MOVSB
  5321.     ADD SI,DX
  5322.     ADD DI,AX
  5323.  
  5324.     MOV CX,BX   {9.Zeile}
  5325.     REP MOVSB
  5326.     ADD SI,DX
  5327.     ADD DI,AX
  5328.  
  5329.     MOV CX,BX  {10.Zeile}
  5330.     REP MOVSB
  5331.     ADD SI,DX
  5332.     ADD DI,AX
  5333.  
  5334.     MOV CX,BX  {11.Zeile}
  5335.     REP MOVSB
  5336.     ADD SI,DX
  5337.     ADD DI,AX
  5338.  
  5339.     MOV CX,BX  {12.Zeile}
  5340.     REP MOVSB
  5341.     ADD SI,DX
  5342.     ADD DI,AX
  5343.  
  5344.     MOV CX,BX  {13.Zeile}
  5345.     REP MOVSB
  5346.     ADD SI,DX
  5347.     ADD DI,AX
  5348.  
  5349.     MOV CX,BX  {14.Zeile}
  5350.     REP MOVSB
  5351.     ADD SI,DX
  5352.     ADD DI,AX
  5353.  
  5354.     MOV CX,BX  {15.Zeile}
  5355.     REP MOVSB
  5356.     ADD SI,DX
  5357.     ADD DI,AX
  5358.  
  5359.     MOV CX,BX  {16.Zeile}
  5360.     REP MOVSB
  5361.     ADD SI,DX
  5362.     ADD DI,AX
  5363.  
  5364.     MOV AX,SEG @DATA
  5365.     MOV DS,AX
  5366.  
  5367.   {Nächste Kachel; DI steht schon richtig:}
  5368.   MOV AX,XTiles
  5369.   ADD actIndex,AX
  5370.   (* MOV AX,16 *)
  5371.   (* ADD y,AX *)
  5372.   INC ytil
  5373.  
  5374.   DEC counter
  5375.   JNZ @m1repeat5
  5376.  
  5377.   POP DI
  5378.   POP ytil
  5379.   (* POP y *)
  5380.   POP actIndex
  5381.  
  5382.  @m1LeftLoopDone:
  5383.   INC actIndex
  5384.   ADD DI,BytesPerPlane
  5385.   INC xtil
  5386.   (* MOV AX,16 *)
  5387.   (* SUB AX,leftcut *)
  5388.   (* ADD x,AX *)
  5389.  
  5390.  @m1SkipLeftColumn:
  5391.   {ES:DI = ^Zieladresse der ersten inneren Kachel (immer noch)}
  5392.  
  5393.   MOV AX,innerTilesY   {gibt's überhaupt innere Kacheln?}
  5394.   OR AX,AX
  5395.   JBE @m1SkipInnerTiles
  5396.   CMP innerTilesX,0
  5397.   JB @m1SkipInnerTiles
  5398.  
  5399.   MOV counter,AX
  5400.   MOV AX,actIndex     {Kopien der aktuellen Werte anlegen}
  5401.   MOV tempActIndex,AX
  5402.   (* MOV AX,x *)
  5403.   (* MOV tempX,AX *)
  5404.   MOV AX,xtil
  5405.   MOV tempXtil,AX
  5406.   (* MOV AX,y *)
  5407.   (* MOV tempY,AX *)
  5408.   MOV AX,ytil
  5409.   MOV tempYtil,AX
  5410.   MOV tempDI,DI
  5411.  
  5412.   CMP rightcut,0
  5413.   JE @m1SkipRightColumn
  5414.  
  5415.   MOV DX,16
  5416.   SUB DX,rightcut
  5417.   SHR DX,1
  5418.   SHR DX,1
  5419.   MOV BytesPerPlane,DX
  5420.   MOV AX,LINESIZE
  5421.   SUB AX,DX
  5422.   MOV LINESIZE_sub_BytesPerPlane,AX
  5423.  
  5424.   MOV AX,innerTilesX
  5425.   ADD xtil,AX
  5426.   ADD actIndex,AX
  5427.   MOV CL,2
  5428.   SHL AX,CL
  5429.   ADD DI,AX  {ES:DI = ^erste rechte Randkachel, die oben nicht geschnitten ist}
  5430.   (* SHL AX,CL *)
  5431.   (* ADD x,AX *)
  5432.  
  5433.   MOV AX,xtil
  5434.   CWD
  5435.   SUB AX,XTiles
  5436.   NOT AX
  5437.   OR AX,DX
  5438.   CWD
  5439.   NOT DX
  5440.   MOV Xoffscreen,DX
  5441.   MOV BX,BytesPerPlane
  5442.  
  5443.  @m1repeat6:
  5444.   MOV SI,actIndex
  5445.   AND SI,Xoffscreen
  5446.   JZ @m1go12
  5447.   MOV AX,ytil
  5448.   CWD
  5449.   SUB AX,YTiles
  5450.   NOT AX
  5451.   OR AX,DX
  5452.   CWD
  5453.   NOT DX
  5454.   AND SI,DX
  5455.  @m1go12:
  5456.     {PROCEDURE DrawRightTile mit WriteMode1: }
  5457.     { in: ES:DI = ^Zieladresse}
  5458.     {     SI = Kachelindex    }
  5459.     {     BX = BytesPerPlane  }
  5460.     {     rightcut MOD 4 = 0  }
  5461.     {     innerTilesY >= 1    }
  5462.     {     LINESIZE_sub_BytesPerPlane}
  5463.     {     SCROLLADR,...}
  5464.     {out: ES:DI = ^Zieladresse der nächsten Kachel darunter}
  5465.     {rem: WriteMode1 ist bereits gesetzt und bleibt gesetzt }
  5466.     {     BX wird nicht verändert}
  5467.     MOV AL,[OFFSET BackTile +SI]
  5468.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  5469.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  5470.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  5471.     MOV SI,AX
  5472.  
  5473.     MOV AX,LINESIZE_sub_BytesPerPlane
  5474.     MOV DX,4
  5475.  
  5476.     SUB DX,BX
  5477.     MOV DS,SCROLLADR
  5478.  
  5479.     MOV CX,BX   {1.Zeile}
  5480.     REP MOVSB
  5481.     ADD SI,DX
  5482.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  5483.  
  5484.     MOV CX,BX   {2.Zeile}
  5485.     REP MOVSB
  5486.     ADD SI,DX
  5487.     ADD DI,AX
  5488.  
  5489.     MOV CX,BX   {3.Zeile}
  5490.     REP MOVSB
  5491.     ADD SI,DX
  5492.     ADD DI,AX
  5493.  
  5494.     MOV CX,BX   {4.Zeile}
  5495.     REP MOVSB
  5496.     ADD SI,DX
  5497.     ADD DI,AX
  5498.  
  5499.     MOV CX,BX   {5.Zeile}
  5500.     REP MOVSB
  5501.     ADD SI,DX
  5502.     ADD DI,AX
  5503.  
  5504.     MOV CX,BX   {6.Zeile}
  5505.     REP MOVSB
  5506.     ADD SI,DX
  5507.     ADD DI,AX
  5508.  
  5509.     MOV CX,BX   {7.Zeile}
  5510.     REP MOVSB
  5511.     ADD SI,DX
  5512.     ADD DI,AX
  5513.  
  5514.     MOV CX,BX   {8.Zeile}
  5515.     REP MOVSB
  5516.     ADD SI,DX
  5517.     ADD DI,AX
  5518.  
  5519.     MOV CX,BX   {9.Zeile}
  5520.     REP MOVSB
  5521.     ADD SI,DX
  5522.     ADD DI,AX
  5523.  
  5524.     MOV CX,BX  {10.Zeile}
  5525.     REP MOVSB
  5526.     ADD SI,DX
  5527.     ADD DI,AX
  5528.  
  5529.     MOV CX,BX  {11.Zeile}
  5530.     REP MOVSB
  5531.     ADD SI,DX
  5532.     ADD DI,AX
  5533.  
  5534.     MOV CX,BX  {12.Zeile}
  5535.     REP MOVSB
  5536.     ADD SI,DX
  5537.     ADD DI,AX
  5538.  
  5539.     MOV CX,BX  {13.Zeile}
  5540.     REP MOVSB
  5541.     ADD SI,DX
  5542.     ADD DI,AX
  5543.  
  5544.     MOV CX,BX  {14.Zeile}
  5545.     REP MOVSB
  5546.     ADD SI,DX
  5547.     ADD DI,AX
  5548.  
  5549.     MOV CX,BX  {15.Zeile}
  5550.     REP MOVSB
  5551.     ADD SI,DX
  5552.     ADD DI,AX
  5553.  
  5554.     MOV CX,BX  {16.Zeile}
  5555.     REP MOVSB
  5556.     ADD SI,DX
  5557.     ADD DI,AX
  5558.  
  5559.     MOV AX,SEG @DATA
  5560.     MOV DS,AX
  5561.  
  5562.   {Nächste Kachel; DI steht schon richtig:}
  5563.   MOV AX,XTiles
  5564.   ADD actIndex,AX
  5565.   (* MOV AX,16 *)
  5566.   (* ADD y,AX *)
  5567.   INC ytil
  5568.  
  5569.   DEC counter
  5570.   JNZ @m1repeat6
  5571.  
  5572.   MOV DI,tempDI
  5573.   MOV AX,tempActIndex
  5574.   MOV actIndex,AX
  5575.   (* MOV AX,tempX *)
  5576.   (* MOV x,tempX *)
  5577.   MOV AX,tempXtil
  5578.   MOV xtil,AX
  5579.   (* MOV AX,tempY *)
  5580.   (* MOV y,AX *)
  5581.   MOV AX,tempYtil
  5582.   MOV ytil,AX
  5583.  
  5584.  @m1RightLoopDone:
  5585.  @m1SkipRightColumn:
  5586.   {ES:DI = ^Zieladresse der ersten inneren Kachel (immer noch)}
  5587.   {innerTilesX >= 0, innerTilesX >= 1 -> es reichte, innerTilesX=0 zu prüfen:}
  5588.  
  5589.   CMP innerTilesX,0    {IF (innerTilesX <= 0) OR (innerTilesY <= 0) THEN skip}
  5590.   JBE @m1SkipInnerTiles  {Falls keine inneren Kachel existieren, dann ist  }
  5591.                          {stattdessen bereits auf erste links nicht ge-    }
  5592.                          {schnittene Kachel der untersten Kachelzeile pos. }
  5593.  
  5594.  
  5595.   {Nun "FOR x:=1 TO innerTilesX DO FOR y:=1 TO innerTilesY DO .." realisieren}
  5596.   MOV oldDI,DI        {temporäre Kopien von DI und actIndex anlegen}
  5597.   MOV BX,actIndex     {BX entspricht "oldActIndex"}
  5598.  
  5599.   MOV AX,innerTilesX
  5600.   MOV counter,AX  {Zähler für X-Richtung}
  5601.   MOV CL,6
  5602.  
  5603.  @m1xloop:
  5604.   MOV AX,xtil
  5605.   CWD
  5606.   SUB AX,XTiles
  5607.   NOT AX
  5608.   OR AX,DX
  5609.   CWD
  5610.   NOT DX
  5611.   MOV Xoffscreen,DX  {ist für Kachelspalte konstant}
  5612.  
  5613.   MOV AX,innerTilesY
  5614.   MOV CH,AL          {CH dient als Zähler für Y-Richtung}
  5615.  
  5616.  
  5617.  @m1yloop:
  5618.   MOV SI,BX          {SI = temp. actIndex}
  5619.   AND SI,Xoffscreen
  5620.   JZ @m1go5
  5621.   MOV AX,ytil
  5622.   CWD
  5623.   SUB AX,YTiles
  5624.   NOT AX
  5625.   OR AX,DX
  5626.   CWD
  5627.   NOT DX
  5628.   AND SI,DX
  5629.  @m1go5:
  5630.     {PROCEDURE DrawInnerTile mit WriteMode1: }
  5631.     { in: ES:DI = ^Zieladresse}
  5632.     {     SI = Kachelindex    }
  5633.     {     CL = 6              }
  5634.     {     SCROLLADR}
  5635.     {out: ES:DI = ^Zieladresse der nächsten Kachel darunter}
  5636.     {     CL = 6}
  5637.     {rem: WriteMode1 ist bereits gesetzt und bleibt gesetzt }
  5638.     {     CH, BX dürfen nicht verändert werden!}
  5639.     MOV AL,[OFFSET BackTile +SI]
  5640.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  5641.                    {jede Kachel ist 64 Bytes lang, also}
  5642.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  5643.     MOV SI,AX
  5644.  
  5645.     MOV DX,DS   {DS nach DX retten}
  5646.     MOV DS,SCROLLADR
  5647.  
  5648.     MOVSB       {1.Zeile}
  5649.     MOVSB
  5650.     MOVSB
  5651.     MOVSB
  5652.     ADD DI,LINESIZE-4   {ES:DI = ^nächste Kachel}
  5653.  
  5654.     MOVSB       {2.Zeile}
  5655.     MOVSB
  5656.     MOVSB
  5657.     MOVSB
  5658.     ADD DI,LINESIZE-4
  5659.  
  5660.     MOVSB       {3.Zeile}
  5661.     MOVSB
  5662.     MOVSB
  5663.     MOVSB
  5664.     ADD DI,LINESIZE-4
  5665.  
  5666.     MOVSB       {4.Zeile}
  5667.     MOVSB
  5668.     MOVSB
  5669.     MOVSB
  5670.     ADD DI,LINESIZE-4
  5671.  
  5672.     MOVSB       {5.Zeile}
  5673.     MOVSB
  5674.     MOVSB
  5675.     MOVSB
  5676.     ADD DI,LINESIZE-4
  5677.  
  5678.     MOVSB       {6.Zeile}
  5679.     MOVSB
  5680.     MOVSB
  5681.     MOVSB
  5682.     ADD DI,LINESIZE-4
  5683.  
  5684.     MOVSB       {7.Zeile}
  5685.     MOVSB
  5686.     MOVSB
  5687.     MOVSB
  5688.     ADD DI,LINESIZE-4
  5689.  
  5690.     MOVSB       {8.Zeile}
  5691.     MOVSB
  5692.     MOVSB
  5693.     MOVSB
  5694.     ADD DI,LINESIZE-4
  5695.  
  5696.     MOVSB       {9.Zeile}
  5697.     MOVSB
  5698.     MOVSB
  5699.     MOVSB
  5700.     ADD DI,LINESIZE-4
  5701.  
  5702.     MOVSB      {10.Zeile}
  5703.     MOVSB
  5704.     MOVSB
  5705.     MOVSB
  5706.     ADD DI,LINESIZE-4
  5707.  
  5708.     MOVSB      {11.Zeile}
  5709.     MOVSB
  5710.     MOVSB
  5711.     MOVSB
  5712.     ADD DI,LINESIZE-4
  5713.  
  5714.     MOVSB      {12.Zeile}
  5715.     MOVSB
  5716.     MOVSB
  5717.     MOVSB
  5718.     ADD DI,LINESIZE-4
  5719.  
  5720.     MOVSB      {13.Zeile}
  5721.     MOVSB
  5722.     MOVSB
  5723.     MOVSB
  5724.     ADD DI,LINESIZE-4
  5725.  
  5726.     MOVSB      {14.Zeile}
  5727.     MOVSB
  5728.     MOVSB
  5729.     MOVSB
  5730.     ADD DI,LINESIZE-4
  5731.  
  5732.     MOVSB      {15.Zeile}
  5733.     MOVSB
  5734.     MOVSB
  5735.     MOVSB
  5736.     ADD DI,LINESIZE-4
  5737.  
  5738.     MOVSB      {16.Zeile}
  5739.     MOVSB
  5740.     MOVSB
  5741.     MOVSB
  5742.     ADD DI,LINESIZE-4
  5743.  
  5744.  
  5745.     MOV DS,DX
  5746.  
  5747.   {Nächste Kachel; DI steht schon richtig:}
  5748.   ADD BX,XTiles     {temp. actIndex in nächste Zeile setzen}
  5749.   INC ytil
  5750.   (* MOV AX,16 *)
  5751.   (* ADD y,AX *)
  5752.  
  5753.   DEC CH
  5754.   JNZ @m1yloop
  5755.  
  5756.   {actIndex hat noch seinen alten Wert, da nur "oldActIndex" verändert wurde.}
  5757.   INC actIndex        {actIndex = nächste innere Kachel in oberster Kachelzeile}
  5758.   MOV BX,actIndex     {und als Startwert für nächste Spalte übernehmen}
  5759.  
  5760.   MOV DI,oldDI        {ES:DI = ^innere Kachel in oberster Kachelzeile}
  5761.   ADD DI,4            {eine Kachel weitersetzen}
  5762.   MOV oldDI,DI        {und als Startwert für nächste Spalte übernehmen}
  5763.  
  5764.   MOV AX,tempYtil
  5765.   MOV ytil,AX     {Y-Koordinate wieder auf oberste innere Kachelzeile setzen}
  5766.   (* MOV AX,oldY *)
  5767.   (* MOV y,AX *)
  5768.  
  5769.   INC xtil      {X-Koordinate eine Kachelspalte weitersetzen}
  5770.   (* MOV AX,16 *)
  5771.   (* ADD x,AX *)
  5772.  
  5773.   DEC counter
  5774.   JNZ @m1xloop
  5775.  
  5776.   MOV DI,tempDI       {Damit: ES:DI, actIndex, xtil, ytil, x, y zeigen wieder}
  5777.   MOV AX,tempActIndex {auf die erste, innere Kachel  (N.B.: y, ytil wurden   }
  5778.   MOV actIndex,AX     {bereits weiter oben wiederhergestellt)}
  5779.   MOV AX,tempXtil
  5780.   MOV xtil,AX
  5781.   (* MOV AX,tempX *)
  5782.   (* MOV x,AX *)
  5783.  
  5784.   MOV AX,innerTilesY
  5785.   MOV DX,AX     {Kopie in DX aufheben}
  5786.   ADD ytil,AX   {ytil zeigt jetzt auf unterste Kachelzeile}
  5787.  
  5788.   MOV CL,5
  5789.   SHL AX,CL     {dto. für DI: inc(DI,16 * innerTilesY * LINESIZE) }
  5790.   MOV BX,AX
  5791.   ADD DI,CS:[OFFSET GADR +BX]
  5792.   (* SHR AX,1 *)
  5793.   (* ADD y,AX *)  {dto. für y: inc(y,16 * innerTilesY) }
  5794.  
  5795.   MOV AX,XTiles
  5796.   MUL DX          {AX := XTiles * innerTilesY}
  5797.   ADD actIndex,AX {dto. für actIndex: inc(actIndex,XTiles * innerTilesY) }
  5798.  
  5799.  @m1SkipInnerTiles:
  5800.   {ES:DI, actIndex, xtil, ytil, x, y zeigen auf erste innere Kachel der}
  5801.   {untersten Kachelzeile}
  5802.   CMP bottomcut,0
  5803.   JE @m1fertig
  5804.  
  5805.   MOV AX,ytil
  5806.   CWD
  5807.   SUB AX,YTiles
  5808.   NOT AX
  5809.   OR AX,DX
  5810.   CWD
  5811.   NOT DX
  5812.   MOV Yoffscreen,DX
  5813.  
  5814.   MOV AX,innerTilesX
  5815.   OR AX,AX
  5816.   JBE @m1LowerInnerTilesDone {stehen wir bereits auf rechter unterer Eckkachel?}
  5817.   MOV counter,AX
  5818.  
  5819.   {Additionsfaktor berechnen, um von unten nach oben zu kommen:}
  5820.   {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  5821.   {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  5822.   {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  5823.   { ▀                      }
  5824.   {BX := -(16 - bottomcut) * LINESIZE + 4}
  5825.   MOV BX,16
  5826.   SUB BX,bottomcut
  5827.   SHL BX,1
  5828.   MOV BX,CS:[OFFSET GADR +BX]
  5829.   NEG BX
  5830.   ADD BX,4
  5831.  
  5832.  @m1repeat4:
  5833.   MOV SI,actIndex
  5834.   AND SI,Yoffscreen
  5835.   JZ @m1go8
  5836.  
  5837.   MOV AX,xtil
  5838.   CWD
  5839.   SUB AX,XTiles
  5840.   NOT AX
  5841.   OR AX,DX
  5842.   CWD
  5843.   NOT DX
  5844.   AND SI,DX
  5845.  @m1go8:
  5846.     {PROCEDURE DrawLowerInnerTile mit WriteMode1: }
  5847.     { in: ES:DI = ^Zieladresse}
  5848.     {     SI = Kachelindex    }
  5849.     {     BX = Korrekturfaktor für Zeilenadressen }
  5850.     {     bottomcut, Win*, SCROLLADR,...}
  5851.     {out: ES:DI = ^Zieladresse der nächsten Kachel rechts davon}
  5852.     {     BX = Korrekturfaktor für Zeilenadressen }
  5853.     {rem: WriteMode1 ist bereits gesetzt und bleibt gesetzt}
  5854.     {     DX wird nicht benutzt}
  5855.     MOV AL,[OFFSET BackTile +SI]
  5856.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  5857.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  5858.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  5859.     MOV SI,AX
  5860.  
  5861.     MOV CX,16
  5862.     SUB CX,bottomcut
  5863.  
  5864.     MOV AX,DS   {DS nach AX retten}
  5865.     MOV DS,SCROLLADR
  5866.  
  5867.    @m1eineZeile4e1:
  5868.     MOVSB
  5869.     MOVSB
  5870.     MOVSB
  5871.     MOVSB
  5872.     ADD DI,LINESIZE-4
  5873.     LOOP @m1eineZeile4e1
  5874.  
  5875.     MOV DS,AX
  5876.  
  5877.     {DI = ^Start der Zeile unterhalb der Kachel, jetzt auf Start der nächsten}
  5878.     {Kachel setzen:}
  5879.     {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  5880.     {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  5881.     {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  5882.     { ▀                      }
  5883.     ADD DI,BX
  5884.  
  5885.   {Auf nächste Kachel rechts davon positionieren:}
  5886.   INC actIndex
  5887.   INC xtil
  5888.   (* MOV AX,16 *)
  5889.   (* ADD x,AX *)
  5890.  
  5891.   DEC counter
  5892.   JNZ @m1repeat4
  5893.  
  5894.  @m1LowerInnerTilesDone:
  5895.   {ES:DI, actIndex, xtil, ytil, x, y zeigen auf untere rechte Eckkachel}
  5896.   CMP rightcut,0
  5897.   JE @m1SkipLowerRightCorner
  5898.  
  5899.   PUSH DI
  5900.   MOV SI,actIndex
  5901.   AND SI,Yoffscreen
  5902.   JZ @m1go9
  5903.   MOV AX,xtil
  5904.   CWD
  5905.   SUB AX,XTiles
  5906.   NOT AX
  5907.   OR AX,DX
  5908.   CWD
  5909.   NOT DX
  5910.   AND SI,DX
  5911.  @m1go9:
  5912.     {PROCEDURE DrawLowerRightTile mit WriteMode1: }
  5913.     { in: ES:DI = ^Zieladresse}
  5914.     {     SI = Kachelindex    }
  5915.     {     rightcut MOD 4 = 0  }
  5916.     {     rightcut, bottomcut, Win*, SCROLLADR,...}
  5917.     {out: ES = ^Grafiksegment }
  5918.     {rem: WriteMode1 ist bereits gesetzt und bleibt gesetzt}
  5919.     MOV AL,[OFFSET BackTile +SI]
  5920.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  5921.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  5922.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  5923.     MOV SI,AX
  5924.  
  5925.     MOV CX,16
  5926.     SUB CX,bottomcut
  5927.     MOV BX,16
  5928.     SUB BX,rightcut
  5929.     SHR BX,1
  5930.     SHR BX,1  {BX = BytesPerPlane = (16 - rightcut) DIV 4}
  5931.  
  5932.     MOV DS,SCROLLADR
  5933.  
  5934.     MOV AX,LINESIZE
  5935.     SUB AX,BX
  5936.     MOV DX,4
  5937.     SUB DX,BX
  5938.     PUSH BP
  5939.     MOV BP,CX  {BP=Zeilenzähler}
  5940.  
  5941.    @m1eineZeile4g1:
  5942.     MOV CX,BX
  5943.     REP MOVSB
  5944.     ADD SI,DX
  5945.     ADD DI,AX
  5946.     DEC BP
  5947.     JNZ @m1eineZeile4g1
  5948.  
  5949.     POP BP
  5950.     MOV AX,SEG @DATA
  5951.     MOV DS,AX
  5952.  
  5953.   POP DI   {ES:DI etc. zeigen auf rechte untere Eckkachel}
  5954.  
  5955.  @m1SkipLowerRightCorner:
  5956.   CMP leftcut,0
  5957.   JE @m1fertig
  5958.  
  5959.   {jetzt auf linke untere Eckkachel positionieren:}
  5960.   MOV AX,innerTilesX
  5961.   INC AX
  5962.   SUB actIndex,AX  {dec(actIndex,innerTilesX + 1) }
  5963.   SUB xtil,AX      {dec(xtil,innerTilesX + 1) }
  5964.   MOV CL,2
  5965.   SHL AX,CL
  5966.   SUB DI,AX        {dec(DI,4 * (innerTilesX + 1) }
  5967.   ADD DI,leftcutDIV4 {berücksichtige: Eckkachel kann links geschnitten sein}
  5968.   (* MOV AX,WinXMIN *)
  5969.   (* MOV x,AX *)
  5970.  
  5971.   MOV SI,actIndex
  5972.   AND SI,Yoffscreen
  5973.   JZ @m1go7
  5974.   MOV AX,xtil
  5975.   CWD
  5976.   SUB AX,XTiles
  5977.   NOT AX
  5978.   OR AX,DX
  5979.   CWD
  5980.   NOT DX
  5981.   AND SI,DX
  5982.  @m1go7:
  5983.     {PROCEDURE DrawLowerLeftTile mit WriteMode1: }
  5984.     { in: ES:DI = ^Zieladresse}
  5985.     {     SI = Kachelindex    }
  5986.     {     rightcut MOD 4 = 0  }
  5987.     {     leftcut, bottomcut, Win*, SCROLLADR,...}
  5988.     {out: (ES = ^Grafiksegment) }
  5989.     {rem: WriteMode1 ist bereits gesetzt (und bleibt gesetzt)}
  5990.     MOV AL,[OFFSET BackTile +SI]
  5991.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  5992.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  5993.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  5994.     MOV SI,AX
  5995.  
  5996.     MOV AX,leftcut
  5997.     MOV BX,AX
  5998.     MOV CL,2
  5999.     SHR AX,CL
  6000.     ADD SI,AX
  6001.  
  6002.     MOV CX,16
  6003.     SUB CX,bottomcut
  6004.  
  6005.     MOV DS,SCROLLADR
  6006.  
  6007.     PUSH BP
  6008.     MOV BP,16
  6009.     SUB BP,BX   
  6010.     SHR BP,1
  6011.     SHR BP,1    {BP:=(16 - leftcut) DIV 4}
  6012.  
  6013.     MOV AX,LINESIZE
  6014.     SUB AX,BP
  6015.     MOV DX,4
  6016.     SUB DX,BP
  6017.  
  6018.     MOV BX,CX   {BX = Zeilenzähler}
  6019.  
  6020.    @m1eineZeile4d1:
  6021.     MOV CX,BP
  6022.     REP MOVSB
  6023.     ADD SI,DX
  6024.     ADD DI,AX
  6025.     DEC BX
  6026.     JNZ @m1eineZeile4d1
  6027.  
  6028.     POP BP
  6029.     MOV AX,SEG @DATA
  6030.     MOV DS,AX
  6031.  
  6032.  @m1fertig:
  6033.   {Jetzt wieder auf WriteMode0 zurückschalten:}
  6034.   MOV AX,4005h
  6035.   MOV DX,3CEh
  6036.   OUT DX,AX
  6037.   JMP @Sprites_zeichnen
  6038.  
  6039.  {----------------------------------------------------}
  6040.  
  6041.  @useMode0:
  6042.   CMP topcut,0      {IF ytil ε [0..YTiles(          }
  6043.   JE @m0SkipTopRow  { THEN DX = Yoffscreen := $FFFF }
  6044.   MOV AX,ytil       { ELSE DX = Yoffscreen := $0000 }
  6045.   CWD
  6046.   SUB AX,YTiles
  6047.   NOT AX
  6048.   OR AX,DX
  6049.   CWD
  6050.   NOT DX
  6051.   MOV Yoffscreen,DX
  6052.  
  6053.   MOV AX,WinXMINdiv4  {AX = richtiger Wert, falls gesprungen wird!}
  6054.   OR BX,BX   {BX=leftcut}
  6055.   JZ @m0SkipTopLeftCorner
  6056.  
  6057.   MOV SI,actIndex     {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
  6058.   AND SI,DX           { THEN SI := 0 }
  6059.   JZ @m0go1           { ELSE SI := actIndex }
  6060.   MOV AX,CX  {CX = xtil}
  6061.   CWD
  6062.   SUB AX,XTiles
  6063.   NOT AX
  6064.   OR AX,DX
  6065.   CWD
  6066.   NOT DX
  6067.   AND SI,DX
  6068.  
  6069.  @m0go1:
  6070.     {PROCEDURE DrawUpperLeftTile mit WriteMode0: }
  6071.     { in: WinXMIN,WinYMIN = Bildschirmkoordinaten}
  6072.     {     ES = ^Grafiksegment}
  6073.     {     SI = Kachelindex   }
  6074.     {     BX = leftcut       }
  6075.     {     CX = xtil          }
  6076.     {     topcut, Win*, SCROLLADR,...}
  6077.     {out: ES = ^Grafiksegment}
  6078.     {rem: WriteMode0 ist bereits gesetzt und bleibt gesetzt}
  6079.     MOV AL,[OFFSET BackTile +SI]
  6080.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  6081.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  6082.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  6083.  
  6084.     MOV SI,topcut  {Dazu kommen die oben abgeschnittenen Zeilen:}
  6085.     MOV CX,16      {für jede Zeile 4 Bytes}
  6086.     SUB CX,SI      {CX := 16 - topcut = zu zeichnende Zeilen}
  6087.     SHL SI,1
  6088.     SHL SI,1
  6089.     ADD SI,AX      {SI = Zeiger auf erste zu kopierende Tile*zeile*}
  6090.  
  6091.     MOV AX,BX      {BX = leftcut}
  6092.     SHR AX,1       {SI um linken cutoff weitersetzen = leftcut DIV 4}
  6093.     SHR AX,1
  6094.     ADD SI,AX      {SI = Zeiger auf erstes zu kopierendes Tile*byte*}
  6095.  
  6096.     MOV DI,WinYMINmLINESIZEaWinXMINdiv4 {ES:DI = Zieladresse}
  6097.     MOV DS,SCROLLADR  {DS:SI = Quelladresse}
  6098.     {Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
  6099.     {verwendet werden!}
  6100.     PUSH BP        {wird beim Verlassen der Prozedur gebraucht!}
  6101.     MOV BP,16+3    {BP:=(16 + 3 - leftcut) DIV 4 = Bytes je Kachelzeile}
  6102.     SUB BP,BX
  6103.     PUSH BP        {BP für nächste Plane merken}
  6104.     SHR BP,1
  6105.     SHR BP,1
  6106.  
  6107.     MOV AH,BL      {BL = leftcut}
  6108.     AND AH,3
  6109.     MOV AL,4
  6110.     MOV DX,3CEh
  6111.     OUT DX,AX      {erste Leseplane wählen}
  6112.     PUSH AX
  6113.     MOV DX,3C4h
  6114.     MOV AX,0102h
  6115.     OUT DX,AX      {Schreibplane 0 wählen}
  6116.  
  6117.     MOV AX,LINESIZE  {ist eine Konstante}
  6118.     SUB AX,BP
  6119.     MOV DX,4
  6120.     SUB DX,BP
  6121.     MOV BX,CX      {CX = Zeilenanzahl}
  6122.  
  6123.     PUSH SI
  6124.     PUSH DI
  6125.     PUSH BX
  6126.    @m0eineZeile4a1:
  6127.     MOV CX,BP
  6128.     REP MOVSB
  6129.     ADD DI,AX
  6130.     ADD SI,DX
  6131.     DEC BX
  6132.     JNZ @m0eineZeile4a1
  6133.     POP BX
  6134.     POP DI
  6135.     POP SI
  6136.  
  6137.     MOV DX,3C4h
  6138.     MOV AX,0202h
  6139.     OUT DX,AX      {Schreibplane 1 wählen}
  6140.     MOV DX,3CEh
  6141.     POP AX
  6142.     INC AH
  6143.     AND AH,3
  6144.     JNE @nowrap1a
  6145.     INC SI
  6146.    @nowrap1a:
  6147.     OUT DX,AX      {nächste Leseplane wählen}
  6148.     POP BP         {BP = 16 + 3 - leftcut }
  6149.     DEC BP
  6150.     PUSH BP
  6151.     PUSH AX
  6152.     SHR BP,1
  6153.     SHR BP,1
  6154.  
  6155.     MOV AX,LINESIZE  {ist eine Konstante}
  6156.     SUB AX,BP
  6157.     MOV DX,4
  6158.     SUB DX,BP
  6159.  
  6160.     PUSH SI
  6161.     PUSH DI
  6162.     PUSH BX
  6163.    @m0eineZeile4a2:
  6164.     MOV CX,BP
  6165.     REP MOVSB
  6166.     ADD DI,AX
  6167.     ADD SI,DX
  6168.     DEC BX
  6169.     JNZ @m0eineZeile4a2
  6170.     POP BX
  6171.     POP DI
  6172.     POP SI
  6173.  
  6174.     MOV DX,3C4h
  6175.     MOV AX,0402h
  6176.     OUT DX,AX      {Schreibplane 2 wählen}
  6177.     MOV DX,3CEh
  6178.     POP AX
  6179.     INC AH
  6180.     AND AH,3
  6181.     JNE @nowrap2a
  6182.     INC SI
  6183.    @nowrap2a:
  6184.     OUT DX,AX      {nächste Leseplane wählen}
  6185.     POP BP         {BP = 16 + 2 - leftcut }
  6186.     DEC BP
  6187.     PUSH BP
  6188.     PUSH AX
  6189.     SHR BP,1
  6190.     SHR BP,1
  6191.  
  6192.     MOV AX,LINESIZE  {ist eine Konstante}
  6193.     SUB AX,BP
  6194.     MOV DX,4
  6195.     SUB DX,BP
  6196.  
  6197.     PUSH SI
  6198.     PUSH DI
  6199.     PUSH BX
  6200.    @m0eineZeile4a3:
  6201.     MOV CX,BP
  6202.     REP MOVSB
  6203.     ADD DI,AX
  6204.     ADD SI,DX
  6205.     DEC BX
  6206.     JNZ @m0eineZeile4a3
  6207.     POP BX
  6208.     POP DI
  6209.     POP SI
  6210.  
  6211.     MOV DX,3C4h
  6212.     MOV AX,0802h
  6213.     OUT DX,AX      {Schreibplane 3 wählen}
  6214.     MOV DX,3CEh
  6215.     POP AX
  6216.     INC AH
  6217.     AND AH,3
  6218.     JNE @nowrap3a
  6219.     INC SI
  6220.    @nowrap3a:
  6221.     OUT DX,AX      {nächste Leseplane wählen}
  6222.     POP BP         {BP = 16+ 1 - leftcut }
  6223.     DEC BP
  6224.  
  6225.  
  6226.     SHR BP,1
  6227.     SHR BP,1
  6228.  
  6229.     MOV AX,LINESIZE  {ist eine Konstante}
  6230.     SUB AX,BP
  6231.     MOV DX,4
  6232.     SUB DX,BP
  6233.  
  6234.    @m0eineZeile4a4:
  6235.     MOV CX,BP
  6236.     REP MOVSB
  6237.     ADD DI,AX
  6238.     ADD SI,DX
  6239.     DEC BX
  6240.     JNZ @m0eineZeile4a4
  6241.  
  6242.     POP BP
  6243.     MOV AX,SEG @DATA
  6244.     MOV DS,AX
  6245.  
  6246.   {Auf nächste Kachel rechts davon positionieren:}
  6247.   INC actIndex
  6248.   INC xtil
  6249.  
  6250.   MOV AX,WinXMIN
  6251.   ADD AX,16
  6252.   SUB AX,leftcut
  6253.   (* MOV x,AX *)
  6254.   SHR AX,1
  6255.   SHR AX,1
  6256.  @m0SkipTopLeftCorner:
  6257.   ADD AX,WinYMIN_mul_LINESIZE
  6258.   MOV DI,AX   {ES:DI = ^Zieladresse}
  6259.  
  6260.   {Nun innerTilesX nur oben geschnittene Kacheln zeichnen:}
  6261.   MOV AX,innerTilesX
  6262.   OR AX,AX
  6263.   JBE @m0UpperInnerTilesDone
  6264.   MOV counter,AX
  6265.  
  6266.   MOV BX,16        {Korrekturfaktor, um DI einer Kachelzeile hoch und eine}
  6267.   SUB BX,topcut    {Kachelspalte weiterzusetzen}
  6268.   SHL BX,1
  6269.   MOV AX,CS:[OFFSET GADR +BX]
  6270.   NEG AX
  6271.   ADD AX,4
  6272.   MOV Korrektur,AX {Korrektur := -(16 - topcut) * LINESIZE + 4}
  6273.  
  6274.  @m0repeat1:
  6275.   MOV SI,actIndex   {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
  6276.   AND SI,Yoffscreen { THEN SI := 0        }
  6277.   JZ @m0go2         { ELSE SI := actIndex }
  6278.   MOV AX,xtil
  6279.   CWD
  6280.   SUB AX,XTiles
  6281.   NOT AX
  6282.   OR AX,DX
  6283.   CWD
  6284.   NOT DX
  6285.   AND SI,DX
  6286.  @m0go2:
  6287.     {PROCEDURE DrawUpperInnerTile mit WriteMode0: }
  6288.     { in: ES:DI = ^Zieladresse}
  6289.     {     SI = Kachelindex    }
  6290.     {     topcut, Win*, SCROLLADR,...}
  6291.     {out: ES:DI = ^Zieladresse der nächsten Kachel rechts davon}
  6292.     {rem: WriteMode0 ist bereits gesetzt und bleibt gesetzt}
  6293.     MOV AL,[OFFSET BackTile +SI]
  6294.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  6295.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  6296.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  6297.  
  6298.     MOV SI,topcut  {Dazu kommen die oben abgeschnittenen Zeilen:}
  6299.     MOV CX,16      {für jede Zeile 4 Bytes}
  6300.     SUB CX,SI      {CX := 16 - topcut = zu zeichnende Zeilen}
  6301.     SHL SI,1
  6302.     SHL SI,1
  6303.     ADD SI,AX      {SI = Zeiger auf erste zu kopierende Tile*zeile*}
  6304.  
  6305.     PUSH BP        {BP retten}
  6306.     MOV DX,3C4h
  6307.     MOV AX,StartWritePlane
  6308.     OUT DX,AX      {erste Schreibplane wählen}
  6309.     PUSH AX
  6310.     MOV DX,3CEh
  6311.     MOV AX,0004h
  6312.     OUT DX,AX      {Leseplane 0 wählen}
  6313.  
  6314.     MOV DS,SCROLLADR   {jetzt frei: AX, BX, BP, DX}
  6315.  
  6316.     MOV BX,CX      {Zeilenzählerkopie}
  6317.     MOV BP,SI      {BP = Kopie von SI}
  6318.     MOV AX,DI      {AX = Kopie von DI}
  6319.    @m0eineZeile4b1:
  6320.     MOVSW
  6321.     MOVSW
  6322.     ADD DI,LINESIZE-4
  6323.     LOOP @m0eineZeile4b1
  6324.     MOV SI,BP      {alte Werte wiederherstellen}
  6325.     MOV DI,AX
  6326.     MOV CX,BX
  6327.  
  6328.     MOV AX,0104h
  6329.     OUT DX,AX      {DX = 3CEh -> Leseplane 1 wählen}
  6330.     MOV DX,3C4h
  6331.     POP AX
  6332.     SHL AH,1
  6333.     CMP AH,16
  6334.     JNE @nowrap1b
  6335.     MOV AH,1
  6336.     INC DI
  6337.    @nowrap1b:
  6338.     OUT DX,AX      {nächste Schreibplane wählen}
  6339.     PUSH AX
  6340.     MOV AX,DI
  6341.  
  6342.    @m0eineZeile4b2:
  6343.     MOVSW
  6344.     MOVSW
  6345.     ADD DI,LINESIZE-4
  6346.     LOOP @m0eineZeile4b2
  6347.     MOV SI,BP      {alte Werte wiederherstellen}
  6348.     MOV DI,AX
  6349.     MOV CX,BX
  6350.  
  6351.     POP AX
  6352.     SHL AH,1
  6353.     CMP AH,16
  6354.     JNE @nowrap2b
  6355.     MOV AH,1
  6356.     INC DI
  6357.    @nowrap2b:
  6358.     OUT DX,AX      {DX = 3C4h -> nächste Schreibplane wählen}
  6359.     PUSH AX
  6360.     MOV DX,3CEh
  6361.     MOV AX,0204h
  6362.     OUT DX,AX      {Leseplane 2 wählen}
  6363.     MOV AX,DI
  6364.  
  6365.    @m0eineZeile4b3:
  6366.     MOVSW
  6367.     MOVSW
  6368.     ADD DI,LINESIZE-4
  6369.     LOOP @m0eineZeile4b3
  6370.     MOV SI,BP      {alte Werte wiederherstellen}
  6371.     MOV DI,AX
  6372.     MOV CX,BX
  6373.  
  6374.     MOV AX,0304h
  6375.     OUT DX,AX      {DX = 3CEh -> Leseplane 3 wählen}
  6376.     MOV DX,3C4h
  6377.     POP AX
  6378.     SHL AH,1
  6379.     CMP AH,16
  6380.     JNE @nowrap3b
  6381.     MOV AH,1
  6382.     INC DI
  6383.    @nowrap3b:
  6384.     OUT DX,AX      {letzte Schreibplane wählen: keine PUSHs mehr}
  6385.  
  6386.    @m0eineZeile4b4:
  6387.     MOVSW
  6388.     MOVSW
  6389.     ADD DI,LINESIZE-4
  6390.     LOOP @m0eineZeile4b4
  6391.                    {alte Werte nicht wiederherstellen}
  6392.  
  6393.     POP BP         {TP zufriedenstellen}
  6394.     MOV AX,SEG @Data
  6395.     MOV DS,AX
  6396.  
  6397.     {DI = ^Start der Zeile unterhalb der Kachel, jetzt auf Start der nächsten}
  6398.     {Kachel setzen:}
  6399.     {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  6400.     {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  6401.     {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  6402.     { ▀                      }
  6403.     ADD DI,Korrektur
  6404.     {-1, weil exakt einmal "inc di" ausgeführt wurde!}
  6405.     DEC DI
  6406.  
  6407.   {Auf nächste Kachel rechts davon positionieren:}
  6408.   INC actIndex
  6409.   INC xtil
  6410.   (* MOV AX,16 *)
  6411.   (* ADD x,AX *)
  6412.   DEC counter
  6413.   JNZ @m0repeat1
  6414.  
  6415.  @m0UpperInnerTilesDone:
  6416.   {ES:DI = ^erste Zeile der rechten oberen Eckkachel}
  6417.   CMP rightcut,0
  6418.   JE @m0SkipTopRightCorner
  6419.  
  6420.   MOV SI,actIndex   {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
  6421.   AND SI,Yoffscreen { THEN SI := 0        }
  6422.   JZ @m0go3         { ELSE SI := actIndex }
  6423.   MOV AX,xtil
  6424.   CWD
  6425.   SUB AX,XTiles
  6426.   NOT AX
  6427.   OR AX,DX
  6428.   CWD
  6429.   NOT DX
  6430.   AND SI,DX
  6431.  @m0go3:
  6432.     {PROCEDURE DrawUpperRightTile mit WriteMode0: }
  6433.     { in: ES:DI = ^Zieladresse}
  6434.     {     SI = Kachelindex    }
  6435.     {     StartWritePlane = erste zu beschreibende Bitplane}
  6436.     {     topcut, rightcut, Win*, SCROLLADR,...}
  6437.     {out: ES = ^Grafiksegment }
  6438.     {rem: WriteMode0 ist bereits gesetzt und bleibt gesetzt}
  6439.     MOV AL,[OFFSET BackTile +SI]
  6440.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  6441.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  6442.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  6443.  
  6444.     MOV SI,topcut  {Dazu kommen die oben abgeschnittenen Zeilen:}
  6445.     MOV CX,16      {für jede Zeile 4 Bytes}
  6446.     SUB CX,SI      {CX := 16 - topcut = zu zeichnende Zeilen}
  6447.     SHL SI,1
  6448.     SHL SI,1
  6449.     ADD SI,AX      {SI = Zeiger auf erste zu kopierende Tile*zeile*}
  6450.  
  6451.     MOV BX,rightcut
  6452.     PUSH BP        {wird beim Verlassen der Prozedur gebraucht!}
  6453.     MOV DS,SCROLLADR  {DS:SI = Quelladresse}
  6454.     MOV DX,3C4h
  6455.     MOV AX,StartWritePlane
  6456.     OUT DX,AX
  6457.     PUSH AX
  6458.     MOV DX,3CEh
  6459.     MOV AX,0004h
  6460.     OUT DX,AX
  6461.     {Jetzt wird keine Variable aus dem Stack mehr gebraucht: BP kann}
  6462.     {verwendet werden!}
  6463.     MOV BP,16+3    {BP:=(16 + 3 - rightcut) DIV 4 = Bytes je Kachelzeile}
  6464.     SUB BP,BX
  6465.     PUSH BP
  6466.     SHR BP,1
  6467.     SHR BP,1
  6468.  
  6469.     MOV AX,LINESIZE
  6470.     SUB AX,BP
  6471.     MOV DX,4
  6472.     SUB DX,BP
  6473.     MOV BX,CX      {BX := zu zeichnende Zeilen}
  6474.  
  6475.     PUSH BX
  6476.     PUSH SI
  6477.     PUSH DI
  6478.    @m0eineZeile4c1:
  6479.     MOV CX,BP
  6480.     REP MOVSB
  6481.     ADD DI,AX
  6482.     ADD SI,DX
  6483.     DEC BX
  6484.     JNZ @m0eineZeile4c1
  6485.     POP DI
  6486.     POP SI
  6487.     POP BX
  6488.     POP BP
  6489.     POP AX
  6490.  
  6491.     SHL AH,1
  6492.     CMP AH,16
  6493.     JNE @nowrap1c
  6494.     MOV AH,1
  6495.     INC DI
  6496.    @nowrap1c:
  6497.     MOV DX,3C4h
  6498.     OUT DX,AX
  6499.     PUSH AX
  6500.     MOV DX,3CEh
  6501.     MOV AX,0104h
  6502.     OUT DX,AX
  6503.  
  6504.     DEC BP         {BP := 16 + 2 - rightcut}
  6505.     PUSH BP
  6506.     SHR BP,1
  6507.     SHR BP,1
  6508.     MOV AX,LINESIZE
  6509.     SUB AX,BP
  6510.     MOV DX,4
  6511.     SUB DX,BP
  6512.  
  6513.     PUSH BX
  6514.     PUSH SI
  6515.     PUSH DI
  6516.    @m0eineZeile4c2:
  6517.     MOV CX,BP
  6518.     REP MOVSB
  6519.     ADD DI,AX
  6520.     ADD SI,DX
  6521.     DEC BX
  6522.     JNZ @m0eineZeile4c2
  6523.     POP DI
  6524.     POP SI
  6525.     POP BX
  6526.     POP BP
  6527.     POP AX
  6528.  
  6529.     SHL AH,1
  6530.     CMP AH,16
  6531.     JNE @nowrap2c
  6532.     MOV AH,1
  6533.     INC DI
  6534.    @nowrap2c:
  6535.     MOV DX,3C4h
  6536.     OUT DX,AX
  6537.     PUSH AX
  6538.     MOV DX,3CEh
  6539.     MOV AX,0204h
  6540.     OUT DX,AX
  6541.  
  6542.     DEC BP         {BP := 16 + 1 - rightcut}
  6543.     PUSH BP
  6544.     SHR BP,1
  6545.     SHR BP,1
  6546.     MOV AX,LINESIZE
  6547.     SUB AX,BP
  6548.     MOV DX,4
  6549.     SUB DX,BP
  6550.  
  6551.     PUSH BX
  6552.     PUSH SI
  6553.     PUSH DI
  6554.    @m0eineZeile4c3:
  6555.     MOV CX,BP
  6556.     REP MOVSB
  6557.     ADD DI,AX
  6558.     ADD SI,DX
  6559.     DEC BX
  6560.     JNZ @m0eineZeile4c3
  6561.     POP DI
  6562.     POP SI
  6563.     POP BX
  6564.     POP BP
  6565.     POP AX
  6566.  
  6567.     SHL AH,1
  6568.     CMP AH,16
  6569.     JNE @nowrap3c
  6570.     MOV AH,1
  6571.     INC DI
  6572.    @nowrap3c:
  6573.     MOV DX,3C4h
  6574.     OUT DX,AX
  6575.  
  6576.     MOV DX,3CEh
  6577.     MOV AX,0304h
  6578.     OUT DX,AX
  6579.  
  6580.     DEC BP         {BP := 16 + 0 - rightcut}
  6581.  
  6582.     SHR BP,1
  6583.     SHR BP,1
  6584.     MOV AX,LINESIZE
  6585.     SUB AX,BP
  6586.     MOV DX,4
  6587.     SUB DX,BP
  6588.  
  6589.    @m0eineZeile4c4:
  6590.     MOV CX,BP
  6591.     REP MOVSB
  6592.     ADD DI,AX
  6593.     ADD SI,DX
  6594.     DEC BX
  6595.     JNZ @m0eineZeile4c4
  6596.  
  6597.  
  6598.     POP BP
  6599.     MOV AX,SEG @DATA
  6600.     MOV DS,AX
  6601.  
  6602.  @m0SkipTopRightCorner:
  6603.   {Auf erste linke Kachel positionieren, die oben nicht mehr geschnitten ist:}
  6604.   MOV AX,stepx2
  6605.   ADD actIndex,AX
  6606.   (* MOV AX,WinXMIN *)
  6607.   (* MOV x,AX *)
  6608.   MOV AX,KachelnWegLinks
  6609.   MOV xtil,AX
  6610.   INC ytil
  6611.  
  6612.  @m0SkipTopRow:
  6613.   MOV AX,topcut  {IF topcut = 0 }
  6614.   NEG AX         { THEN AX = y := WinYMIN}
  6615.   JZ @m0l1       { ELSE AX = y := WinYMIN + (16 - topcut)}
  6616.   ADD AX,16
  6617.  @m0l1:
  6618.   ADD AX,WinYMIN
  6619.   (* MOV y,AX *)
  6620.  
  6621.   MOV DI,AX      {DI := y * LINESIZE +X DIV 4}
  6622.   SHL DI,1
  6623.   MOV DI,CS:[OFFSET GADR +DI]
  6624.   ADD DI,WinXMINdiv4
  6625.   {ES:DI = ^Zieladresse der 1.Kachel der 1.oben nicht geschnittenen Kachelzeile}
  6626.  
  6627.   CMP leftcut,0
  6628.   JZ @m0SkipLeftColumn
  6629.  
  6630.   MOV DX,16+3
  6631.   SUB DX,leftcut
  6632.   SHR DX,1
  6633.   SHR DX,1
  6634.   MOV AX,LINESIZE
  6635.   SUB AX,DX   {Korrekturfaktor für AX}
  6636.   MOV LINESIZE_sub_BytesPerPlane,AX
  6637.   MOV BytesPerPlane,DX  {Bytes zu moven}
  6638.  
  6639.   MOV AX,innerTilesY
  6640.   OR AX,AX
  6641.   JBE @m0LeftLoopDone
  6642.   MOV counter,AX
  6643.  
  6644.   PUSH actIndex
  6645.   (* PUSH y *)
  6646.   PUSH ytil
  6647.   PUSH DI
  6648.  
  6649.   MOV AX,xtil
  6650.   CWD
  6651.   SUB AX,XTiles
  6652.   NOT AX
  6653.   OR AX,DX
  6654.   CWD
  6655.   NOT DX
  6656.   MOV Xoffscreen,DX  {ist für Kachelspalte konstant}
  6657.  
  6658.  @m0repeat5:
  6659.   MOV SI,actIndex
  6660.   AND SI,Xoffscreen
  6661.   JZ @m0go11
  6662.   MOV AX,ytil
  6663.   CWD
  6664.   SUB AX,YTiles
  6665.   NOT AX
  6666.   OR AX,DX
  6667.   CWD
  6668.   NOT DX
  6669.   AND SI,DX
  6670.  @m0go11:
  6671.     {PROCEDURE DrawLeftTile mit WriteMode0: }
  6672.     { in: ES:DI = ^Zieladresse}
  6673.     {     SI = Kachelindex    }
  6674.     {     StartLesePlane = erste Bitplane, von der gelesen wird}
  6675.     {     LINESIZE_sub_BytesPerPlane, BytesPerPlane = Korrekturfaktoren}
  6676.     {     leftcut, leftcutDIV4, Win*, SCROLLADR,...}
  6677.     {out: ES:DI = ^Zieladresse der nächsten Kachel darunter}
  6678.     {rem: WriteMode0 ist bereits gesetzt und bleibt gesetzt }
  6679.     MOV AL,[OFFSET BackTile +SI]
  6680.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  6681.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  6682.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  6683.     MOV SI,AX
  6684.  
  6685.     PUSH BP
  6686.     ADD SI,leftcutDIV4
  6687.     MOV DX,3C4h
  6688.     MOV AX,0102h
  6689.     OUT DX,AX
  6690.     MOV DX,3CEh
  6691.     MOV AX,StartLesePlane
  6692.     OUT DX,AX
  6693.     MOV BX,AX
  6694.  
  6695.     MOV AX,16+3
  6696.     SUB AX,leftcut
  6697.     PUSH AX
  6698.     SHR AX,1
  6699.     SHR AX,1
  6700.     MOV BP,AX
  6701.     MOV AX,LINESIZE
  6702.     SUB AX,BP
  6703.     MOV DX,4
  6704.     SUB DX,BP
  6705.  
  6706.     MOV DS,SCROLLADR
  6707.  
  6708.     PUSH SI
  6709.     PUSH DI
  6710.     MOV CX,BP   {1.Zeile}
  6711.     REP MOVSB
  6712.     ADD SI,DX
  6713.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  6714.  
  6715.     MOV CX,BP   {2.Zeile}
  6716.     REP MOVSB
  6717.     ADD SI,DX
  6718.     ADD DI,AX
  6719.  
  6720.     MOV CX,BP   {3.Zeile}
  6721.     REP MOVSB
  6722.     ADD SI,DX
  6723.     ADD DI,AX
  6724.  
  6725.     MOV CX,BP   {4.Zeile}
  6726.     REP MOVSB
  6727.     ADD SI,DX
  6728.     ADD DI,AX
  6729.  
  6730.     MOV CX,BP   {5.Zeile}
  6731.     REP MOVSB
  6732.     ADD SI,DX
  6733.     ADD DI,AX
  6734.  
  6735.     MOV CX,BP   {6.Zeile}
  6736.     REP MOVSB
  6737.     ADD SI,DX
  6738.     ADD DI,AX
  6739.  
  6740.     MOV CX,BP   {7.Zeile}
  6741.     REP MOVSB
  6742.     ADD SI,DX
  6743.     ADD DI,AX
  6744.  
  6745.     MOV CX,BP   {8.Zeile}
  6746.     REP MOVSB
  6747.     ADD SI,DX
  6748.     ADD DI,AX
  6749.  
  6750.     MOV CX,BP   {9.Zeile}
  6751.     REP MOVSB
  6752.     ADD SI,DX
  6753.     ADD DI,AX
  6754.  
  6755.     MOV CX,BP  {10.Zeile}
  6756.     REP MOVSB
  6757.     ADD SI,DX
  6758.     ADD DI,AX
  6759.  
  6760.     MOV CX,BP  {11.Zeile}
  6761.     REP MOVSB
  6762.     ADD SI,DX
  6763.     ADD DI,AX
  6764.  
  6765.     MOV CX,BP  {12.Zeile}
  6766.     REP MOVSB
  6767.     ADD SI,DX
  6768.     ADD DI,AX
  6769.  
  6770.     MOV CX,BP  {13.Zeile}
  6771.     REP MOVSB
  6772.     ADD SI,DX
  6773.     ADD DI,AX
  6774.  
  6775.     MOV CX,BP  {14.Zeile}
  6776.     REP MOVSB
  6777.     ADD SI,DX
  6778.     ADD DI,AX
  6779.  
  6780.     MOV CX,BP  {15.Zeile}
  6781.     REP MOVSB
  6782.     ADD SI,DX
  6783.     ADD DI,AX
  6784.  
  6785.     MOV CX,BP  {16.Zeile}
  6786.     REP MOVSB
  6787.  
  6788.     POP DI
  6789.     POP SI
  6790.  
  6791.     POP BP
  6792.     INC BH
  6793.     AND BH,3
  6794.     JNE @nowrap11a
  6795.     INC SI
  6796.    @nowrap11a:
  6797.     MOV AX,BX
  6798.     MOV DX,3CEh
  6799.     OUT DX,AX
  6800.     MOV DX,3C4h
  6801.     MOV AX,0202h
  6802.     OUT DX,AX
  6803.  
  6804.     DEC BP
  6805.     PUSH BP
  6806.     SHR BP,1
  6807.     SHR BP,1
  6808.     MOV AX,LINESIZE
  6809.     SUB AX,BP
  6810.     MOV DX,4
  6811.     SUB DX,BP
  6812.  
  6813.     PUSH SI
  6814.     PUSH DI
  6815.     MOV CX,BP   {1.Zeile}
  6816.     REP MOVSB
  6817.     ADD SI,DX
  6818.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  6819.  
  6820.     MOV CX,BP   {2.Zeile}
  6821.     REP MOVSB
  6822.     ADD SI,DX
  6823.     ADD DI,AX
  6824.  
  6825.     MOV CX,BP   {3.Zeile}
  6826.     REP MOVSB
  6827.     ADD SI,DX
  6828.     ADD DI,AX
  6829.  
  6830.     MOV CX,BP   {4.Zeile}
  6831.     REP MOVSB
  6832.     ADD SI,DX
  6833.     ADD DI,AX
  6834.  
  6835.     MOV CX,BP   {5.Zeile}
  6836.     REP MOVSB
  6837.     ADD SI,DX
  6838.     ADD DI,AX
  6839.  
  6840.     MOV CX,BP   {6.Zeile}
  6841.     REP MOVSB
  6842.     ADD SI,DX
  6843.     ADD DI,AX
  6844.  
  6845.     MOV CX,BP   {7.Zeile}
  6846.     REP MOVSB
  6847.     ADD SI,DX
  6848.     ADD DI,AX
  6849.  
  6850.     MOV CX,BP   {8.Zeile}
  6851.     REP MOVSB
  6852.     ADD SI,DX
  6853.     ADD DI,AX
  6854.  
  6855.     MOV CX,BP   {9.Zeile}
  6856.     REP MOVSB
  6857.     ADD SI,DX
  6858.     ADD DI,AX
  6859.  
  6860.     MOV CX,BP  {10.Zeile}
  6861.     REP MOVSB
  6862.     ADD SI,DX
  6863.     ADD DI,AX
  6864.  
  6865.     MOV CX,BP  {11.Zeile}
  6866.     REP MOVSB
  6867.     ADD SI,DX
  6868.     ADD DI,AX
  6869.  
  6870.     MOV CX,BP  {12.Zeile}
  6871.     REP MOVSB
  6872.     ADD SI,DX
  6873.     ADD DI,AX
  6874.  
  6875.     MOV CX,BP  {13.Zeile}
  6876.     REP MOVSB
  6877.     ADD SI,DX
  6878.     ADD DI,AX
  6879.  
  6880.     MOV CX,BP  {14.Zeile}
  6881.     REP MOVSB
  6882.     ADD SI,DX
  6883.     ADD DI,AX
  6884.  
  6885.     MOV CX,BP  {15.Zeile}
  6886.     REP MOVSB
  6887.     ADD SI,DX
  6888.     ADD DI,AX
  6889.  
  6890.     MOV CX,BP  {16.Zeile}
  6891.     REP MOVSB
  6892.  
  6893.     POP DI
  6894.     POP SI
  6895.  
  6896.     POP BP
  6897.     INC BH
  6898.     AND BH,3
  6899.     JNE @nowrap11b
  6900.     INC SI
  6901.    @nowrap11b:
  6902.     MOV AX,BX
  6903.     MOV DX,3CEh
  6904.     OUT DX,AX
  6905.     MOV DX,3C4h
  6906.     MOV AX,0402h
  6907.     OUT DX,AX
  6908.  
  6909.     DEC BP
  6910.     PUSH BP
  6911.     SHR BP,1
  6912.     SHR BP,1
  6913.     MOV AX,LINESIZE
  6914.     SUB AX,BP
  6915.     MOV DX,4
  6916.     SUB DX,BP
  6917.  
  6918.     PUSH SI
  6919.     PUSH DI
  6920.     MOV CX,BP   {1.Zeile}
  6921.     REP MOVSB
  6922.     ADD SI,DX
  6923.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  6924.  
  6925.     MOV CX,BP   {2.Zeile}
  6926.     REP MOVSB
  6927.     ADD SI,DX
  6928.     ADD DI,AX
  6929.  
  6930.     MOV CX,BP   {3.Zeile}
  6931.     REP MOVSB
  6932.     ADD SI,DX
  6933.     ADD DI,AX
  6934.  
  6935.     MOV CX,BP   {4.Zeile}
  6936.     REP MOVSB
  6937.     ADD SI,DX
  6938.     ADD DI,AX
  6939.  
  6940.     MOV CX,BP   {5.Zeile}
  6941.     REP MOVSB
  6942.     ADD SI,DX
  6943.     ADD DI,AX
  6944.  
  6945.     MOV CX,BP   {6.Zeile}
  6946.     REP MOVSB
  6947.     ADD SI,DX
  6948.     ADD DI,AX
  6949.  
  6950.     MOV CX,BP   {7.Zeile}
  6951.     REP MOVSB
  6952.     ADD SI,DX
  6953.     ADD DI,AX
  6954.  
  6955.     MOV CX,BP   {8.Zeile}
  6956.     REP MOVSB
  6957.     ADD SI,DX
  6958.     ADD DI,AX
  6959.  
  6960.     MOV CX,BP   {9.Zeile}
  6961.     REP MOVSB
  6962.     ADD SI,DX
  6963.     ADD DI,AX
  6964.  
  6965.     MOV CX,BP  {10.Zeile}
  6966.     REP MOVSB
  6967.     ADD SI,DX
  6968.     ADD DI,AX
  6969.  
  6970.     MOV CX,BP  {11.Zeile}
  6971.     REP MOVSB
  6972.     ADD SI,DX
  6973.     ADD DI,AX
  6974.  
  6975.     MOV CX,BP  {12.Zeile}
  6976.     REP MOVSB
  6977.     ADD SI,DX
  6978.     ADD DI,AX
  6979.  
  6980.     MOV CX,BP  {13.Zeile}
  6981.     REP MOVSB
  6982.     ADD SI,DX
  6983.     ADD DI,AX
  6984.  
  6985.     MOV CX,BP  {14.Zeile}
  6986.     REP MOVSB
  6987.     ADD SI,DX
  6988.     ADD DI,AX
  6989.  
  6990.     MOV CX,BP  {15.Zeile}
  6991.     REP MOVSB
  6992.     ADD SI,DX
  6993.     ADD DI,AX
  6994.  
  6995.     MOV CX,BP  {16.Zeile}
  6996.     REP MOVSB
  6997.  
  6998.     POP DI
  6999.     POP SI
  7000.  
  7001.     POP BP
  7002.     INC BH
  7003.     AND BH,3
  7004.     JNE @nowrap11c
  7005.     INC SI
  7006.    @nowrap11c:
  7007.     MOV AX,BX
  7008.     MOV DX,3CEh
  7009.     OUT DX,AX
  7010.     MOV DX,3C4h
  7011.     MOV AX,0802h
  7012.     OUT DX,AX
  7013.  
  7014.     DEC BP
  7015.  
  7016.     SHR BP,1
  7017.     SHR BP,1
  7018.     MOV AX,LINESIZE
  7019.     SUB AX,BP
  7020.     MOV DX,4
  7021.     SUB DX,BP
  7022.  
  7023.  
  7024.  
  7025.     MOV CX,BP   {1.Zeile}
  7026.     REP MOVSB
  7027.     ADD SI,DX
  7028.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  7029.  
  7030.     MOV CX,BP   {2.Zeile}
  7031.     REP MOVSB
  7032.     ADD SI,DX
  7033.     ADD DI,AX
  7034.  
  7035.     MOV CX,BP   {3.Zeile}
  7036.     REP MOVSB
  7037.     ADD SI,DX
  7038.     ADD DI,AX
  7039.  
  7040.     MOV CX,BP   {4.Zeile}
  7041.     REP MOVSB
  7042.     ADD SI,DX
  7043.     ADD DI,AX
  7044.  
  7045.     MOV CX,BP   {5.Zeile}
  7046.     REP MOVSB
  7047.     ADD SI,DX
  7048.     ADD DI,AX
  7049.  
  7050.     MOV CX,BP   {6.Zeile}
  7051.     REP MOVSB
  7052.     ADD SI,DX
  7053.     ADD DI,AX
  7054.  
  7055.     MOV CX,BP   {7.Zeile}
  7056.     REP MOVSB
  7057.     ADD SI,DX
  7058.     ADD DI,AX
  7059.  
  7060.     MOV CX,BP   {8.Zeile}
  7061.     REP MOVSB
  7062.     ADD SI,DX
  7063.     ADD DI,AX
  7064.  
  7065.     MOV CX,BP   {9.Zeile}
  7066.     REP MOVSB
  7067.     ADD SI,DX
  7068.     ADD DI,AX
  7069.  
  7070.     MOV CX,BP  {10.Zeile}
  7071.     REP MOVSB
  7072.     ADD SI,DX
  7073.     ADD DI,AX
  7074.  
  7075.     MOV CX,BP  {11.Zeile}
  7076.     REP MOVSB
  7077.     ADD SI,DX
  7078.     ADD DI,AX
  7079.  
  7080.     MOV CX,BP  {12.Zeile}
  7081.     REP MOVSB
  7082.     ADD SI,DX
  7083.     ADD DI,AX
  7084.  
  7085.     MOV CX,BP  {13.Zeile}
  7086.     REP MOVSB
  7087.     ADD SI,DX
  7088.     ADD DI,AX
  7089.  
  7090.     MOV CX,BP  {14.Zeile}
  7091.     REP MOVSB
  7092.     ADD SI,DX
  7093.     ADD DI,AX
  7094.  
  7095.     MOV CX,BP  {15.Zeile}
  7096.     REP MOVSB
  7097.     ADD SI,DX
  7098.     ADD DI,AX
  7099.  
  7100.     MOV CX,BP  {16.Zeile}
  7101.     REP MOVSB
  7102.     ADD SI,DX
  7103.     ADD DI,AX
  7104.  
  7105.     POP BP
  7106.     MOV AX,SEG @DATA
  7107.     MOV DS,AX
  7108.  
  7109.   {Nächste Kachel; DI steht schon richtig:}
  7110.   MOV AX,XTiles
  7111.   ADD actIndex,AX
  7112.   (* MOV AX,16 *)
  7113.   (* ADD y,AX *)
  7114.   INC ytil
  7115.  
  7116.   DEC counter
  7117.   JNZ @m0repeat5
  7118.  
  7119.   POP DI
  7120.   POP ytil
  7121.   (* POP y *)
  7122.   POP actIndex
  7123.  
  7124.  @m0LeftLoopDone:
  7125.   INC actIndex
  7126.   ADD DI,BytesPerPlane
  7127.  
  7128.   {-1, weil exakt einmal "inc di" ausgeführt wurde!}
  7129.   DEC DI
  7130.  
  7131.   INC xtil
  7132.   (* MOV AX,16 *)
  7133.   (* SUB AX,leftcut *)
  7134.   (* ADD x,AX *)
  7135.  
  7136.  @m0SkipLeftColumn:
  7137.   {ES:DI = ^Zieladresse der ersten inneren Kachel (immer noch)}
  7138.  
  7139.   MOV AX,innerTilesY    {gibt's überhaupt innere Kacheln?}
  7140.   OR AX,AX
  7141.   JBE @m0SkipInnerTiles {nein}
  7142.   CMP innerTilesX,0
  7143.   JB @m0SkipInnerTiles
  7144.  
  7145.   MOV counter,AX
  7146.   MOV AX,actIndex     {Kopien der aktuellen Werte anlegen}
  7147.   MOV tempActIndex,AX
  7148.   (* MOV AX,x *)
  7149.   (* MOV tempX,AX *)
  7150.   MOV AX,xtil
  7151.   MOV tempXtil,AX
  7152.   (* MOV AX,y *)
  7153.   (* MOV tempY,AX *)
  7154.   MOV AX,ytil
  7155.   MOV tempYtil,AX
  7156.   MOV tempDI,DI
  7157.  
  7158.   CMP rightcut,0
  7159.   JE @m0SkipRightColumn
  7160.  
  7161.   MOV DX,16
  7162.   SUB DX,rightcut
  7163.   SHR DX,1
  7164.   SHR DX,1
  7165.   MOV BytesPerPlane,DX
  7166.   MOV AX,LINESIZE
  7167.   SUB AX,DX
  7168.   MOV LINESIZE_sub_BytesPerPlane,AX
  7169.  
  7170.   MOV AX,innerTilesX
  7171.   ADD xtil,AX
  7172.   ADD actIndex,AX
  7173.   MOV CL,2
  7174.   SHL AX,CL
  7175.   ADD DI,AX  {ES:DI = ^erste rechte Randkachel, die oben nicht geschnitten}
  7176.   (* SHL AX,CL *)
  7177.   (* ADD x,AX *)
  7178.  
  7179.   MOV AX,xtil
  7180.   CWD
  7181.   SUB AX,XTiles
  7182.   NOT AX
  7183.   OR AX,DX
  7184.   CWD
  7185.   NOT DX
  7186.   MOV Xoffscreen,DX
  7187.  
  7188.  @m0repeat6:
  7189.   MOV SI,actIndex
  7190.   AND SI,Xoffscreen
  7191.   JZ @m0go12
  7192.   MOV AX,ytil
  7193.   CWD
  7194.   SUB AX,YTiles
  7195.   NOT AX
  7196.   OR AX,DX
  7197.   CWD
  7198.   NOT DX
  7199.   AND SI,DX
  7200.  @m0go12:
  7201.     {PROCEDURE DrawRightTile mit WriteMode0: }
  7202.     { in: ES:DI = ^Zieladresse}
  7203.     {     SI = Kachelindex    }
  7204.     {     StartWritePlane = erste zu beschreibende Bitplane}
  7205.     {     innerTilesY >= 1    }
  7206.     {     rightcut, Win*, SCROLLADR,...}
  7207.     {out: ES:DI = ^Zieladresse der nächsten Kachel darunter}
  7208.     {rem: WriteMode0 ist bereits gesetzt und bleibt gesetzt }
  7209.     MOV AL,[OFFSET BackTile +SI]
  7210.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  7211.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  7212.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  7213.     MOV SI,AX
  7214.  
  7215.     PUSH BP
  7216.     MOV DX,3C4h
  7217.     MOV AX,StartWritePlane
  7218.     OUT DX,AX
  7219.     MOV BX,AX
  7220.     MOV DX,3CEh
  7221.     MOV AX,0004h
  7222.     OUT DX,AX
  7223.  
  7224.     MOV AX,16+3
  7225.     SUB AX,rightcut
  7226.     PUSH AX
  7227.     SHR AX,1
  7228.     SHR AX,1
  7229.     MOV BP,AX
  7230.     MOV AX,LINESIZE
  7231.     SUB AX,BP
  7232.     MOV DX,4
  7233.     SUB DX,BP
  7234.  
  7235.     MOV DS,SCROLLADR
  7236.  
  7237.     PUSH SI
  7238.     PUSH DI
  7239.     MOV CX,BP   {1.Zeile}
  7240.     REP MOVSB
  7241.     ADD SI,DX
  7242.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  7243.  
  7244.     MOV CX,BP   {2.Zeile}
  7245.     REP MOVSB
  7246.     ADD SI,DX
  7247.     ADD DI,AX
  7248.  
  7249.     MOV CX,BP   {3.Zeile}
  7250.     REP MOVSB
  7251.     ADD SI,DX
  7252.     ADD DI,AX
  7253.  
  7254.     MOV CX,BP   {4.Zeile}
  7255.     REP MOVSB
  7256.     ADD SI,DX
  7257.     ADD DI,AX
  7258.  
  7259.     MOV CX,BP   {5.Zeile}
  7260.     REP MOVSB
  7261.     ADD SI,DX
  7262.     ADD DI,AX
  7263.  
  7264.     MOV CX,BP   {6.Zeile}
  7265.     REP MOVSB
  7266.     ADD SI,DX
  7267.     ADD DI,AX
  7268.  
  7269.     MOV CX,BP   {7.Zeile}
  7270.     REP MOVSB
  7271.     ADD SI,DX
  7272.     ADD DI,AX
  7273.  
  7274.     MOV CX,BP   {8.Zeile}
  7275.     REP MOVSB
  7276.     ADD SI,DX
  7277.     ADD DI,AX
  7278.  
  7279.     MOV CX,BP   {9.Zeile}
  7280.     REP MOVSB
  7281.     ADD SI,DX
  7282.     ADD DI,AX
  7283.  
  7284.     MOV CX,BP  {10.Zeile}
  7285.     REP MOVSB
  7286.     ADD SI,DX
  7287.     ADD DI,AX
  7288.  
  7289.     MOV CX,BP  {11.Zeile}
  7290.     REP MOVSB
  7291.     ADD SI,DX
  7292.     ADD DI,AX
  7293.  
  7294.     MOV CX,BP  {12.Zeile}
  7295.     REP MOVSB
  7296.     ADD SI,DX
  7297.     ADD DI,AX
  7298.  
  7299.     MOV CX,BP  {13.Zeile}
  7300.     REP MOVSB
  7301.     ADD SI,DX
  7302.     ADD DI,AX
  7303.  
  7304.     MOV CX,BP  {14.Zeile}
  7305.     REP MOVSB
  7306.     ADD SI,DX
  7307.     ADD DI,AX
  7308.  
  7309.     MOV CX,BP  {15.Zeile}
  7310.     REP MOVSB
  7311.     ADD SI,DX
  7312.     ADD DI,AX
  7313.  
  7314.     MOV CX,BP  {16.Zeile}
  7315.     REP MOVSB
  7316.  
  7317.     POP DI
  7318.     POP SI
  7319.  
  7320.     POP BP
  7321.     SHL BH,1
  7322.     CMP BH,16
  7323.     JNE @nowrap12a
  7324.     MOV BH,1
  7325.     INC DI
  7326.    @nowrap12a:
  7327.     MOV AX,BX
  7328.     MOV DX,3C4h
  7329.     OUT DX,AX
  7330.     MOV DX,3CEh
  7331.     MOV AX,0104h
  7332.     OUT DX,AX
  7333.  
  7334.     DEC BP
  7335.     PUSH BP
  7336.     SHR BP,1
  7337.     SHR BP,1
  7338.     MOV AX,LINESIZE
  7339.     SUB AX,BP
  7340.     MOV DX,4
  7341.     SUB DX,BP
  7342.  
  7343.     PUSH SI
  7344.     PUSH DI
  7345.     MOV CX,BP   {1.Zeile}
  7346.     REP MOVSB
  7347.     ADD SI,DX
  7348.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  7349.  
  7350.     MOV CX,BP   {2.Zeile}
  7351.     REP MOVSB
  7352.     ADD SI,DX
  7353.     ADD DI,AX
  7354.  
  7355.     MOV CX,BP   {3.Zeile}
  7356.     REP MOVSB
  7357.     ADD SI,DX
  7358.     ADD DI,AX
  7359.  
  7360.     MOV CX,BP   {4.Zeile}
  7361.     REP MOVSB
  7362.     ADD SI,DX
  7363.     ADD DI,AX
  7364.  
  7365.     MOV CX,BP   {5.Zeile}
  7366.     REP MOVSB
  7367.     ADD SI,DX
  7368.     ADD DI,AX
  7369.  
  7370.     MOV CX,BP   {6.Zeile}
  7371.     REP MOVSB
  7372.     ADD SI,DX
  7373.     ADD DI,AX
  7374.  
  7375.     MOV CX,BP   {7.Zeile}
  7376.     REP MOVSB
  7377.     ADD SI,DX
  7378.     ADD DI,AX
  7379.  
  7380.     MOV CX,BP   {8.Zeile}
  7381.     REP MOVSB
  7382.     ADD SI,DX
  7383.     ADD DI,AX
  7384.  
  7385.     MOV CX,BP   {9.Zeile}
  7386.     REP MOVSB
  7387.     ADD SI,DX
  7388.     ADD DI,AX
  7389.  
  7390.     MOV CX,BP  {10.Zeile}
  7391.     REP MOVSB
  7392.     ADD SI,DX
  7393.     ADD DI,AX
  7394.  
  7395.     MOV CX,BP  {11.Zeile}
  7396.     REP MOVSB
  7397.     ADD SI,DX
  7398.     ADD DI,AX
  7399.  
  7400.     MOV CX,BP  {12.Zeile}
  7401.     REP MOVSB
  7402.     ADD SI,DX
  7403.     ADD DI,AX
  7404.  
  7405.     MOV CX,BP  {13.Zeile}
  7406.     REP MOVSB
  7407.     ADD SI,DX
  7408.     ADD DI,AX
  7409.  
  7410.     MOV CX,BP  {14.Zeile}
  7411.     REP MOVSB
  7412.     ADD SI,DX
  7413.     ADD DI,AX
  7414.  
  7415.     MOV CX,BP  {15.Zeile}
  7416.     REP MOVSB
  7417.     ADD SI,DX
  7418.     ADD DI,AX
  7419.  
  7420.     MOV CX,BP  {16.Zeile}
  7421.     REP MOVSB
  7422.  
  7423.     POP DI
  7424.     POP SI
  7425.  
  7426.     POP BP
  7427.     SHL BH,1
  7428.     CMP BH,16
  7429.     JNE @nowrap12b
  7430.     MOV BH,1
  7431.     INC DI
  7432.    @nowrap12b:
  7433.     MOV AX,BX
  7434.     MOV DX,3C4h
  7435.     OUT DX,AX
  7436.     MOV DX,3CEh
  7437.     MOV AX,0204h
  7438.     OUT DX,AX
  7439.  
  7440.     DEC BP
  7441.     PUSH BP
  7442.     SHR BP,1
  7443.     SHR BP,1
  7444.     MOV AX,LINESIZE
  7445.     SUB AX,BP
  7446.     MOV DX,4
  7447.     SUB DX,BP
  7448.  
  7449.     PUSH SI
  7450.     PUSH DI
  7451.     MOV CX,BP   {1.Zeile}
  7452.     REP MOVSB
  7453.     ADD SI,DX
  7454.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  7455.  
  7456.     MOV CX,BP   {2.Zeile}
  7457.     REP MOVSB
  7458.     ADD SI,DX
  7459.     ADD DI,AX
  7460.  
  7461.     MOV CX,BP   {3.Zeile}
  7462.     REP MOVSB
  7463.     ADD SI,DX
  7464.     ADD DI,AX
  7465.  
  7466.     MOV CX,BP   {4.Zeile}
  7467.     REP MOVSB
  7468.     ADD SI,DX
  7469.     ADD DI,AX
  7470.  
  7471.     MOV CX,BP   {5.Zeile}
  7472.     REP MOVSB
  7473.     ADD SI,DX
  7474.     ADD DI,AX
  7475.  
  7476.     MOV CX,BP   {6.Zeile}
  7477.     REP MOVSB
  7478.     ADD SI,DX
  7479.     ADD DI,AX
  7480.  
  7481.     MOV CX,BP   {7.Zeile}
  7482.     REP MOVSB
  7483.     ADD SI,DX
  7484.     ADD DI,AX
  7485.  
  7486.     MOV CX,BP   {8.Zeile}
  7487.     REP MOVSB
  7488.     ADD SI,DX
  7489.     ADD DI,AX
  7490.  
  7491.     MOV CX,BP   {9.Zeile}
  7492.     REP MOVSB
  7493.     ADD SI,DX
  7494.     ADD DI,AX
  7495.  
  7496.     MOV CX,BP  {10.Zeile}
  7497.     REP MOVSB
  7498.     ADD SI,DX
  7499.     ADD DI,AX
  7500.  
  7501.     MOV CX,BP  {11.Zeile}
  7502.     REP MOVSB
  7503.     ADD SI,DX
  7504.     ADD DI,AX
  7505.  
  7506.     MOV CX,BP  {12.Zeile}
  7507.     REP MOVSB
  7508.     ADD SI,DX
  7509.     ADD DI,AX
  7510.  
  7511.     MOV CX,BP  {13.Zeile}
  7512.     REP MOVSB
  7513.     ADD SI,DX
  7514.     ADD DI,AX
  7515.  
  7516.     MOV CX,BP  {14.Zeile}
  7517.     REP MOVSB
  7518.     ADD SI,DX
  7519.     ADD DI,AX
  7520.  
  7521.     MOV CX,BP  {15.Zeile}
  7522.     REP MOVSB
  7523.     ADD SI,DX
  7524.     ADD DI,AX
  7525.  
  7526.     MOV CX,BP  {16.Zeile}
  7527.     REP MOVSB
  7528.  
  7529.     POP DI
  7530.     POP SI
  7531.  
  7532.     POP BP
  7533.     SHL BH,1
  7534.     CMP BH,16
  7535.     JNE @nowrap12c
  7536.     MOV BH,1
  7537.     INC DI
  7538.    @nowrap12c:
  7539.     MOV AX,BX
  7540.     MOV DX,3C4h
  7541.     OUT DX,AX
  7542.     MOV DX,3CEh
  7543.     MOV AX,0304h
  7544.     OUT DX,AX
  7545.  
  7546.     DEC BP
  7547.  
  7548.     SHR BP,1
  7549.     SHR BP,1
  7550.     MOV AX,LINESIZE
  7551.     SUB AX,BP
  7552.     MOV DX,4
  7553.     SUB DX,BP
  7554.  
  7555.  
  7556.  
  7557.     MOV CX,BP   {1.Zeile}
  7558.     REP MOVSB
  7559.     ADD SI,DX
  7560.     ADD DI,AX   {ES:DI = ^nächste Kachel}
  7561.  
  7562.     MOV CX,BP   {2.Zeile}
  7563.     REP MOVSB
  7564.     ADD SI,DX
  7565.     ADD DI,AX
  7566.  
  7567.     MOV CX,BP   {3.Zeile}
  7568.     REP MOVSB
  7569.     ADD SI,DX
  7570.     ADD DI,AX
  7571.  
  7572.     MOV CX,BP   {4.Zeile}
  7573.     REP MOVSB
  7574.     ADD SI,DX
  7575.     ADD DI,AX
  7576.  
  7577.     MOV CX,BP   {5.Zeile}
  7578.     REP MOVSB
  7579.     ADD SI,DX
  7580.     ADD DI,AX
  7581.  
  7582.     MOV CX,BP   {6.Zeile}
  7583.     REP MOVSB
  7584.     ADD SI,DX
  7585.     ADD DI,AX
  7586.  
  7587.     MOV CX,BP   {7.Zeile}
  7588.     REP MOVSB
  7589.     ADD SI,DX
  7590.     ADD DI,AX
  7591.  
  7592.     MOV CX,BP   {8.Zeile}
  7593.     REP MOVSB
  7594.     ADD SI,DX
  7595.     ADD DI,AX
  7596.  
  7597.     MOV CX,BP   {9.Zeile}
  7598.     REP MOVSB
  7599.     ADD SI,DX
  7600.     ADD DI,AX
  7601.  
  7602.     MOV CX,BP  {10.Zeile}
  7603.     REP MOVSB
  7604.     ADD SI,DX
  7605.     ADD DI,AX
  7606.  
  7607.     MOV CX,BP  {11.Zeile}
  7608.     REP MOVSB
  7609.     ADD SI,DX
  7610.     ADD DI,AX
  7611.  
  7612.     MOV CX,BP  {12.Zeile}
  7613.     REP MOVSB
  7614.     ADD SI,DX
  7615.     ADD DI,AX
  7616.  
  7617.     MOV CX,BP  {13.Zeile}
  7618.     REP MOVSB
  7619.     ADD SI,DX
  7620.     ADD DI,AX
  7621.  
  7622.     MOV CX,BP  {14.Zeile}
  7623.     REP MOVSB
  7624.     ADD SI,DX
  7625.     ADD DI,AX
  7626.  
  7627.     MOV CX,BP  {15.Zeile}
  7628.     REP MOVSB
  7629.     ADD SI,DX
  7630.     ADD DI,AX
  7631.  
  7632.     MOV CX,BP  {16.Zeile}
  7633.     REP MOVSB
  7634.     ADD SI,DX
  7635.     ADD DI,AX
  7636.  
  7637.     DEC DI
  7638.  
  7639.  
  7640.     POP BP
  7641.     MOV AX,SEG @DATA
  7642.     MOV DS,AX
  7643.  
  7644.   {Nächste Kachel; DI steht schon richtig:}
  7645.   MOV AX,XTiles
  7646.   ADD actIndex,AX
  7647.   (* MOV AX,16 *)
  7648.   (* ADD y,AX *)
  7649.   INC ytil
  7650.  
  7651.   DEC counter
  7652.   JNZ @m0repeat6
  7653.  
  7654.   MOV DI,tempDI
  7655.   MOV AX,tempActIndex
  7656.   MOV actIndex,AX
  7657.   (* MOV AX,tempX *)
  7658.   (* MOV x,tempX *)
  7659.   MOV AX,tempXtil
  7660.   MOV xtil,AX
  7661.   (* MOV AX,tempY *)
  7662.   (* MOV y,AX *)
  7663.   MOV AX,tempYtil
  7664.   MOV ytil,AX
  7665.  
  7666.  @m0RightLoopDone:
  7667.  @m0SkipRightColumn:
  7668.   {ES:DI = ^Zieladresse der ersten inneren Kachel (immer noch)}
  7669.   {innerTilesX >= 0, innerTilesX >= 1 -> es reichte, innerTilesX=0 zu prüfen:}
  7670.  
  7671.   CMP innerTilesX,0   {IF (innerTilesX <= 0) OR (innerTilesY <= 0) THEN skip}
  7672.   JBE @m0SkipInnerTiles  {Falls keine inneren Kachel existieren, dann ist  }
  7673.                          {stattdessen bereits auf erste links nicht ge-    }
  7674.                          {schnittene Kachel der untersten Kachelzeile pos. }
  7675.  
  7676.  
  7677.   {Nun "FOR x:=1 TO innerTilesX DO FOR y:=1 TO innerTilesY DO .." realisieren}
  7678.   MOV oldDI,DI        {temporäre Kopien von DI und actIndex anlegen}
  7679.   MOV AX,actIndex
  7680.   MOV oldActIndex,AX
  7681.  
  7682.   MOV AX,innerTilesX
  7683.   MOV counter,AX  {Zähler für X-Richtung}
  7684.   MOV CL,6
  7685.  
  7686.  @m0xloop:
  7687.   MOV AX,xtil
  7688.   CWD
  7689.   SUB AX,XTiles
  7690.   NOT AX
  7691.   OR AX,DX
  7692.   CWD
  7693.   NOT DX
  7694.   MOV Xoffscreen,DX  {ist für Kachelspalte konstant}
  7695.  
  7696.   MOV AX,innerTilesY
  7697.   MOV CH,AL  {CH dient als Zähler für Y-Richtung}
  7698.  
  7699.  
  7700.  @m0yloop:
  7701.   MOV SI,oldActIndex {SI = temp. actIndex}
  7702.   AND SI,Xoffscreen
  7703.   JZ @m0go5
  7704.   MOV AX,ytil
  7705.   CWD
  7706.   SUB AX,YTiles
  7707.   NOT AX
  7708.   OR AX,DX
  7709.   CWD
  7710.   NOT DX
  7711.   AND SI,DX
  7712.  @m0go5:
  7713.     {PROCEDURE DrawInnerTile mit WriteMode0: }
  7714.     { in: ES:DI = ^Zieladresse}
  7715.     {     SI = Kachelindex    }
  7716.     {     CL = 6              }
  7717.     {     SCROLLADR}
  7718.     {out: ES:DI = ^Zieladresse der nächsten Kachel darunter}
  7719.     {     CL = 6}
  7720.     {rem: WriteMode0 ist bereits gesetzt und bleibt gesetzt }
  7721.     {     CH wird nicht verändert!}
  7722.     MOV AL,[OFFSET BackTile +SI]
  7723.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  7724.                    {jede Kachel ist 64 Bytes lang, also}
  7725.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  7726.     MOV SI,AX
  7727.  
  7728.     MOV DS,SCROLLADR
  7729.  
  7730.     MOV DX,3C4h
  7731.     MOV AX,StartWritePlane
  7732.     MOV BX,AX
  7733.     OUT DX,AX
  7734.     MOV DX,3CEh
  7735.     MOV AX,0004h
  7736.     OUT DX,AX
  7737.  
  7738.     MOVSW       {1.Zeile}
  7739.     MOVSW
  7740.     ADD DI,LINESIZE-4   {ES:DI = ^nächste Kachel}
  7741.  
  7742.     MOVSW       {2.Zeile}
  7743.     MOVSW
  7744.     ADD DI,LINESIZE-4
  7745.  
  7746.     MOVSW       {3.Zeile}
  7747.     MOVSW
  7748.     ADD DI,LINESIZE-4
  7749.  
  7750.     MOVSW       {4.Zeile}
  7751.     MOVSW
  7752.     ADD DI,LINESIZE-4
  7753.  
  7754.     MOVSW       {5.Zeile}
  7755.     MOVSW
  7756.     ADD DI,LINESIZE-4
  7757.  
  7758.     MOVSW       {6.Zeile}
  7759.     MOVSW
  7760.     ADD DI,LINESIZE-4
  7761.  
  7762.     MOVSW       {7.Zeile}
  7763.     MOVSW
  7764.     ADD DI,LINESIZE-4
  7765.  
  7766.     MOVSW       {8.Zeile}
  7767.     MOVSW
  7768.     ADD DI,LINESIZE-4
  7769.  
  7770.     MOVSW       {9.Zeile}
  7771.     MOVSW
  7772.     ADD DI,LINESIZE-4
  7773.  
  7774.     MOVSW      {10.Zeile}
  7775.     MOVSW
  7776.     ADD DI,LINESIZE-4
  7777.  
  7778.     MOVSW      {11.Zeile}
  7779.     MOVSW
  7780.     ADD DI,LINESIZE-4
  7781.  
  7782.     MOVSW      {12.Zeile}
  7783.     MOVSW
  7784.     ADD DI,LINESIZE-4
  7785.  
  7786.     MOVSW      {13.Zeile}
  7787.     MOVSW
  7788.     ADD DI,LINESIZE-4
  7789.  
  7790.     MOVSW      {14.Zeile}
  7791.     MOVSW
  7792.     ADD DI,LINESIZE-4
  7793.  
  7794.     MOVSW      {15.Zeile}
  7795.     MOVSW
  7796.     ADD DI,LINESIZE-4
  7797.  
  7798.     MOVSW      {16.Zeile}
  7799.     MOVSW
  7800.  
  7801.     SUB SI,16*4
  7802.     SUB DI,15*LINESIZE +4
  7803.  
  7804.     MOV AX,0104h
  7805.     OUT DX,AX      {DX = 3CEh}
  7806.     SHL BH,1
  7807.     CMP BH,16
  7808.     JNE @nowrap5a
  7809.     MOV BH,1
  7810.     INC DI
  7811.    @nowrap5a:
  7812.     MOV AX,BX
  7813.     MOV DX,3C4h
  7814.     OUT DX,AX
  7815.  
  7816.     MOVSW       {1.Zeile}
  7817.     MOVSW
  7818.     ADD DI,LINESIZE-4   {ES:DI = ^nächste Kachel}
  7819.  
  7820.     MOVSW       {2.Zeile}
  7821.     MOVSW
  7822.     ADD DI,LINESIZE-4
  7823.  
  7824.     MOVSW       {3.Zeile}
  7825.     MOVSW
  7826.     ADD DI,LINESIZE-4
  7827.  
  7828.     MOVSW       {4.Zeile}
  7829.     MOVSW
  7830.     ADD DI,LINESIZE-4
  7831.  
  7832.     MOVSW       {5.Zeile}
  7833.     MOVSW
  7834.     ADD DI,LINESIZE-4
  7835.  
  7836.     MOVSW       {6.Zeile}
  7837.     MOVSW
  7838.     ADD DI,LINESIZE-4
  7839.  
  7840.     MOVSW       {7.Zeile}
  7841.     MOVSW
  7842.     ADD DI,LINESIZE-4
  7843.  
  7844.     MOVSW       {8.Zeile}
  7845.     MOVSW
  7846.     ADD DI,LINESIZE-4
  7847.  
  7848.     MOVSW       {9.Zeile}
  7849.     MOVSW
  7850.     ADD DI,LINESIZE-4
  7851.  
  7852.     MOVSW      {10.Zeile}
  7853.     MOVSW
  7854.     ADD DI,LINESIZE-4
  7855.  
  7856.     MOVSW      {11.Zeile}
  7857.     MOVSW
  7858.     ADD DI,LINESIZE-4
  7859.  
  7860.     MOVSW      {12.Zeile}
  7861.     MOVSW
  7862.     ADD DI,LINESIZE-4
  7863.  
  7864.     MOVSW      {13.Zeile}
  7865.     MOVSW
  7866.     ADD DI,LINESIZE-4
  7867.  
  7868.     MOVSW      {14.Zeile}
  7869.     MOVSW
  7870.     ADD DI,LINESIZE-4
  7871.  
  7872.     MOVSW      {15.Zeile}
  7873.     MOVSW
  7874.     ADD DI,LINESIZE-4
  7875.  
  7876.     MOVSW      {16.Zeile}
  7877.     MOVSW
  7878.  
  7879.     SUB SI,16*4
  7880.     SUB DI,15*LINESIZE +4
  7881.  
  7882.     SHL BH,1
  7883.     CMP BH,16
  7884.     JNE @nowrap5b
  7885.     MOV BH,1
  7886.     INC DI
  7887.    @nowrap5b:
  7888.     MOV AX,BX
  7889.     OUT DX,AX      {DX = 3C4h}
  7890.     MOV DX,3CEh
  7891.     MOV AX,0204h
  7892.     OUT DX,AX
  7893.  
  7894.     MOVSW       {1.Zeile}
  7895.     MOVSW
  7896.     ADD DI,LINESIZE-4   {ES:DI = ^nächste Kachel}
  7897.  
  7898.     MOVSW       {2.Zeile}
  7899.     MOVSW
  7900.     ADD DI,LINESIZE-4
  7901.  
  7902.     MOVSW       {3.Zeile}
  7903.     MOVSW
  7904.     ADD DI,LINESIZE-4
  7905.  
  7906.     MOVSW       {4.Zeile}
  7907.     MOVSW
  7908.     ADD DI,LINESIZE-4
  7909.  
  7910.     MOVSW       {5.Zeile}
  7911.     MOVSW
  7912.     ADD DI,LINESIZE-4
  7913.  
  7914.     MOVSW       {6.Zeile}
  7915.     MOVSW
  7916.     ADD DI,LINESIZE-4
  7917.  
  7918.     MOVSW       {7.Zeile}
  7919.     MOVSW
  7920.     ADD DI,LINESIZE-4
  7921.  
  7922.     MOVSW       {8.Zeile}
  7923.     MOVSW
  7924.     ADD DI,LINESIZE-4
  7925.  
  7926.     MOVSW       {9.Zeile}
  7927.     MOVSW
  7928.     ADD DI,LINESIZE-4
  7929.  
  7930.     MOVSW      {10.Zeile}
  7931.     MOVSW
  7932.     ADD DI,LINESIZE-4
  7933.  
  7934.     MOVSW      {11.Zeile}
  7935.     MOVSW
  7936.     ADD DI,LINESIZE-4
  7937.  
  7938.     MOVSW      {12.Zeile}
  7939.     MOVSW
  7940.     ADD DI,LINESIZE-4
  7941.  
  7942.     MOVSW      {13.Zeile}
  7943.     MOVSW
  7944.     ADD DI,LINESIZE-4
  7945.  
  7946.     MOVSW      {14.Zeile}
  7947.     MOVSW
  7948.     ADD DI,LINESIZE-4
  7949.  
  7950.     MOVSW      {15.Zeile}
  7951.     MOVSW
  7952.     ADD DI,LINESIZE-4
  7953.  
  7954.     MOVSW      {16.Zeile}
  7955.     MOVSW
  7956.  
  7957.     SUB SI,16*4
  7958.     SUB DI,15*LINESIZE +4
  7959.  
  7960.     MOV AX,0304h
  7961.     OUT DX,AX      {DX = 3CEh}
  7962.     SHL BH,1
  7963.     CMP BH,16
  7964.     JNE @nowrap5c
  7965.     MOV BH,1
  7966.     INC DI
  7967.    @nowrap5c:
  7968.     MOV AX,BX
  7969.     MOV DX,3C4h
  7970.     OUT DX,AX
  7971.  
  7972.     MOVSW       {1.Zeile}
  7973.     MOVSW
  7974.     ADD DI,LINESIZE-4   {ES:DI = ^nächste Kachel}
  7975.  
  7976.     MOVSW       {2.Zeile}
  7977.     MOVSW
  7978.     ADD DI,LINESIZE-4
  7979.  
  7980.     MOVSW       {3.Zeile}
  7981.     MOVSW
  7982.     ADD DI,LINESIZE-4
  7983.  
  7984.     MOVSW       {4.Zeile}
  7985.     MOVSW
  7986.     ADD DI,LINESIZE-4
  7987.  
  7988.     MOVSW       {5.Zeile}
  7989.     MOVSW
  7990.     ADD DI,LINESIZE-4
  7991.  
  7992.     MOVSW       {6.Zeile}
  7993.     MOVSW
  7994.     ADD DI,LINESIZE-4
  7995.  
  7996.     MOVSW       {7.Zeile}
  7997.     MOVSW
  7998.     ADD DI,LINESIZE-4
  7999.  
  8000.     MOVSW       {8.Zeile}
  8001.     MOVSW
  8002.     ADD DI,LINESIZE-4
  8003.  
  8004.     MOVSW       {9.Zeile}
  8005.     MOVSW
  8006.     ADD DI,LINESIZE-4
  8007.  
  8008.     MOVSW      {10.Zeile}
  8009.     MOVSW
  8010.     ADD DI,LINESIZE-4
  8011.  
  8012.     MOVSW      {11.Zeile}
  8013.     MOVSW
  8014.     ADD DI,LINESIZE-4
  8015.  
  8016.     MOVSW      {12.Zeile}
  8017.     MOVSW
  8018.     ADD DI,LINESIZE-4
  8019.  
  8020.     MOVSW      {13.Zeile}
  8021.     MOVSW
  8022.     ADD DI,LINESIZE-4
  8023.  
  8024.     MOVSW      {14.Zeile}
  8025.     MOVSW
  8026.     ADD DI,LINESIZE-4
  8027.  
  8028.     MOVSW      {15.Zeile}
  8029.     MOVSW
  8030.     ADD DI,LINESIZE-4
  8031.  
  8032.     MOVSW      {16.Zeile}
  8033.     MOVSW
  8034.  
  8035.  
  8036.     MOV AX,SEG @Data
  8037.     MOV DS,AX
  8038.  
  8039.     ADD DI,LINESIZE-4-1
  8040.  
  8041.   {Nächste Kachel; DI steht schon richtig:}
  8042.   MOV AX,XTiles     {temp. actIndex in nächste Zeile setzen}
  8043.   ADD oldActIndex,AX
  8044.   INC ytil
  8045.   (* MOV AX,16 *)
  8046.   (* ADD y,AX *)
  8047.  
  8048.   DEC CH
  8049.   JNZ @m0yloop
  8050.  
  8051.   {actIndex hat noch seinen alten Wert, da nur oldActIndex verändert wurde.}
  8052.   INC actIndex        {actIndex = nächste innere Kachel in oberster Kachelzeile}
  8053.   MOV AX,actIndex     {und als Startwert für nächste Spalte übernehmen}
  8054.   MOV oldActIndex,AX
  8055.  
  8056.   MOV DI,oldDI        {ES:DI = ^innere Kachel in oberster Kachelzeile}
  8057.   ADD DI,4            {eine Kachel weitersetzen}
  8058.   MOV oldDI,DI        {und als Startwert für nächste Spalte übernehmen}
  8059.  
  8060.   MOV AX,tempYtil
  8061.   MOV ytil,AX     {Y-Koordinate wieder auf oberste innere Kachelzeile setzen}
  8062.   (* MOV AX,oldY *)
  8063.   (* MOV y,AX *)
  8064.  
  8065.   INC xtil      {X-Koordinate eine Kachelspalte weitersetzen}
  8066.   (* MOV AX,16 *)
  8067.   (* ADD x,AX *)
  8068.  
  8069.   DEC counter
  8070.   JNZ @m0xloop
  8071.  
  8072.   MOV DI,tempDI       {Damit: ES:DI, actIndex, xtil, ytil, x, y zeigen wieder}
  8073.   MOV AX,tempActIndex {auf die erste, innere Kachel  (N.B.: y, ytil wurden   }
  8074.   MOV actIndex,AX     {bereits weiter oben wiederhergestellt)}
  8075.   MOV AX,tempXtil
  8076.   MOV xtil,AX
  8077.   (* MOV AX,tempX *)
  8078.   (* MOV x,AX *)
  8079.  
  8080.   MOV AX,innerTilesY
  8081.   MOV DX,AX     {Kopie in DX aufheben}
  8082.   ADD ytil,AX   {ytil zeigt jetzt auf unterste Kachelzeile}
  8083.  
  8084.   MOV CL,5
  8085.   SHL AX,CL     {dto. für DI: inc(DI,16 * innerTilesY * LINESIZE) }
  8086.   MOV BX,AX
  8087.   ADD DI,CS:[OFFSET GADR +BX]
  8088.   (* SHR AX,1 *)
  8089.   (* ADD y,AX *)  {dto. für y: inc(y,16 * innerTilesY) }
  8090.  
  8091.   MOV AX,XTiles
  8092.   MUL DX          {AX := XTiles * innerTilesY}
  8093.   ADD actIndex,AX {dto. für actIndex: inc(actIndex,XTiles * innerTilesY) }
  8094.  
  8095.  @m0SkipInnerTiles:
  8096.   {ES:DI, actIndex, xtil, ytil, x, y zeigen auf erste innere Kachel der}
  8097.   {untersten Kachelzeile}
  8098.   CMP bottomcut,0
  8099.   JE @m0fertig
  8100.  
  8101.   MOV AX,ytil
  8102.   CWD
  8103.   SUB AX,YTiles
  8104.   NOT AX
  8105.   OR AX,DX
  8106.   CWD
  8107.   NOT DX
  8108.   MOV Yoffscreen,DX
  8109.  
  8110.   MOV AX,innerTilesX
  8111.   OR AX,AX
  8112.   JBE @m0LowerInnerTilesDone {stehen wir bereits auf rechter unterer Eckkachel?}
  8113.   MOV counter,AX
  8114.  
  8115.   {Additionsfaktor berechnen, um von unten nach oben zu kommen:}
  8116.   {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  8117.   {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  8118.   {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  8119.   { ▀                      }
  8120.   {Korrektur:=-(16 - bottomcut) * LINESIZE + 4}
  8121.   MOV BX,16
  8122.   SUB BX,bottomcut
  8123.   SHL BX,1
  8124.   MOV AX,CS:[OFFSET GADR +BX]
  8125.   NEG AX
  8126.   ADD AX,4-1  {-1, weil durch Planing DI 1x erhöht wird}
  8127.   MOV Korrektur,AX
  8128.  
  8129.  @m0repeat4:
  8130.   MOV SI,actIndex
  8131.   AND SI,Yoffscreen
  8132.   JZ @m0go8
  8133.  
  8134.   MOV AX,xtil
  8135.   CWD
  8136.   SUB AX,XTiles
  8137.   NOT AX
  8138.   OR AX,DX
  8139.   CWD
  8140.   NOT DX
  8141.   AND SI,DX
  8142.  @m0go8:
  8143.     {PROCEDURE DrawLowerInnerTile mit WriteMode0: }
  8144.     { in: ES:DI = ^Zieladresse}
  8145.     {     SI = Kachelindex    }
  8146.     {     bottomcut, Win*, SCROLLADR,...}
  8147.     {out: ES:DI = ^Zieladresse der nächsten Kachel rechts davon}
  8148.     {rem: WriteMode0 ist bereits gesetzt und bleibt gesetzt}
  8149.     MOV AL,[OFFSET BackTile +SI]
  8150.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  8151.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  8152.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  8153.     MOV SI,AX
  8154.  
  8155.     MOV CX,16
  8156.     SUB CX,bottomcut
  8157.  
  8158.     MOV DX,3CEh
  8159.     MOV AX,0004h
  8160.     OUT DX,AX
  8161.     MOV DX,3C4h
  8162.     MOV AX,StartWritePlane
  8163.     OUT DX,AX
  8164.  
  8165.     MOV DS,SCROLLADR
  8166.  
  8167.     MOV BX,CX
  8168.     PUSH SI
  8169.     PUSH DI
  8170.    @m0eineZeile4e1:
  8171.     MOVSW
  8172.     MOVSW
  8173.     ADD DI,LINESIZE-4
  8174.     LOOP @m0eineZeile4e1
  8175.     POP DI
  8176.     POP SI
  8177.     MOV CX,BX
  8178.  
  8179.     SHL AH,1
  8180.     CMP AH,16
  8181.     JNE @nowrap8a
  8182.     MOV AH,1
  8183.     INC DI
  8184.    @nowrap8a:
  8185.     MOV DX,3C4h
  8186.     OUT DX,AX
  8187.     MOV BX,AX
  8188.     MOV DX,3CEh
  8189.     MOV AX,0104h
  8190.     OUT DX,AX
  8191.  
  8192.     MOV AX,CX
  8193.     PUSH SI
  8194.     PUSH DI
  8195.    @m0eineZeile4e2:
  8196.     MOVSW
  8197.     MOVSW
  8198.     ADD DI,LINESIZE-4
  8199.     LOOP @m0eineZeile4e2
  8200.     POP DI
  8201.     POP SI
  8202.     MOV CX,AX
  8203.  
  8204.     MOV DX,3CEh
  8205.     MOV AX,0204h
  8206.     OUT DX,AX
  8207.     SHL BH,1
  8208.     CMP BH,16
  8209.     JNE @nowrap8b
  8210.     MOV BH,1
  8211.     INC DI
  8212.    @nowrap8b:
  8213.     MOV AX,BX
  8214.     MOV DX,3C4h
  8215.     OUT DX,AX
  8216.  
  8217.     MOV BX,CX
  8218.     PUSH SI
  8219.     PUSH DI
  8220.    @m0eineZeile4e3:
  8221.     MOVSW
  8222.     MOVSW
  8223.     ADD DI,LINESIZE-4
  8224.     LOOP @m0eineZeile4e3
  8225.     POP DI
  8226.     POP SI
  8227.     MOV CX,BX
  8228.  
  8229.     SHL AH,1
  8230.     CMP AH,16
  8231.     JNE @nowrap8c
  8232.     MOV AH,1
  8233.     INC DI
  8234.    @nowrap8c:
  8235.     MOV DX,3C4h
  8236.     OUT DX,AX
  8237.     MOV BX,AX
  8238.     MOV DX,3CEh
  8239.     MOV AX,0304h
  8240.     OUT DX,AX
  8241.  
  8242.    @m0eineZeile4e4:
  8243.     MOVSW
  8244.     MOVSW
  8245.     ADD DI,LINESIZE-4
  8246.     LOOP @m0eineZeile4e4
  8247.  
  8248.  
  8249.     MOV AX,SEG @Data
  8250.     MOV DS,AX
  8251.  
  8252.     {DI = ^Start der Zeile unterhalb der Kachel, jetzt auf Start der nächsten}
  8253.     {Kachel setzen:}
  8254.     {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  8255.     {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  8256.     {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  8257.     { ▀                      }
  8258.     ADD DI,Korrektur
  8259.  
  8260.   {Auf nächste Kachel rechts davon positionieren:}
  8261.   INC actIndex
  8262.   INC xtil
  8263.   (* MOV AX,16 *)
  8264.   (* ADD x,AX *)
  8265.  
  8266.   DEC counter
  8267.   JNZ @m0repeat4
  8268.  
  8269.  @m0LowerInnerTilesDone:
  8270.   {ES:DI, actIndex, xtil, ytil, x, y zeigen auf untere rechte Eckkachel}
  8271.   CMP rightcut,0
  8272.   JE @m0SkipLowerRightCorner
  8273.  
  8274.   PUSH DI
  8275.   MOV SI,actIndex
  8276.   AND SI,Yoffscreen
  8277.   JZ @m0go9
  8278.   MOV AX,xtil
  8279.   CWD
  8280.   SUB AX,XTiles
  8281.   NOT AX
  8282.   OR AX,DX
  8283.   CWD
  8284.   NOT DX
  8285.   AND SI,DX
  8286.  @m0go9:
  8287.     {PROCEDURE DrawLowerRightTile mit WriteMode0: }
  8288.     { in: ES:DI = ^Zieladresse}
  8289.     {     SI = Kachelindex    }
  8290.     {     StartWritePlane = erste zu beschreibende Bitplane}
  8291.     {     rightcut MOD 4 = 0  }
  8292.     {     rightcut, bottomcut, Win*, SCROLLADR,...}
  8293.     {out: ES = ^Grafiksegment }
  8294.     {rem: WriteMode0 ist bereits gesetzt und bleibt gesetzt}
  8295.     PUSH BP
  8296.     MOV AL,[OFFSET BackTile +SI]
  8297.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  8298.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  8299.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  8300.     MOV SI,AX
  8301.  
  8302.     MOV CX,16
  8303.     SUB CX,bottomcut
  8304.     MOV BX,16+3
  8305.     SUB BX,rightcut
  8306.     PUSH BX
  8307.     SHR BX,1
  8308.     SHR BX,1  {BX = BytesPerPlane = (16 + 3 - rightcut) DIV 4}
  8309.  
  8310.     MOV DS,SCROLLADR
  8311.  
  8312.     MOV DX,3C4h
  8313.     MOV AX,StartWritePlane
  8314.     OUT DX,AX
  8315.     PUSH AX
  8316.     MOV DX,3CEh
  8317.     MOV AX,0004h
  8318.     OUT DX,AX
  8319.  
  8320.     MOV AX,LINESIZE
  8321.     SUB AX,BX
  8322.     MOV DX,4
  8323.     SUB DX,BX
  8324.     MOV BP,BX  {BP = Bytes je Zeile}
  8325.     MOV BL,CL  {BL = Zeilenzähler }
  8326.     MOV BH,CL  {Kopie nach BH}
  8327.  
  8328.     PUSH SI
  8329.     PUSH DI
  8330.    @m0eineZeile4g1:
  8331.     MOV CX,BP
  8332.     REP MOVSB
  8333.     ADD SI,DX
  8334.     ADD DI,AX
  8335.     DEC BL
  8336.     JNZ @m0eineZeile4g1
  8337.     POP DI
  8338.     POP SI
  8339.     MOV BL,BH
  8340.  
  8341.     POP AX
  8342.     SHL AH,1
  8343.     CMP AH,16
  8344.     JNE @nowrap9a
  8345.     MOV AH,1
  8346.     INC DI
  8347.    @nowrap9a:
  8348.     MOV DX,3C4h
  8349.     OUT DX,AX
  8350.     POP BP
  8351.     DEC BP
  8352.     PUSH BP
  8353.     PUSH AX
  8354.     MOV DX,3CEh
  8355.     MOV AX,0104h
  8356.     OUT DX,AX
  8357.     SHR BP,1
  8358.     SHR BP,1
  8359.  
  8360.     MOV AX,LINESIZE
  8361.     SUB AX,BP
  8362.     MOV DX,4
  8363.     SUB DX,BP
  8364.  
  8365.     PUSH SI
  8366.     PUSH DI
  8367.    @m0eineZeile4g2:
  8368.     MOV CX,BP
  8369.     REP MOVSB
  8370.     ADD SI,DX
  8371.     ADD DI,AX
  8372.     DEC BL
  8373.     JNZ @m0eineZeile4g2
  8374.     POP DI
  8375.     POP SI
  8376.     MOV BL,BH
  8377.  
  8378.     POP AX
  8379.     SHL AH,1
  8380.     CMP AH,16
  8381.     JNE @nowrap9b
  8382.     MOV AH,1
  8383.     INC DI
  8384.    @nowrap9b:
  8385.     MOV DX,3C4h
  8386.     OUT DX,AX
  8387.     POP BP
  8388.     DEC BP
  8389.     PUSH BP
  8390.     PUSH AX
  8391.     MOV DX,3CEh
  8392.     MOV AX,0204h
  8393.     OUT DX,AX
  8394.     SHR BP,1
  8395.     SHR BP,1
  8396.  
  8397.     MOV AX,LINESIZE
  8398.     SUB AX,BP
  8399.     MOV DX,4
  8400.     SUB DX,BP
  8401.  
  8402.     PUSH SI
  8403.     PUSH DI
  8404.    @m0eineZeile4g3:
  8405.     MOV CX,BP
  8406.     REP MOVSB
  8407.     ADD SI,DX
  8408.     ADD DI,AX
  8409.     DEC BL
  8410.     JNZ @m0eineZeile4g3
  8411.     POP DI
  8412.     POP SI
  8413.     MOV BL,BH
  8414.  
  8415.     POP AX
  8416.     SHL AH,1
  8417.     CMP AH,16
  8418.     JNE @nowrap9c
  8419.     MOV AH,1
  8420.     INC DI
  8421.    @nowrap9c:
  8422.     MOV DX,3C4h
  8423.     OUT DX,AX
  8424.     POP BP
  8425.     DEC BP
  8426.     MOV DX,3CEh
  8427.     MOV AX,0304h
  8428.     OUT DX,AX
  8429.     SHR BP,1
  8430.     SHR BP,1
  8431.  
  8432.     MOV AX,LINESIZE
  8433.     SUB AX,BP
  8434.     MOV DX,4
  8435.     SUB DX,BP
  8436.  
  8437.    @m0eineZeile4g4:
  8438.     MOV CX,BP
  8439.     REP MOVSB
  8440.     ADD SI,DX
  8441.     ADD DI,AX
  8442.     DEC BL
  8443.     JNZ @m0eineZeile4g4
  8444.  
  8445.  
  8446.     POP BP
  8447.     MOV AX,SEG @DATA
  8448.     MOV DS,AX
  8449.  
  8450.   POP DI   {ES:DI etc. zeigen auf rechte untere Eckkachel}
  8451.  
  8452.  @m0SkipLowerRightCorner:
  8453.   CMP leftcut,0
  8454.   JE @m0fertig
  8455.  
  8456.   {jetzt auf linke untere Eckkachel positionieren:}
  8457.   MOV AX,innerTilesX
  8458.   INC AX
  8459.   SUB actIndex,AX  {dec(actIndex,innerTilesX + 1) }
  8460.   SUB xtil,AX      {dec(xtil,innerTilesX + 1) }
  8461.   MOV CL,2
  8462.   SHL AX,CL
  8463.   SUB DI,AX        {dec(DI,4 * (innerTilesX + 1) }
  8464.   ADD DI,leftcutDIV4 {berücksichtige: Eckkachel kann links geschnitten sein}
  8465.   INC DI
  8466.   (* MOV AX,WinXMIN *)
  8467.   (* MOV x,AX *)
  8468.  
  8469.   MOV SI,actIndex
  8470.   AND SI,Yoffscreen
  8471.   JZ @m0go7
  8472.   MOV AX,xtil
  8473.   CWD
  8474.   SUB AX,XTiles
  8475.   NOT AX
  8476.   OR AX,DX
  8477.   CWD
  8478.   NOT DX
  8479.   AND SI,DX
  8480.  @m0go7:
  8481.     {PROCEDURE DrawLowerLeftTile mit WriteMode0: }
  8482.     { in: ES:DI = ^Zieladresse}
  8483.     {     SI = Kachelindex    }
  8484.     {     StartLesePlane = erste Bitplane, von der gelesen wird}
  8485.     {     rightcut MOD 4 = 0  }
  8486.     {     leftcut, bottomcut, Win*, SCROLLADR,...}
  8487.     {out: (ES = ^Grafiksegment) }
  8488.     {rem: WriteMode0 ist bereits gesetzt (und bleibt gesetzt)}
  8489.     PUSH BP
  8490.     MOV AL,[OFFSET BackTile +SI]
  8491.     XOR AH,AH      {Offsetadresse der Kachel berechnen:}
  8492.     MOV CL,6       {jede Kachel ist 64 Bytes lang, also}
  8493.     SHL AX,CL      {AX := Kachel * 64 = Kachel SHL 6   }
  8494.     MOV SI,AX
  8495.  
  8496.     MOV AX,leftcut
  8497.     MOV BX,AX
  8498.     MOV CL,2
  8499.     SHR AX,CL
  8500.     ADD SI,AX
  8501.  
  8502.     MOV CX,16
  8503.     SUB CX,bottomcut
  8504.  
  8505.     MOV DS,SCROLLADR
  8506.  
  8507.     MOV DX,3C4h
  8508.     MOV AX,0102h
  8509.     OUT DX,AX
  8510.     MOV DX,3CEh
  8511.     MOV AX,StartLesePlane
  8512.     OUT DX,AX
  8513.  
  8514.     MOV BP,16+3
  8515.     SUB BP,BX
  8516.     PUSH BP
  8517.     PUSH AX
  8518.     SHR BP,1
  8519.     SHR BP,1    {BP := (16 + 3 - leftcut) DIV 4}
  8520.  
  8521.     MOV AX,LINESIZE
  8522.     SUB AX,BP
  8523.     MOV DX,4
  8524.     SUB DX,BP
  8525.  
  8526.     MOV BL,CL   {BL=Zeilenzähler}
  8527.     MOV BH,CL   {Kopie davon}
  8528.  
  8529.     PUSH SI
  8530.     PUSH DI
  8531.    @m0eineZeile4d1:
  8532.     MOV CX,BP
  8533.     REP MOVSB
  8534.     ADD SI,DX
  8535.     ADD DI,AX
  8536.     DEC BL
  8537.     JNZ @m0eineZeile4d1
  8538.     POP DI
  8539.     POP SI
  8540.  
  8541.     POP AX
  8542.     INC AH
  8543.     AND AH,3
  8544.     JNE @nowrap7a
  8545.     INC SI
  8546.    @nowrap7a:
  8547.     MOV DX,3CEh
  8548.     OUT DX,AX
  8549.     POP BP
  8550.     DEC BP
  8551.     PUSH BP
  8552.     PUSH AX
  8553.     MOV DX,3C4h
  8554.     MOV AX,0202h
  8555.     OUT DX,AX
  8556.     SHR BP,1
  8557.     SHR BP,1
  8558.     MOV AX,LINESIZE
  8559.     SUB AX,BP
  8560.     MOV DX,4
  8561.     SUB DX,BP
  8562.     MOV BL,BH
  8563.  
  8564.     PUSH SI
  8565.     PUSH DI
  8566.    @m0eineZeile4d2:
  8567.     MOV CX,BP
  8568.     REP MOVSB
  8569.     ADD SI,DX
  8570.     ADD DI,AX
  8571.     DEC BL
  8572.     JNZ @m0eineZeile4d2
  8573.     POP DI
  8574.     POP SI
  8575.  
  8576.     POP AX
  8577.     INC AH
  8578.     AND AH,3
  8579.     JNE @nowrap7b
  8580.     INC SI
  8581.    @nowrap7b:
  8582.     MOV DX,3CEh
  8583.     OUT DX,AX
  8584.     POP BP
  8585.     DEC BP
  8586.     PUSH BP
  8587.     PUSH AX
  8588.     MOV DX,3C4h
  8589.     MOV AX,0402h
  8590.     OUT DX,AX
  8591.     SHR BP,1
  8592.     SHR BP,1
  8593.     MOV AX,LINESIZE
  8594.     SUB AX,BP
  8595.     MOV DX,4
  8596.     SUB DX,BP
  8597.     MOV BL,BH
  8598.  
  8599.     PUSH SI
  8600.     PUSH DI
  8601.    @m0eineZeile4d3:
  8602.     MOV CX,BP
  8603.     REP MOVSB
  8604.     ADD SI,DX
  8605.     ADD DI,AX
  8606.     DEC BL
  8607.     JNZ @m0eineZeile4d3
  8608.     POP DI
  8609.     POP SI
  8610.  
  8611.     POP AX
  8612.     INC AH
  8613.     AND AH,3
  8614.     JNE @nowrap7c
  8615.     INC SI
  8616.    @nowrap7c:
  8617.     MOV DX,3CEh
  8618.     OUT DX,AX
  8619.     POP BP
  8620.     DEC BP
  8621.     MOV DX,3C4h
  8622.     MOV AX,0802h
  8623.     OUT DX,AX
  8624.     SHR BP,1
  8625.     SHR BP,1
  8626.     MOV AX,LINESIZE
  8627.     SUB AX,BP
  8628.     MOV DX,4
  8629.     SUB DX,BP
  8630.     MOV BL,BH
  8631.  
  8632.    @m0eineZeile4d4:
  8633.     MOV CX,BP
  8634.     REP MOVSB
  8635.     ADD SI,DX
  8636.     ADD DI,AX
  8637.     DEC BL
  8638.     JNZ @m0eineZeile4d4
  8639.  
  8640.  
  8641.     POP BP
  8642.     MOV AX,SEG @DATA
  8643.     MOV DS,AX
  8644.  
  8645.  @m0fertig:
  8646.  
  8647.  
  8648.   {------- ab hier: Sprites auf aktuelle Grafikseite bringen}
  8649.  
  8650.   @Sprites_zeichnen:
  8651.     MOV SI,NMAX*2
  8652.     PUSH BP      {BP nachher wieder poppen!}
  8653.  
  8654.  
  8655.     @zeichne:
  8656.      CMP SI,SplitIndex_mal2  {Splitpunkt?}
  8657.      JNE @SZeich
  8658.  
  8659.      MOV AX,WinXMIN
  8660.      {ja: Win* Werte auf gesamte Fläche (0,0)..(XMAX,YMAX) setzen:}
  8661.      OR AX,WinYMIN
  8662.      JNZ @replace1
  8663.      CMP WinXMAX,XMAX
  8664.      JNE @replace2
  8665.      CMP WinYMAX,YMAX
  8666.      JE @replacedone
  8667.      JMP @replace3 {short}
  8668.  
  8669.     @replace1:
  8670.      MOV WinXMIN,0
  8671.      MOV WinYMIN,0
  8672.      MOV WinXMINdiv4,0
  8673.      MOV WinYMIN_mul_LINESIZE,0
  8674.      MOV WinYMINmLINESIZEaWinXMINdiv4,0
  8675.     @replace2:
  8676.      MOV WinXMAX,XMAX
  8677.      MOV WinWidth,XMAX+1
  8678.      MOV WinWidthDiv4,(XMAX+1)/4
  8679.     @replace3:
  8680.      MOV WinYMAX,YMAX
  8681.      MOV WinHeight,YMAX+1
  8682.      MOV WinLowerRight,0
  8683.     @replacedone:
  8684.  
  8685.     {DS = normales Datensegment, ES = Grafikseitensegment, }
  8686.     {SI = Spritepositionsnummer * 2                        }
  8687.     @SZeich:
  8688.       MOV BX,[SI + OFFSET SpriteN]   {BX = SpriteN[?] = Spriteladenummer}
  8689.       SHL BX,1                       {BX = Spriteladenummer * 2}
  8690.  
  8691.     {Jetzt: "SpriteN[?] := SpriteN[NextSprite[?]]" berechnen:}
  8692.       MOV BX,[BX + OFFSET NextSprite] {AX = NextSprite[SpriteN[?]]}
  8693.       MOV [SI + OFFSET SpriteN],BX    {als neue SpriteN[?] übernehmen}
  8694.       SHL BX,1
  8695.  
  8696.  
  8697.       JNZ @aktiv
  8698.       JMP @noSprite
  8699.  
  8700.  
  8701.     @aktiv:
  8702.       PUSH SI           {Spritepositionsnummer * 2 retten}
  8703.  
  8704.       MOV DX,[SI + OFFSET SpriteX]   {if SpriteX > xmax then skip_sprite}
  8705.       SUB DX,StartVirtualX           {virtuelle -> absolute Koordinaten }
  8706.       MOV AX,WinXMAX
  8707.       CMP DX,AX
  8708.       JLE @L0
  8709.     @ToSprite_fertig:                {Sprungleiste zu @Sprite_fertig}
  8710.       JMP @Sprite_fertig
  8711.     @L0:
  8712.       MOV CS:WORD PTR @akt_SpriteX+1,DX
  8713.       MOV DI,[SI + OFFSET SpriteY]   {DI = SpriteY_virtuell}
  8714.       SUB DI,StartVirtualY           {DI = SpriteY (absolut!)}
  8715.  
  8716.       PUSH AX                        {WinXMAX}
  8717.       MOV AX,WinYMIN
  8718.       MOV SI,WinXMIN
  8719.       MOV CX,WinYMAX                 {alte Win* Werte retten...}
  8720.  
  8721.       MOV DS,[BX + OFFSET SPRITEAD]  {!!!DS = ^Spritedaten !!!}
  8722.  
  8723.       MOV [WinYMIN_],AX              {...und in neues DS übernehmen}
  8724.       MOV [WinXMIN_],SI
  8725.       POP AX
  8726.       MOV [WinXMAX_],AX              {WinYMAX in CX halten}
  8727.  
  8728.       MOV AX,[Breite]                {AX = Breite in 4er-Gruppen}
  8729.       MOV CS:WORD PTR @max_Breite+1,AX
  8730.       MOV SI,AX                      {SI = dto.}
  8731.       SHL AX,1
  8732.       SHL AX,1                       {AX = max_Breite_in_Punkten}
  8733.       ADD AX,DX                      {AX = max_Breite_in_Punkten+SpriteX}
  8734.       CMP AX,[WinXMIN_]   {liegt rechtes Ende links vom Fensterrand?}
  8735.       JL @ToSprite_fertig
  8736.       MOV BX,DI                      {if SpriteY - WinYMIN >= 0 }
  8737.       SUB DI,[WinYMIN_]              { then starty := 0}
  8738.       NEG DI                         { else starty := -(SpriteY - WinYMIN)}
  8739.       MOV BP,DI
  8740.       JG @Top_cut
  8741.       XOR DI,DI
  8742.     @Top_cut:                        {DI = starty, BP = -(SpriteY - WinYMIN)}
  8743.       MOV AX,[Hoehe]                 {AX = Hoehe (in Zeilen)  }
  8744.       CMP DI,AX                      {if starty >= Hoehe then skip_sprite}
  8745.       JGE @ToSprite_fertig
  8746.       ADD BP,CX                      {BP = -(SpriteY - WinYMIN) + WinYMAX}
  8747.       SUB BP,[WinYMIN_]
  8748.       JL @ToSprite_fertig            {(etwas frei:) }
  8749.       CMP AX,BP                      {if Hoehe + SpriteY > WinYMAX  }
  8750.       JG @To_then                    { then [ endy := WinYMAX - SpriteY }
  8751.       DEC AX                         {       if endy < 0 then skip_sprite ] }
  8752.       MOV BP,AX                      { else endy := Hoehe - 1 }
  8753.  
  8754.     {BP = endy, SI=[@max_Breite+1] = max_Breite_in_4er_Gruppen, }
  8755.     {DI = starty, BX = SpriteY, DX=[@akt_SpriteX+1] = SpriteX,  }
  8756.     {DS = ^Spritedaten, ES = ^Grafikseite}
  8757.     @To_then:
  8758.       MOV AX,BP
  8759.       SUB BP,DI
  8760.  
  8761.       SHL BP,1
  8762.       MOV [End_min_Start],BP         {= (endy - starty) * 2 =Yaktuell * 2}
  8763.       ADD BX,AX
  8764.       SHL BX,1
  8765.       MOV BX,CS:[OFFSET gadr + BX] {BX =zeilenadr :=(endy + SpriteY) * LINESIZE}
  8766.       MOV [zeilenadr],BX             {auch nach [zeilenadr] }
  8767.       MOV BP,DX
  8768.       MUL SI                         {AX = endy * max_Breite_in_4er = yoffset}
  8769.       MOV [yoffset_],AX              {auch nach [yoffset_]}
  8770.       SHL DI,1                       {DI = starty * 2}
  8771.       MOV CS:WORD PTR @Starty_2+1,DI {auch nach [@Starty_2 + 1] }
  8772.  
  8773.       {kleiner Einschub: anhand des Modusbytes des Sprites entscheiden, ob}
  8774.       {eine andere Routine zur Darstellung des Sprites als die gerade ak- }
  8775.       {tive benötigt wird und wenn ja, diese in Position bringen!         }
  8776.       {Verwendete Register: AX und SI                                     }
  8777.        MOV AL,[Modus]                {Modusbyte des Sprites holen}
  8778.        XOR AH,AH
  8779.        SHL AX,1
  8780.        MOV SI,AX
  8781.        MOV SI,CS:[OFFSET Adressen +SI] {Pointer auf zugehörige Routine holen}
  8782.        MOV AX,CS:[SI]
  8783.        CMP AX,CS:[WORD PTR @Patch1]    {ist diese Routine bereits aktiv?}
  8784.        JE @no_newcode                  {ja, nix zu tun}
  8785.        PUSH DS                         {nein, kopiere die Routine an die}
  8786.        PUSH CS                         {entsprechenden Stellen}
  8787.        POP DS
  8788.        MOV [WORD PTR @Patch1],AX
  8789.        MOV [WORD PTR @Patch2],AX
  8790.        MOV [WORD PTR @Patch3],AX
  8791.        MOV [WORD PTR @Patch4],AX
  8792.        INC SI
  8793.        INC SI
  8794.        LODSW
  8795.        MOV [WORD PTR @Patch1+2],AX
  8796.        MOV [WORD PTR @Patch2+2],AX
  8797.        MOV [WORD PTR @Patch3+2],AX
  8798.        MOV [WORD PTR @Patch4+2],AX
  8799.        LODSW
  8800.        MOV [WORD PTR @Patch1+4],AX
  8801.        MOV [WORD PTR @Patch2+4],AX
  8802.        MOV [WORD PTR @Patch3+4],AX
  8803.        MOV [WORD PTR @Patch4+4],AX
  8804.        LODSW
  8805.        MOV [WORD PTR @Patch1+6],AX
  8806.        MOV [WORD PTR @Patch2+6],AX
  8807.        MOV [WORD PTR @Patch3+6],AX
  8808.        MOV [WORD PTR @Patch4+6],AX
  8809.        LODSW
  8810.        MOV [WORD PTR @Patch1+8],AX
  8811.        MOV [WORD PTR @Patch2+8],AX
  8812.        MOV [WORD PTR @Patch3+8],AX
  8813.        MOV [WORD PTR @Patch4+8],AX
  8814.        LODSW
  8815.        MOV [WORD PTR @Patch1+10],AX
  8816.        MOV [WORD PTR @Patch2+10],AX
  8817.        MOV [WORD PTR @Patch3+10],AX
  8818.        MOV [WORD PTR @Patch4+10],AX
  8819.        LODSW
  8820.        MOV [WORD PTR @Patch1+12],AX
  8821.        MOV [WORD PTR @Patch2+12],AX
  8822.        MOV [WORD PTR @Patch3+12],AX
  8823.        MOV [WORD PTR @Patch4+12],AX
  8824.        LODSW
  8825.        MOV [WORD PTR @Patch1+14],AX
  8826.        MOV [WORD PTR @Patch2+14],AX
  8827.        MOV [WORD PTR @Patch3+14],AX
  8828.        MOV [WORD PTR @Patch4+14],AX
  8829.  
  8830.        POP DS                          {DS wiederherstellen}
  8831.      @no_newcode:
  8832.  
  8833.  
  8834.     {(AX=)[yoffset_]         = yoffset }
  8835.     { BX = [zeilenadr]       = (endy + SpriteY) * LINESIZE}
  8836.     { CX                     = WinYMAX }
  8837.     { DI = [@Starty_2 + 1]   = starty * 2}
  8838.     {(SI = [@max_Breite + 1] = max_Breite_in_4er_) }
  8839.     { BP = [@akt_SpriteX + 1]= SpriteX}
  8840.     { DS                     = ^Spritedaten}
  8841.     { ES                     = ^Grafikseite}
  8842.     { [end_min_start]        = (endy - starty) * 2 = Yaktuell * 2}
  8843.     { [@max_Breite + 1]      = max_Breite_in_4er_Gruppen  }
  8844.     @eine_Zeile:
  8845.       MOV SI,[end_min_start]         {SI = Yaktuell * 2 }
  8846.       ADD SI,DI                      {startx := sprite[WORD PTR sprite[L] +  }
  8847.       MOV DI,SI                      {              (Yaktuell + starty) * 2] }
  8848.       ADD SI,[Left]
  8849.       MOV SI,[SI]                    {SI = startx, DI = (Yaktuell + starty) * 2}
  8850.       MOV AX,BP
  8851.       MOV DX,AX                      {AX = DX = SpriteX}
  8852.       SUB BP,[WinXMIN_]              {BP = SpriteX - WinXMIN}
  8853.       ADD AX,SI                      {AX = bildschirmstartx := SpriteX + startx }
  8854.       CMP AX,[WinXMAX_]       {if bildschirmstartx > WinXMAX then skip_zeile}
  8855.       JG @ToZeile_fertig
  8856.       MOV CX,SI                      {CX = startx}
  8857.       SUB AX,[WinXMIN_]              {licutoff_in_Punkten := startx}
  8858.       JGE @L1                        {if bildschirmstartx < WinXMIN then }
  8859.       SUB SI,AX                      { [dec(startx,bildschirmstartx - WinXMIN) }
  8860.       XOR AX,AX                      {  bildschirmstartx := WinXMIN      }
  8861.       MOV CX,BP                      {  licutoff_in_Punkten := -SpriteX] }
  8862.       NEG CX
  8863.     @L1:
  8864.       ADD AX,[WinXMIN_]              {CX = [licutoff_] = licutoff_in_Punkten, }
  8865.       MOV [licutoff_],CX             {SI = startx, AX = bildschirmstartx   }
  8866.       ADD DI,[Right]
  8867.       MOV DI,[DI]                    {DI = endx := sprite[WORD PTR sprite[R] +  }
  8868.                                      {                (Yaktuell + starty) * 2]  }
  8869.       NEG DX                         {DX = -SpriteX }
  8870.       MOV BP,DI
  8871.       SUB BP,SI                      {BP = endx - startx }
  8872.       SUB DX,DI                      {DX = -(SpriteX + endx) }
  8873.       ADD DX,[WinXMAX_]              {DX = Überhang := WinXMAX - (SpriteX + endx) }
  8874.       JNS @kein_Ueberhang_rechts
  8875.       ADD BP,DX
  8876.     @kein_Ueberhang_rechts:          {BP = sichtbare Breite dieser Zeile -1}
  8877.       OR BP,BP
  8878.       JNS @L6
  8879.     @ToZeile_fertig:
  8880.       JMP @Zeile_fertig              {if Breite <= 0 then skip_zeile }
  8881.     @L6:
  8882.       ADD BP,4
  8883.  
  8884.       { AX                 = bildschirmstartx}
  8885.       { BX = [zeilenadr]   = (endy + SpriteY) * LINESIZE }
  8886.       { CX = [licutoff_]   = licutoff_in_Punkten}
  8887.       {(DX                 = (negativer) Überhang (falls Wert < 0) ) }
  8888.       {(SI                 = startx) }
  8889.       {(DI                 = endx) }
  8890.       { BP                 = Breite für diese Zeile in Punkten + 3 }
  8891.       { DS                 = ^Spritedaten}
  8892.       { ES                 = ^Grafikseite}
  8893.       { [@max_Breite + 1]  = max_Breite_in_4er_) }
  8894.       { [end_min_start]    = (endy - starty) * 2 =Yaktuell * 2}
  8895.       { [@Starty_2 + 1]    = starty * 2}
  8896.       { [@max_Breite + 1]  = max_Breite_in_4er_Gruppen, }
  8897.       { [@akt_SpriteX + 1] = SpriteX}
  8898.       MOV [bildx],AX                 {bildschirmstartx retten}
  8899.       MOV DX,CX                      {DX = licutoff_in_Punkten}
  8900.       MOV CX,BP
  8901.       SHR CX,1
  8902.       SHR CX,1                       {CX = Breite DIV 4}
  8903.       JCXZ @Plane1
  8904.  
  8905.       {SI = Quellzeiger := sprite[WORD PTR (licutoff_in_Punkten + 0 AND 3) * 2}
  8906.       {                       + (licutoff_in_Punkten + 0) DIV 4 + yoffset  }
  8907.       MOV SI,DX
  8908.       AND SI,3                       
  8909.       SHL SI,1                  {SI = ((licutoff_in_Punkten + 0) AND 3) * 2}
  8910.       MOV SI,[SI]                    
  8911.       MOV DI,DX
  8912.       SHR DI,1
  8913.       SHR DI,1
  8914.       ADD SI,DI
  8915.       ADD SI,[yoffset_]              {SI = sprite[WORD PTR (licutoff_...)]   }
  8916.                                      {     + (licutoff_in_Punkten + i) DIV 4 }
  8917.                                      {     + yoffset                         }
  8918.  
  8919.       {DI = Zielzeiger := (bildschirmstartx + 0) DIV 4 + zeilenadr}
  8920.       MOV DI,AX                      {DI = bildschirmstartx }
  8921.       SHR DI,1
  8922.       SHR DI,1
  8923.       ADD DI,BX
  8924.       MOV BL,AL
  8925.       AND BX,3                       {BX = (bildschirmstartx + i) AND 3 }
  8926.       MOV AH,Translate[BX]           {AH = 1,2,4,8 für BX = 0,1,2,3     }
  8927.       MOV AL,2
  8928.       MOV DX,3C4h
  8929.       OUT DX,AX                      {Plane auswählen}
  8930.  
  8931.       XCHG BX,DI
  8932.       {CX Bytes von DS:SI nach ES:BX übertragen }
  8933.       {Hierher kommt die Routine zur Datenübertragung!}
  8934.     @Patch1:
  8935.       db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  8936.  
  8937.     @Plane1:
  8938.       MOV DX,[bildx]
  8939.       INC DX                      {DX = bildschirmstartx+1}
  8940.       MOV BX,DX
  8941.       SHR BX,1
  8942.       SHR BX,1                    {BX = zielzeiger := (bildschirmstartx + 1) }
  8943.       ADD BX,[zeilenadr]          {                DIV 4 + zeilenadr    }
  8944.       MOV CX,BP
  8945.       DEC CX                      {CX = Breite dieser Zeile + 3 - 1 }
  8946.       SHR CX,1
  8947.       SHR CX,1                    {CX = Bytes_zu_moven für i = 1 }
  8948.       JCXZ @Plane2
  8949.       MOV DI,[licutoff_]
  8950.       INC DI                      {DI = (licutoff_in_Punkten + 1) }
  8951.       MOV SI,DI
  8952.       AND SI,3
  8953.       SHL SI,1                    {SI = ((licutoff_in_Punkten + 1) AND 3) * 2}
  8954.       MOV SI,[SI]                 {SI = sprite[WORD PTR licutoff_...]     }
  8955.       SHR DI,1                    {     + (licutoff_in_Punkten + 1) DIV 4 }
  8956.       SHR DI,1                    {     + yoffset                         }
  8957.       ADD SI,DI
  8958.       ADD SI,[yoffset_]           {SI = Quellzeiger, }
  8959.                                   {DI = (licutoff_in_Punkten + 1) DIV 4 }
  8960.  
  8961.       MOV DI,DX                   {DI = bildschirmstartx + 1}
  8962.       AND DI,3                    {DI = (bildschirmstartx + 1) AND 3 }
  8963.       MOV AH,Translate[DI]        {Maske für Portzugriff laden}
  8964.       MOV AL,2
  8965.       MOV DX,3C4h                 {Plane anwählen}
  8966.       OUT DX,AX
  8967.  
  8968.       {Hierher kommt die Routine zur Datenübertragung!}
  8969.     @Patch2:
  8970.       db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  8971.  
  8972.     @Plane2:
  8973.       MOV DX,[bildx]
  8974.       ADD DX,2
  8975.       MOV BX,DX
  8976.       SHR BX,1
  8977.       SHR BX,1
  8978.       ADD BX,[zeilenadr]
  8979.       MOV CX,BP
  8980.       SUB CX,2
  8981.       SHR CX,1
  8982.       SHR CX,1
  8983.       JCXZ @Plane3
  8984.       MOV DI,[licutoff_]
  8985.       ADD DI,2
  8986.       MOV SI,DI
  8987.       AND SI,3
  8988.       SHL SI,1
  8989.       MOV SI,[SI]
  8990.       SHR DI,1
  8991.       SHR DI,1
  8992.       ADD SI,DI
  8993.       ADD SI,[yoffset_]
  8994.  
  8995.       MOV DI,DX                      {DI = bildschirmstartx + 2}
  8996.       AND DI,3                       {DI = (bildschirmstartx + 1) AND 3 }
  8997.       MOV AH,Translate[DI]
  8998.       MOV AL,2
  8999.       MOV DX,3C4h
  9000.       OUT DX,AX
  9001.  
  9002.       {Hierher kommt die Routine zur Datenübertragung!}
  9003.     @Patch3:
  9004.       db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  9005.  
  9006.     @Plane3:
  9007.       MOV DX,[bildx]
  9008.       ADD DX,3
  9009.       MOV BX,DX
  9010.       SHR BX,1
  9011.       SHR BX,1
  9012.       ADD BX,[zeilenadr]
  9013.       MOV CX,BP
  9014.       SUB CX,3
  9015.       SHR CX,1
  9016.       SHR CX,1
  9017.       JCXZ @Zeile_fertig
  9018.       MOV DI,[licutoff_]
  9019.       ADD DI,3
  9020.       MOV SI,DI
  9021.       AND SI,3
  9022.       SHL SI,1
  9023.       MOV SI,[SI]
  9024.       SHR DI,1
  9025.       SHR DI,1
  9026.       ADD SI,DI
  9027.       ADD SI,[yoffset_]
  9028.  
  9029.       MOV DI,DX                      {DI = bildschirmstartx + 3}
  9030.       AND DI,3                       {DI = (bildschirmstartx + 1) AND 3 }
  9031.       MOV AH,Translate[DI]
  9032.       MOV AL,2
  9033.       MOV DX,3C4h
  9034.       OUT DX,AX
  9035.  
  9036.       {Hierher kommt die Routine zur Datenübertragung!}
  9037.     @Patch4:
  9038.       db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  9039.  
  9040.     @Zeile_fertig:
  9041.       MOV AX,[yoffset_]
  9042.     @max_Breite:
  9043.       SUB AX,1234
  9044.       MOV [yoffset_],AX
  9045.       MOV BX,[zeilenadr]
  9046.       SUB BX,LINESIZE
  9047.       MOV [zeilenadr],BX
  9048.       SUB WORD PTR [end_min_start],2
  9049.       JS @Sprite_fertig
  9050.  
  9051.     @Starty_2:
  9052.       MOV DI,1234
  9053.     @akt_SpriteX:
  9054.       MOV BP,1234
  9055.       JMP @eine_Zeile
  9056.  
  9057.     @Sprite_fertig:
  9058.       POP SI
  9059.       MOV AX,SEG @Data
  9060.       MOV DS,AX
  9061.  
  9062.     @noSprite:
  9063.       DEC SI
  9064.       DEC SI
  9065.       JS @fertig
  9066.       JMP @zeichne
  9067.     @fertig:
  9068.  
  9069.       POP BP
  9070.  
  9071.       {Win* Werte wieder auf alte Werte zurücksetzen --wenn nötig:}
  9072.       MOV AX,SplitIndex
  9073.       CMP AX,NMAX
  9074.       JGE @skip  {IF (SplitIndex<0) OR (SplitIndex>NMAX) THEN Skip}
  9075.  
  9076.       MOV AX,BWinLowerRight
  9077.       MOV WinLowerRight,AX
  9078.       MOV BX,BWinXMIN
  9079.       MOV WinXMIN,BX
  9080.       MOV SI,BX           {SI := WinXMIN}
  9081.       SHR BX,1
  9082.       SHR BX,1
  9083.       MOV WinXMINdiv4,BX  {BX := WinXMIN div 4}
  9084.       MOV CX,BWinYMIN
  9085.       MOV WinYMIN,CX      {CX := WinYMIN}
  9086.       MOV AX,BWinYMIN_mul_LINESIZE
  9087.       MOV WinYMIN_mul_LINESIZE,AX
  9088.       ADD AX,BX
  9089.       MOV WinYMINmLINESIZEaWinXMINdiv4,AX
  9090.       MOV AX,BWinXMAX
  9091.       MOV WinXMAX,AX      {AX := WinXMAX}
  9092.       MOV DX,BWinYMAX
  9093.       MOV WinYMAX,DX      {DX := WinYMAX}
  9094.       SUB DX,CX
  9095.       INC DX
  9096.       MOV WinHeight,DX
  9097.       SUB AX,SI
  9098.       INC AX
  9099.       MOV WinWidth,AX
  9100.       SHR AX,1
  9101.       SHR AX,1
  9102.       MOV WinWidthDiv4,AL
  9103.     @skip:
  9104.  
  9105.                                  {"CX := Offset_Adr[Page]" vorbereiten:}
  9106.       MOV SI,PAGE                {Pagewert * 2 (da Worteinträge!)}
  9107.       MOV BX,SI                  {Pagewert in BX merken!}
  9108.       SHL SI,1                   {dazu Startadresse des Feldes addieren}
  9109.       ADD SI,OFFSET Offset_Adr-StartIndex*2  {evtl. Verschiebung korrigieren}
  9110.       LODSW                      {Realisiere "AX := Offset_Adr[Page]"}
  9111.       MOV CX,AX                  {nach CX bringen}
  9112.       MOV DI,CRTAddress          {DI := CRTAddress }
  9113.  
  9114.     {Die Grafikseite ist nun fertiggestellt und muß noch angezeigt werden:}
  9115.       cli
  9116.       mov  dx,StatusReg
  9117.  
  9118.     {Auf "display enable"=0 (d.h.: aktiv) warten, so daß Seitenumschaltung}
  9119.     {für HB/LB auf selber Seite geschieht:}
  9120.  
  9121.     @WaitNotHSyncLoop:
  9122.       in   al,dx
  9123.       and  al,1
  9124.       jz  @WaitNotHSyncLoop
  9125.  
  9126.     @WaitHSyncLoop:
  9127.       in   al,dx
  9128.       and  al,1
  9129.       jnz  @WaitHSyncLoop
  9130.  
  9131.       MOV DX,DI                  {DX := CRTAddress}
  9132.       MOV AL,$0D                 {LB-Startadress-Register}
  9133.       OUT DX,AL
  9134.       INC DX
  9135.                                  
  9136.       MOV AX,CX                  {AX := Offset_Adr[Page]}
  9137.       OUT DX,AL                  {LB der neuen Startadresse setzen}
  9138.       DEC DX
  9139.       MOV AL,$0C
  9140.       OUT DX,AL
  9141.       INC DX
  9142.       MOV AL,AH                  {HB der neuen Startadresse setzen}
  9143.       OUT DX,AL
  9144.       STI
  9145.  
  9146.       NEG BX       {neuer PAGE-Wert := 1-alter PAGE-Wert, d.h.:  }
  9147.       ADD BX,1     {IF PAGE = 0 THEN PAGE := 1 ELSE (PAGE = 1) PAGE := 0 }
  9148.       MOV PAGE,BX
  9149.  
  9150.       SHL BX,1     {neuer PAGEADR-Wert := Segment_Adr[PAGE] }
  9151.       ADD BX,OFFSET Segment_Adr-StartIndex*2
  9152.       MOV AX,[BX]
  9153.       MOV PAGEADR,AX
  9154.  
  9155.       {Jetzt überprüfen, ob gesetzte Zyklus(mindest)zeit abgelaufen ist:}
  9156.     @L10:
  9157.       MOV AL,TimeFlag   {Bit 7 = 0/1 für Zeit ist abgelaufen/läuft noch }
  9158.       AND AL,$80
  9159.       JE @L10
  9160.  
  9161.       {Zeitüberwachung für nächsten Zyklus starten:}
  9162.       MOV AL,IsAT                 {ist das ein AT/386? ($0/$80 = ja/nein)  }
  9163.       OR AL,AL                    {Zeitmechanismus geht nur auf AT/386     }
  9164.       JNE @L11                    {anderenfalls keine Zeitüberwachung!     }
  9165.       MOV TimeFlag,AL             {AL = 0 zugleich als Init-Wert benutzen  }
  9166.       MOV DX,WORD PTR CycleTime   {Mindestzykluszeit (in Mikrosekunden)    }
  9167.       MOV CX,WORD PTR CycleTime+2 {eintragen: CX = HIGH-Word, DX = LOW-Word}
  9168.       MOV BX,OFFSET TimeFlag      {ES:BX = Zeiger auf TimeFlag, Bit 7 = 0/1}
  9169.       MOV AX,DS                   {für: Zeit läuft noch/ist um             }
  9170.       MOV ES,AX
  9171.       MOV AX,8300h                {Zeitüberwachung starten}
  9172.       INT 15h
  9173.     @L11:
  9174.  END; {of ASM}
  9175. END; {of ANIMATE}
  9176.  
  9177. PROCEDURE FreeSpriteMem(number:WORD);
  9178. { in: number = Ladenummer des Sprites, das freigegeben werden soll}
  9179. {out: belegter Speicher wurde freigegeben, SPRITEAD[number] auf 0 und}
  9180. {     SPRITEPTR[number] auf NIL gesetzt}
  9181. {rem: Spritenummern auf den freigegebenen Bereich sind anschließend}
  9182. {     natürlich nicht mehr erlaubt!}
  9183. {     War unter "number" kein Sprite geladen, so passiert nichts}
  9184. {     Evtl. Spritezyklen müssen durch entsprechend viele Aufrufe dieser}
  9185. {     Routine aufgelöst werden.}
  9186. BEGIN
  9187.  IF (number<1) OR (number>LoadMAX)
  9188.   THEN Error:=Err_InvalidSpriteLoadNumber
  9189.   ELSE IF SPRITEPTR[number]<>NIL
  9190.         THEN BEGIN
  9191.               FreeMem(SPRITEPTR[number],SPRITESIZE[number]);
  9192.               SPRITEPTR[number]:=NIL;
  9193.               SPRITESIZE[number]:=0;
  9194.               SPRITEAD[number]:=0
  9195.              END
  9196. END;
  9197.  
  9198. FUNCTION LoadSprite(name:String; number:WORD):WORD;
  9199. { in: name   = Name des zu ladenden Sprite-Files (Typ: "*.COD" / "*.LIB" )}
  9200. {     number = Nummer, die das erste Sprite aus diesem File bekommen soll }
  9201. {out: Anzahl der aus dem File gelesenen Sprites (0 = Fehler trat auf)     }
  9202. {rem: Die Routine erkennt automatisch, ob es sich bei dem File um ein     }
  9203. {     zelnes Sprite oder eine ganze Spritebibliothek handelt und lädt     }
  9204. {     alle Spritedaten auf den Heap, und zwar derart, daß die Adresse     }
  9205. {     immer auf eine Segmentgrenze fällt. Diese Anfangsadressen werden    }
  9206. {     dann in der Tabelle SPRITEAD[number] abgelegt; sind mehrere Sprites }
  9207. {     in der Datei, so werden sie mit fortlaufender Nummer eingetragen,   }
  9208. {     also number+i }
  9209. LABEL quit_loop;
  9210. VAR p1,p2:Pointer;
  9211.     f:FileOfByte;
  9212.     count:WORD;
  9213.     Header:SpriteHeader;
  9214.     tempName:STRING;
  9215. BEGIN
  9216.  count:=0;  {Zahl der bisher eingelesenen Sprites}
  9217.  
  9218.  tempName:=FindFile(name);
  9219.  IF tempName<>'' THEN name:=tempName;
  9220.  
  9221.  _assign(f,name);
  9222.  {$I-} _reset(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
  9223.  if (ioresult<>0) OR (CompressError<>CompressErr_NoError)
  9224.   THEN BEGIN  {Datei existiert nicht oder nicht unter diesem Pfad}
  9225.         Error:=Err_FileIO;
  9226.         CompressError:=CompressErr_NoError;
  9227.         loadSprite:=0; exit
  9228.        END;
  9229.  IF (number=0) or (number>LoadMAX)
  9230.   THEN BEGIN
  9231.         Error:=Err_InvalidSpritenumber;
  9232.         goto quit_loop;
  9233.        END;
  9234.  WHILE NOT _physicalEOF(f) DO
  9235.  BEGIN
  9236.   {Zunächst den Spriteheader einlesen: }
  9237.   {$I-}     {jetzt den Spriteheader vià BLOCKREAD auf den Heap laden}
  9238.   _blockread(f,Header,Kopf);
  9239.   {$IFDEF IOcheck} {$I+} {$ENDIF}
  9240.  
  9241.   IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
  9242.    THEN BEGIN
  9243.          Error:=Err_FileIO;
  9244.          CompressError:=CompressErr_NoError;
  9245.          goto quit_loop;
  9246.         END;
  9247.   IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
  9248.    THEN BEGIN
  9249.          Error:=Err_NoSprite;
  9250.          goto quit_loop;
  9251.         END;
  9252.   {noch genug Platz da?}
  9253.   IF (Header.SpriteLength+15>MaxAvail+SPRITESIZE[number+count])
  9254.    THEN BEGIN 
  9255.          Error:=Err_NotEnoughMemory;
  9256.          goto quit_loop;
  9257.         END;
  9258.  
  9259.   FreeSpriteMem(number+count);  {evtl. alten Speicher freigeben}
  9260.  
  9261.   {Jetzt eigentliche Spritedaten einlesen: }
  9262.   getmem(p1,Header.SpriteLength+15);       {genug Platz reservieren}
  9263.   SPRITESIZE[number+count]:=Header.SpriteLength+15;
  9264.   SPRITEPTR [number+count]:=p1;
  9265.   IF (LONGINT(p1) mod 16)=0
  9266.    THEN p2:=p1                             {p2 auf Segmentgrenze bringen}
  9267.    ELSE LONGINT(p2):=LONGINT(p1) + (16-LONGINT(p1) mod 16);
  9268.  
  9269.   MOVE(Header,p2^,Kopf);  {Spriteheader auf Heap bringen}
  9270.   LONGINT(p1):=LONGINT(p2)+Kopf;   {zeigt genau hinter den Header}
  9271.  
  9272.   {$I-}     {jetzt den "Rest" des Sprites laden}
  9273.   _blockread(f,p1^,Header.SpriteLength-Kopf);
  9274.   {$IFDEF IOcheck} {$I+} {$ENDIF}
  9275.   IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
  9276.    THEN BEGIN
  9277.          Error:=Err_FileIO;
  9278.          CompressError:=CompressErr_NoError;
  9279.          goto quit_loop;
  9280.         END;
  9281.  
  9282.   {der Spritenummer zuordnen:}
  9283.   spritead[number+count]:=(longint(p2) shr 16)
  9284.                          +(longint(p2) and 65535) shr 4;
  9285.   INC(count);
  9286.  
  9287.   IF (NOT _physicalEOF(f)) AND
  9288.      ( (NOT f.komprimiert) OR (_logicalEOF(f)) )
  9289.    THEN Resync(f)
  9290.  END;
  9291.  
  9292. quit_loop: ;
  9293.  _close(f);
  9294.  loadSprite:=count
  9295. END;
  9296.  
  9297. FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
  9298. { in: name   = Name des zu ladenden Sprite-Files (Typ: "*.COD" / "*.LIB" )}
  9299. {     number = 0..255 = Tilenummer für das erste Sprite der Datei         }
  9300. {out: Anzahl der aus dem File gelesenen Tiles (0 = Fehler trat auf)       }
  9301. {rem: Die Routine erkennt automatisch, ob es sich bei dem File um ein     }
  9302. {     einzelnes Sprite oder eine ganze Spritebibliothek handelt und lädt  }
  9303. {     alle Sprites, zerlegt diese in Tiles und legt sie in der 4.Grafik-  }
  9304. {     seite ab, beginnend mit der übergebenen Nummer number               }
  9305. {     Da eine Kachel 16x16 Punkte groß ist, müssen die Sprites ein Viel-  }
  9306. {     faches von 16 Punkten in x- und y-Richtung sein                     }
  9307. {     Enthält die Datei mehrere Tiles, so werden sie zeilenweise geladen, }
  9308. {     jede Zeile dabei in der Reihenfolge von links nach rechts           }
  9309. LABEL quit_loop;
  9310. TYPE split=RECORD loword,hiword:WORD END;
  9311. VAR p1:Pointer;
  9312.     ad,p:LONGINT;
  9313.     f:FileOfByte;
  9314.     count,ZielOfs,step,yoffset:WORD;
  9315.     pSeg,pOfs:ARRAY[0..3] OF WORD;
  9316.     Breite_in_Tiles,Hoehe_in_Tiles,x,y,i,zeilen:BYTE;
  9317.     Header:SpriteHeader;
  9318.     tempName:STRING;
  9319. BEGIN
  9320.  count:=0;  {Zahl der bisher eingelesenen Sprites}
  9321.  tempName:=FindFile(name);
  9322.  IF tempName<>'' THEN name:=tempName;
  9323.  _assign(f,name);
  9324.  {$I-} _reset(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
  9325.  if (ioresult<>0) OR (CompressError<>CompressErr_NoError)
  9326.   THEN BEGIN  {Datei existiert nicht oder nicht unter diesem Pfad}
  9327.         Error:=Err_FileIO;
  9328.         CompressError:=CompressErr_NoError;
  9329.         LoadTile:=0; exit
  9330.        END;
  9331.  WHILE NOT _physicalEOF(f) DO
  9332.  BEGIN
  9333.   {Zunächst den Spriteheader einlesen: }
  9334.   {$I-}     {jetzt den Spriteheader vià BLOCKREAD auf den Heap laden}
  9335.   _blockread(f,Header,Kopf);
  9336.   {$IFDEF IOcheck} {$I+} {$ENDIF}
  9337.  
  9338.   IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
  9339.    THEN BEGIN
  9340.          Error:=Err_FileIO;
  9341.          CompressError:=CompressErr_NoError;
  9342.          goto quit_loop;
  9343.         END;
  9344.   IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
  9345.    THEN BEGIN
  9346.          Error:=Err_NoTile;  {oder Err_NoSprite!}
  9347.          goto quit_loop
  9348.         END;
  9349.   IF (Header.Breite_in_4er_Gruppen MOD 4<>0) OR
  9350.      (Header.Hoehe_in_Zeilen MOD 16<>0)   {Größe Vielfaches von 16?}
  9351.    THEN BEGIN
  9352.          Error:=Err_NoTile;
  9353.          goto quit_loop
  9354.         END
  9355.    ELSE BEGIN {ja, Anzahl Tiles in diesem Spritefile ermitteln}
  9356.          Breite_in_Tiles:=Header.Breite_in_4er_Gruppen SHR 2;
  9357.          Hoehe_in_Tiles :=Header.Hoehe_in_Zeilen SHR 4;
  9358.          step:=Breite_in_Tiles*4; {Schrittweite beim laden}
  9359.         END;
  9360.   IF (Header.SpriteLength>MaxAvail)    {noch genug Platz da?}
  9361.    THEN BEGIN
  9362.          Error:=Err_NotEnoughMemory;
  9363.          goto quit_loop;
  9364.         END;
  9365.  
  9366.   {Jetzt eigentliche Spritedaten einlesen: }
  9367.   getmem(p1,Header.SpriteLength);      {genug Platz reservieren}
  9368.  
  9369.   {$I-}     {jetzt den "Rest" des Sprites laden}
  9370.   _blockread(f,p1^,Header.SpriteLength-Kopf);
  9371.   {$IFDEF IOcheck} {$I+} {$ENDIF}
  9372.   IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
  9373.    THEN BEGIN
  9374.          Error:=Err_FileIO;
  9375.          CompressError:=CompressErr_NoError;
  9376.          goto quit_loop;
  9377.         END;
  9378.  
  9379.   ad:=(LONGINT(split(p1).HiWord) SHL 4) + split(p1).LoWord - Kopf;
  9380.   FOR i:=0 TO 3 DO
  9381.    BEGIN
  9382.     p:=ad+Header.Zeiger_auf_Plane[i]; pSeg[i]:=p SHR 4; pOfs[i]:=p AND $F;
  9383.    END;
  9384.  
  9385.   FOR y:=0 TO Pred(Hoehe_in_Tiles) DO
  9386.    BEGIN
  9387.     yoffset:=y*Breite_in_Tiles*16*(16 DIV 4);
  9388.     FOR x:=0 TO Pred(Breite_in_Tiles) DO
  9389.      BEGIN
  9390.       IF count+number>255
  9391.        THEN BEGIN
  9392.              Error:=Err_InvalidTileNumber;
  9393.              goto quit_loop
  9394.             END;
  9395.       ZielOfs:=(number+count) SHL 6;
  9396.       FOR i:=0 TO 3 DO
  9397.        BEGIN
  9398.         PORTW[$3C4]:=(TranslateTab[i] SHL 8) + 2;
  9399.         FOR zeilen:=0 TO 15 DO
  9400.          BEGIN
  9401.           move(mem[pSeg[i]:pOfs[i] + yoffset + zeilen*step + x*(16 DIV 4)],
  9402.                mem[SCROLLADR:ZielOfs + zeilen*(16 DIV 4)],
  9403.                16 DIV 4);
  9404.          END;
  9405.        END;
  9406.  
  9407.       INC(count);
  9408.      END;
  9409.    END;
  9410.   FreeMem(p1,Header.SpriteLength);      {Speicher freigeben}
  9411.   IF (NOT _physicalEOF(f)) AND
  9412.      ( (NOT f.komprimiert) OR (_logicalEOF(f)) )
  9413.    THEN Resync(f)
  9414.  END;
  9415.  
  9416. quit_loop: ;
  9417.  _close(f);
  9418.  LoadTile:=count
  9419. END;
  9420.  
  9421. PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
  9422. { in: (x1,y1) = linke obere Ecke des Bereiches (virtuelle Koord.)}
  9423. {     (x2,y2) = dto., rechte untere Ecke}
  9424. {out: (BackX1,BackY1), (BackX2,BackY2) = auf 16er Raster gerundete Koord.  }
  9425. {     XTiles, YTiles = Breite und Höhe des gewählten Bereiches in Kacheln  }
  9426. {rem: Die li. obere Ecke wird nach links oben gezogen, die re. untere nach }
  9427. {     rechts unten!}
  9428. {     Die Anwendung dieser Routine ist natürlich nur für den Hintergrund-  }
  9429. {     modus SCROLLING sinnvoll}
  9430. BEGIN
  9431.  BackX1:=x1 AND $FFF0; BackX2:=x2 OR $F;
  9432.  BackY1:=y1 AND $FFF0; BackY2:=y2 OR $F;
  9433.  xtiles:=succ(BackX2-BackX1) shr 4;
  9434.  ytiles:=succ(BackY2-BackY1) shr 4;
  9435.  IF (xtiles OR ytiles)<=0
  9436.   THEN Error:=Err_InvalidCoordinates
  9437.  ELSE IF xtiles*ytiles>MaxTiles
  9438.   THEN Error:=Err_BackgroundToBig;
  9439. END;
  9440.  
  9441. PROCEDURE SetBackgroundMode(mode:BYTE);
  9442. { in: mode = gewünschter Hintergrundmodus STATIC oder SCROLLING}
  9443. {out: Backgroundmode = gesetzter Modus STATIC/SCROLLING}
  9444. BEGIN
  9445.  IF (mode<>STATIC) AND (mode<>SCROLLING)
  9446.   THEN Error:=Err_InvalidMode
  9447.   ELSE Backgroundmode:=mode
  9448. END;
  9449.  
  9450. PROCEDURE MakeTileArea(FirstTile:BYTE; TileWidth,TileHeight:INTEGER);
  9451. { in: FirstTile = Anfangsindex der ersten Kachel}
  9452. {     TileWidth = Breite (in Kacheln) des zu wiederholenden Rechtecks}
  9453. {     TileHeight= dto., Höhe}
  9454. {     BackX1,BackY1,BackX2,BackY2= per SetScrollRange()-Aufruf gesetzter}
  9455. {      Hintergrundbereich}
  9456. {out: Beginnend mit Kachel FirstTile wurden die TileWidth*TileHeight }
  9457. {     nächsten Kacheln als sich wiederholendes Rechteck der Breite   }
  9458. {     TileWidth und Höhe TileHeight in den Hintergrund übernommen.   }
  9459. {     Auf diese Art wurde der gesamte definierte Hintergrund gebildet}
  9460. {     Bspw. sorgt ein Aufruf der Form MakeTileArea(7,3,2) für folgendes}
  9461. {     Layout: }
  9462. {     ┌──┬──┬──┬──┬──┬──┬──┬    }
  9463. {     │ 7│ 8│ 9│ 7│ 8│ 9│ 7│... }
  9464. {     ├──┼──┼──┼──┼──┼──┼──┼    }
  9465. {     │10│11│12│10│11│12│10│... }
  9466. {     ├──┼──┼──┼──┼──┼──┼──┼    }
  9467. {     │ 7│ 8│ 9│ 7│ 8│ 9│ 7│... }
  9468. {     ├──┼──┼──┼──┼──┼──┼──┼    }
  9469. {     │10│11│12│10│11│12│10│... }
  9470. {     ├──┼──┼──┼──┼──┼──┼──┼    }
  9471. {       .  .  .  .  .  .  .     }
  9472. {       .  .  .  .  .  .  .     }
  9473. {rem: Der Aufruf dieser Routine ist nur sinnvoll, wenn SCROLLING als}
  9474. {     Hintergrundmodus verwendet wird und SetScrollRange() aufgerufen}
  9475. {     wurde!}
  9476. VAR GY,StartRowTile,
  9477.     x,y:INTEGER;
  9478. BEGIN
  9479.   IF (TileWidth>0) AND (TileHeight>0)
  9480.    THEN BEGIN
  9481.          FOR y:=0 TO ytiles-1 DO
  9482.           BEGIN
  9483.            gy:=BackY1+(y SHL 4); {y-Koordinate für diese Zeile}
  9484.            {Index der 1.Tile der aktuellen Zeile berechnen:}
  9485.            StartRowTile:=(y MOD TileHeight)*TileWidth+FirstTile;
  9486.            FOR x:=0 TO xtiles-1 DO
  9487.             PutTile(BackX1+(x SHL 4),gy,StartRowTile+(x MOD TileWidth));
  9488.           END
  9489.         END
  9490.  
  9491.    ELSE Error := Err_InvalidCoordinates
  9492. END;
  9493.  
  9494. PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
  9495. { in: x,y   = virtuelle Koordinate, an die die Kachel plaziert werden soll}
  9496. {     TileNr= Nummer der zu plazierenden Kachel}
  9497. {out: - }
  9498. {rem: Der Punkt (x,y) wird zunächst auf 16er Raster gebracht!  }
  9499. {     Die Anwendung dieser Routine ist nur für den Hintergrund-}
  9500. {     modus SCROLLING sinnvoll}
  9501. VAR index:WORD;
  9502. BEGIN
  9503.  ASM
  9504.     MOV AX,x        {berechne relativen X-Abstand vom linken Rand des}
  9505.     SUB AX,BackX1   {definierten Bereiches in "x" mittels der Formel:}
  9506.     SAR AX,1        { x := ((x AND $FFF0) - BackX1) DIV 16  (nicht:  }
  9507.     SAR AX,1        {SHR 4)!  "AND $FFF0" kann dabei entfallen, da in}
  9508.     SAR AX,1        {BackX1 die letzte Hex-Ziffer $0 ist!            }
  9509.     SAR AX,1
  9510.     MOV x,AX
  9511.  
  9512.     MOV AX,y        {dto. für Abstand der y-Koordinate vom oberen }
  9513.     SUB AX,BackY1   {Rand: y := ((y AND $FFF0) - BackY1) DIV 16   }
  9514.     SAR AX,1
  9515.     SAR AX,1
  9516.     SAR AX,1
  9517.     SAR AX,1
  9518.     MOV y,AX
  9519.  END;
  9520.  
  9521.  IF (x<0) OR (x>=XTiles) OR (y<0) OR (y>=YTiles)
  9522.   THEN Error:=Err_InvalidCoordinates
  9523.   ELSE BEGIN {jede Kachelzeile ist XTiles breit, jede Kachel 16x16 Punkte}
  9524.         index:=y*XTiles+x;  {eigentlich: (x MOD XTiles)}
  9525.         BackTile[Succ(index)]:=TileNr;  {"Succ", um BackTile[0] freizuhalten}
  9526.        END;
  9527. END;
  9528.  
  9529. FUNCTION GetTile(x,y:INTEGER):BYTE;
  9530. { in: x,y   = virtuelle Koordinate, von der die Kachel gelesen werden soll}
  9531. {out: Nummer der Kachel an dieser Stelle}
  9532. {rem: Der Punkt (x,y) wird zunächst auf 16er Raster gebracht!}
  9533. {     Liegt der Punkt außerhalb des definierten Bereiches, so wird die}
  9534. {     Offscreen-Kachel BackTile[0] zurückgegeben}
  9535. BEGIN
  9536.  ASM
  9537.     MOV AX,x        {berechne relativen X-Abstand vom linken Rand des}
  9538.     SUB AX,BackX1   {definierten Bereiches in "x" mittels der Formel:}
  9539.     SAR AX,1        { x := ((x AND $FFF0) - BackX1) DIV 16  (nicht:  }
  9540.     SAR AX,1        {SHR 4)!  "AND $FFF0" kann dabei entfallen, da in}
  9541.     SAR AX,1        {BackX1 die letzte Hex-Ziffer $0 ist!            }
  9542.     SAR AX,1
  9543.     MOV x,AX
  9544.  
  9545.     MOV AX,y        {dto. für Abstand der y-Koordinate vom oberen }
  9546.     SUB AX,BackY1   {Rand: y := ((y AND $FFF0) - BackY1) DIV 16   }
  9547.     SAR AX,1
  9548.     SAR AX,1
  9549.     SAR AX,1
  9550.     SAR AX,1
  9551.     MOV y,AX
  9552.  END;
  9553.  
  9554.  IF (x<0) OR (x>=XTiles) OR (y<0) OR (y>=YTiles)
  9555.   THEN GetTile:=BackTile[0]
  9556.   ELSE GetTile:=BackTile[y*XTiles+x+1]
  9557. END;
  9558.  
  9559. PROCEDURE SetOffscreenTile(TileNr:BYTE);
  9560. { in: TileNr= Nummer der zu plazierenden Kachel}
  9561. {out: - }
  9562. {rem: Alle Bildschirmteile, die außerhalb des durch SetBackground-}
  9563. {     ScrollRange definierten Bereiches liegen, erhalten TileNr als}
  9564. {     Muster zugewiesen }
  9565. {     Die Anwendung dieser Routine ist nur für den Hintergrund-    }
  9566. {     modus SCROLLING sinnvoll}
  9567. BEGIN
  9568.  BackTile[0]:=TileNr
  9569. END;
  9570.  
  9571. PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
  9572. { in: Sp = SpriteLADEnummer, dessen Modusbyte verändert werden soll}
  9573. {out: M  = Methode, mit der Sp ab sofort dargestellt werden soll:   }
  9574. {          Display_NORMAL, Display_FAST, Display_SHADOW oder        }
  9575. {          Display_SHADOWEXACT                                      }
  9576. {rem: Existiert das Sprite noch nicht oder ist der Modus nicht er-  }
  9577. {     laubt, so geschieht nichts!                                   }
  9578. VAR ad:WORD;
  9579. BEGIN
  9580.  ad:=SPRITEAD[Sp];
  9581.  IF ad=0 THEN Error:=Err_InvalidSpriteNumber {Sprite muß schon geladen sein}
  9582.  ELSE IF (M<Display_NORMAL) OR (M>Display_SHADOWEXACT)
  9583.   THEN Error:=Err_InvalidMode  {nur diese 4 Modi sind zulässig}
  9584.  ELSE MEM[ad:Modus]:=M
  9585. END;
  9586.  
  9587. FUNCTION GetModeByte(Sp:WORD):BYTE;
  9588. { in: Sp = SpriteLADEnummer, dessen Modusbyte abgefragt werden soll}
  9589. {out: Methode, die für Sp momentan gesetzt ist: Display_NORMAL,    }
  9590. {     Display_FAST, Display_SHADOW, Display_SHADOWEXACT bzw.       }
  9591. {     Display_UNKNOWN, wenn das Sprite noch nicht geladen wurde!   }
  9592. VAR ad:WORD;
  9593. BEGIN
  9594.  ad:=SPRITEAD[Sp];
  9595.  IF (ad=0)
  9596.   THEN GetModeByte:=Display_UNKNOWN     {Sprite noch nicht geladen}
  9597.   ELSE GetModeByte:=MEM[SPRITEAD[Sp]:Modus]
  9598. END;
  9599.  
  9600. PROCEDURE FillBackground(color:BYTE);
  9601. { in: color = Füllfarbe für die Hintergrundseite BACKGNDPAGE         }
  9602. {     BACKGNDADR = Zeiger auf Hintergrundspeicher}
  9603. {out: Die Grafikseite BACKGNDPAGE wurde mit der Farbe "Color" gefüllt}
  9604. {rem: Die Routine ist nur für den Hintergrundmodus STATIC sinnvoll   }
  9605. BEGIN
  9606.  IF EMSused
  9607.   THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  9608.  FillChar(MEM[BACKGNDADR:0],4*PAGESIZE,color);
  9609. END;
  9610.  
  9611. PROCEDURE FillPage(pa:WORD; color:BYTE);
  9612. { in: pa    = die Seite, die gefüllt werden soll (0..3)}
  9613. {     color = Füllfarbe für die Seite}
  9614. {out: Grafikseite "pa" wurde mit der Farbe "Color" gefüllt}
  9615. {rem: Sinnvoll sind nur die Seiten 0,1 und BACKGNDPAGE,   }
  9616. {     jedoch ist SCROLLPAGE ebenfalls erlaubt}
  9617. BEGIN
  9618.  IF (pa<0) OR (pa>SCROLLPAGE)
  9619.   THEN Error:=Err_InvalidPageNumber
  9620.  ELSE IF pa=BACKGNDPAGE
  9621.   THEN FillBackground(color)
  9622.   ELSE BEGIN
  9623.         portw[$3C4]:=$0F02; {im Map-Mask Register alle 4 Ebenen selektieren}
  9624.         fillchar(MEM[Segment_Adr[pa]:0],PAGESIZE,Color)
  9625.        END;
  9626. END;
  9627.  
  9628. PROCEDURE GetBackgroundFromPage(pa:WORD);
  9629. {in : pa = 0 oder 1 }
  9630. {out: -             }
  9631. {rem: Der Hintergrundspeicher BACKGNDPAGE wird mit dem Inhalt der }
  9632. {     angegebenen Grafikseite gefüllt.}
  9633. {     Die Routine ist nur für den Hintergrundmodus STATIC sinnvoll}
  9634. VAR p:POINTER;
  9635. BEGIN
  9636.  IF (pa<>0) AND (pa<>1) AND (pa<>SCROLLPAGE)
  9637.   THEN Error:=Err_InvalidPageNumber
  9638.   ELSE BEGIN
  9639.         IF EMSused
  9640.          THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  9641.        ASM
  9642.         MOV ES,BACKGNDADR
  9643.         MOV SI,pa
  9644.         SHL SI,1
  9645.         ADD SI,OFFSET Segment_Adr-StartIndex*2
  9646.         LODSW
  9647.         MOV DS,AX
  9648.         XOR SI,SI
  9649.         XOR DI,DI
  9650.  
  9651.         MOV DX,3CEh
  9652.         MOV AX,0004h  {Leseplane 0}
  9653.         MOV BX,PAGESIZE / 2
  9654.  
  9655.         {DS:SI = Quellzeiger, ES:DI = Zielzeiger, BX = Anzahl Worte }
  9656.         {AX = Zugriffsmaske auf Leseplane 0, DX = Registerport dafür}
  9657.  
  9658.         CLI
  9659.         OUT DX,AX {Plane 0 anwählen}
  9660.         MOV CX,BX
  9661.         REP MOVSW {Planedaten speichern}
  9662.         XOR SI,SI {SI rücksetzen}
  9663.  
  9664.         INC AH    {Plane 1 anwählen}
  9665.         OUT DX,AX
  9666.         MOV CX,BX
  9667.         REP MOVSW
  9668.         XOR SI,SI
  9669.  
  9670.         INC AH    {Plane 2 anwählen}
  9671.         OUT DX,AX
  9672.         MOV CX,BX
  9673.         REP MOVSW
  9674.         XOR SI,SI
  9675.  
  9676.         INC AH    {Plane 3 anwählen}
  9677.         OUT DX,AX
  9678.         MOV CX,BX
  9679.         REP MOVSW
  9680.  
  9681.         STI
  9682.         MOV AX,SEG @DATA
  9683.         MOV DS,AX
  9684.        END
  9685.        END;
  9686. END;
  9687.  
  9688. PROCEDURE WritePage(name:STRING; pa:WORD);
  9689. { in: name     = Filename für das abzuspeichernde Bild}
  9690. {     pa       = abzuspeichernde Seite (0..3) }
  9691. {     PAGESIZE = Größe einer Bitplane       }
  9692. {     PICHeader= einzutragende Kennung für Bilderdateien}
  9693. {out: - }
  9694. {rem: Die Grafik auf Seite "pa" wurde in der Datei "name" als Bitmap abgelegt}
  9695. {     Diese Datei ist 4*PAGESIZE+3 = 64003 Bytes groß: 320x200 Punkte zu je  }
  9696. {     1 Byte plus Length(PICHeader) als Kennung}
  9697. VAR f:FILE;
  9698.     i,oldMode:BYTE;
  9699.     fehler:BOOLEAN;
  9700. BEGIN
  9701.  IF (pa<0) OR (pa>SCROLLPAGE)
  9702.   THEN BEGIN
  9703.         Error:=Err_InvalidPageNumber; exit
  9704.        END;
  9705.  {$I-}
  9706.  Assign(f,name); fehler:=IOResult<>0;
  9707.  Rewrite(f,1);   fehler:=fehler OR (IOResult<>0);
  9708.  BlockWrite(f,PICHeader[1],Length(PICHeader));
  9709.  fehler:=fehler OR (IOResult<>0);
  9710.  {$IFDEF IOcheck} {$I+} {$ENDIF}
  9711.  IF fehler
  9712.   THEN BEGIN
  9713.         {$I-} Close(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
  9714.         Error:=Err_FileIO; exit
  9715.        END;
  9716.  
  9717.  IF pa<>BACKGNDPAGE
  9718.   THEN BEGIN {VRAM nach Disk}
  9719.         port[$3ce]:=5;       {alten Lese-/Schreibmodus merken}
  9720.         oldMode:=port[$3cf];
  9721.         port[$3cf]:=$40;     {Lesemodus 0 setzen}
  9722.         FOR i:=0 TO 3 DO
  9723.          BEGIN
  9724.           portw[$3CE]:=4+(i shl 8); {Lese-Plane anwählen}
  9725.           {$I-}
  9726.           BlockWrite(f,mem[Segment_Adr[pa]:0],PAGESIZE);
  9727.           {$IFDEF IOcheck} {$I+} {$ENDIF}
  9728.           fehler:=fehler OR (IOResult<>0);
  9729.          END;
  9730.          port[$3ce]:=5;       {alten Lese-/Schreibmodus setzen}
  9731.          port[$3cf]:=oldMode;
  9732.        END
  9733.   ELSE BEGIN {RAM nach Disk}
  9734.         IF EMSused
  9735.          THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  9736.         FOR i:=0 TO 3 DO
  9737.          BEGIN
  9738.           {$I-}
  9739.           BlockWrite(f,MEM[BACKGNDADR:BACKtab[i]],PAGESIZE);
  9740.           {$IFDEF IOcheck} {$I+} {$ENDIF}
  9741.           fehler:=fehler OR (IOResult<>0);
  9742.          END
  9743.        END;
  9744.  {$I-}
  9745.  Close(f);
  9746.  {$IFDEF IOcheck} {$I+} {$ENDIF}
  9747.  fehler:=fehler OR (IOResult<>0);
  9748.  IF fehler THEN Error:=Err_FileIO
  9749. END;
  9750.  
  9751. PROCEDURE LoadPage(name:STRING; pa:WORD);
  9752. { in: name     = Filename für das zu ladende Bild}
  9753. {     pa       = Zielseite, in die das Bild geladen werden soll (0..3) }
  9754. {     PAGESIZE = Größe einer Bitplane    }
  9755. {     PICHeader= Kennung für Bilderdateien }
  9756. {out: - }
  9757. {rem: Die Bitmap-Grafik in der Datei "name" wurde in die Seite "pa" geladen}
  9758. VAR f:FileOfByte;
  9759.     i,oldMode:BYTE;
  9760.     fehler:BOOLEAN;
  9761.     s:STRING[3];
  9762.     splane:WORD;
  9763.     p1,p2:POINTER;
  9764.     tempName:STRING;
  9765. TYPE tempArray=ARRAY[1..PAGESIZE] OF BYTE;
  9766. VAR  temp:^tempArray;
  9767. BEGIN
  9768.  IF (pa<0) OR (pa>SCROLLPAGE)
  9769.   THEN BEGIN
  9770.         Error:=Err_InvalidPageNumber; exit
  9771.        END;
  9772.  tempName:=FindFile(name);
  9773.  IF tempName<>'' THEN name:=tempName;
  9774.  {$I-}
  9775.  _Assign(f,name); fehler:=IOResult<>0;
  9776.  _Reset(f);       fehler:=fehler OR (IOResult<>0);
  9777.  s[0]:=PICHeader[0];
  9778.  _BlockRead(f,s[1],Length(PICHeader));
  9779.  fehler:=fehler OR (IOResult<>0) OR (CompressError<>CompressErr_NoError);
  9780.  {$IFDEF IOcheck} {$I+} {$ENDIF}
  9781.  IF fehler
  9782.   THEN BEGIN
  9783.         {$I-} _Close(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
  9784.         Error:=Err_FileIO;
  9785.         CompressError:=CompressErr_NoError;
  9786.         exit
  9787.        END
  9788.   ELSE IF (_FileSize(f)<>4*PAGESIZE+Length(PICHeader)) OR (s<>PICHeader)
  9789.   THEN BEGIN
  9790.         {$I-} _Close(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
  9791.         Error:=Err_NoPicture;
  9792.         exit
  9793.        END;
  9794.  
  9795.  IF pa<>BACKGNDPAGE
  9796.   THEN BEGIN {Disk nach VRAM}
  9797.         ASM cli END;
  9798.         port[$3ce]:=5;       {alten Lese-/Schreibmodus merken}
  9799.         oldMode:=port[$3cf];
  9800.         New(temp);
  9801.         ASM sti END;
  9802.         FOR i:=0 TO 3 DO
  9803.          BEGIN
  9804.           {$I-}
  9805.           _BlockRead(f,temp^[1],PAGESIZE);
  9806.           {$IFDEF IOcheck} {$I+} {$ENDIF}
  9807.           fehler:=fehler OR (IOResult<>0);
  9808.           splane:=2+(TranslateTab[i] shl 8); {welche Schreib-Plane}
  9809.           p1:=@temp^[1];
  9810.           p2:=ptr(Segment_Adr[pa],0);
  9811.           ASM
  9812.            cli
  9813.            mov dx,$3CE      {Schreibmodus 0 wählen}
  9814.            mov ax,$4005
  9815.            out dx,ax
  9816.  
  9817.            mov ax,splane    {Schreibplane anwählen}
  9818.            mov dx,$3C4
  9819.            out dx,ax
  9820.  
  9821.            les di,p2
  9822.            lds si,p1
  9823.            mov cx,PAGESIZE SHR 1
  9824.            rep movsw
  9825.  
  9826.            mov ax,SEG @DATA
  9827.            mov ds,ax
  9828.            sti
  9829.           END;
  9830.  
  9831.          END;
  9832.         portw[$3ce]:=oldMode SHL 8 + 5; {alten Lese-/Schreibmodus setzen}
  9833.         Dispose(temp);
  9834.        END
  9835.   ELSE BEGIN {Disk nach RAM}
  9836.         IF EMSused
  9837.          THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  9838.         FOR i:=0 TO 3 DO
  9839.          BEGIN
  9840.           {$I-}
  9841.           _BlockRead(f,MEM[BACKGNDADR:BACKtab[i]],PAGESIZE);
  9842.           {$IFDEF IOcheck} {$I+} {$ENDIF}
  9843.           fehler:=fehler OR (IOResult<>0);
  9844.          END
  9845.        END;
  9846.  {$I-}
  9847.  _Close(f);
  9848.  {$IFDEF IOcheck} {$I+} {$ENDIF}
  9849.  fehler:=fehler OR (IOResult<>0) OR (CompressError<>CompressErr_NoError);
  9850.  IF fehler THEN Error:=Err_FileIO
  9851. END;
  9852.  
  9853. PROCEDURE WriteBackgroundPage(name:STRING);
  9854. { in: name       = Filename für das abzuspeichernde Hintergrundbild}
  9855. {     BACKGNDPAGE= abzuspeichernde Seite (=2) }
  9856. {     PAGESIZE   = Größe einer Bitplane     }
  9857. {     PICHeader  = einzutragende Kennung für Bilderdateien}
  9858. {out: - }
  9859. {rem: Die Grafik der Hintergrundseite wurde in der Datei "name" abgelegt}
  9860. {     Diese Datei ist 4*PAGESIZE+3 = 64003 Bytes groß: 320x200 Punkte   }
  9861. {     zu je 1 Byte plus Length(PICHeader) als Kennung}
  9862. {     Die Routine ist nur für den Hintergrundmodus STATIC sinnvoll      }
  9863. BEGIN
  9864.  WritePage(name,BACKGNDPAGE)
  9865. END;
  9866.  
  9867. PROCEDURE LoadBackgroundPage(name:STRING);
  9868. { in: name       = Filename für das zu ladende Hintergrundbild}
  9869. {     BACKGNDPAGE= Zielseite, in die das Bild geladen werden soll (=2) }
  9870. {     PAGESIZE = Größe einer Bitplane    }
  9871. {     PICHeader= Kennung für Bilderdateien }
  9872. {out: - }
  9873. {rem: Die Bitmap-Grafik in der Datei "name" wurde in die Hintergrundseite}
  9874. {     BACKGNDPAGE geladen}
  9875. {     Die Routine ist nur für den Hintergrundmodus STATIC sinnvoll}
  9876. BEGIN
  9877.  LoadPage(name,BACKGNDPAGE)
  9878. END;
  9879.  
  9880. PROCEDURE FadeIn(pa,ti,style:WORD);
  9881. { in: pa = Seite, die auf die aktuelle Seite eingeblendet werden soll}
  9882. {     ti = Zeit in Millisekunden, in der dies geschehen soll}
  9883. {     style = Algorithmus der dafür benutzt werden soll}
  9884. {     1-PAGE = aktuell sichtbare Seite}
  9885. {out: Error = Err_InvalidFade, falls nicht zulässiger Alg. gewählt wurde}
  9886. {rem: Grafikmodus muß natürlich bereits initialisiert worden sein}
  9887. {     Sinnvoll ist eigentlich nur pa=BACKGNDPAGE}
  9888.  
  9889.   PROCEDURE WipeIn(pa,time:WORD);
  9890.   { in: pa    = Seite, deren Inhalt sichtbar gemacht werden soll}
  9891.   {     time  = ungefähre Zeit (in Millisekunden), in der das geschehen soll}
  9892.   {     1-PAGE= (sichtbare) Grafikseite, auf die gezeichnet wird}
  9893.   {out: - }
  9894.   {rem: Der Inhalt der Seite "pa" wurde auf die Seite 1-PAGE kopiert}
  9895.   CONST hoehe =40; {Teiler von Succ(YMAX)}
  9896.         breite=40; {Teiler von Succ(XMAX)}
  9897.         br_x  =Succ(XMAX) DIV breite; {Blöcke in X-Richtung}
  9898.         br_y  =Succ(YMAX) DIV hoehe;  {Blöcke in Y-Richtung}
  9899.         n=hoehe*br_x; {Anzahl Aufrufe der Verzögerungsschleife}
  9900.   VAR i,x,y,ploty,plotx:WORD;
  9901.       counter:WORD;
  9902.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  9903.       t:LONGINT;
  9904.       temp:REAL;
  9905.       p:POINTER;
  9906.   BEGIN
  9907.    t:=ClockTicks;
  9908.    counter:=0;
  9909.    temp:=0.0182*time/n;
  9910.    FOR i:=0 TO pred(hoehe) DO
  9911.     FOR x:=0 TO pred(br_x) DO
  9912.      BEGIN
  9913.       FOR y:=0 TO pred(br_y) DO
  9914.        BEGIN
  9915.         IF ODD(x)
  9916.          THEN ploty:=y*hoehe+i          +StartVirtualY
  9917.          ELSE ploty:=y*hoehe+(hoehe-1-i)+StartVirtualY;
  9918.         plotx:=x*breite +StartVirtualX;
  9919.         p:=GetImage(plotx,ploty,plotx+pred(breite),ploty,pa);
  9920.         PutImage(plotx,ploty,p,1-PAGE);
  9921.         FreeImageMem(p);
  9922.        END; {of FOR y}
  9923.       INC(counter);
  9924.       WHILE ClockTicks<t+counter*temp DO BEGIN END;
  9925.      END; {of FOR x}
  9926.   END;
  9927.  
  9928.   PROCEDURE Chaos(pa,time:WORD;m:BYTE);
  9929.   { in: pa    = Seite, deren Inhalt sichtbar gemacht werden soll}
  9930.   {     time  = ungefähre Zeit (in Millisekunden), in der das geschehen soll}
  9931.   {     m     = Art, wie das geschehen soll (1..14)}
  9932.   {     1-PAGE= (sichtbare) Grafikseite, auf die gezeichnet wird}
  9933.   {out: - }
  9934.   {rem: Der Inhalt der Seite "pa" wurde auf die Seite 1-PAGE kopiert}
  9935.   CONST n=Succ(XMAX)*Succ(YMAX);  {Anzahl Bildschirmpunkte}
  9936.         {gute Parameter sind z.B. Summen von Zweierpotenzen +1}
  9937.         para:ARRAY[1..14] OF WORD=
  9938.          (13477,65,337,129,257,513,769,1025,481,4097,5121,177,16385,16897);
  9939.   VAR i,k,x,y:WORD;
  9940.       counter:WORD;
  9941.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  9942.       t:LONGINT;
  9943.       temp:REAL;
  9944.       rand:WORD;
  9945.   BEGIN
  9946.    t:=ClockTicks;
  9947.    counter:=0;
  9948.    rand:=0;
  9949.    IF (m<1) OR (m>14) THEN m:=1;
  9950.    k:=para[m];
  9951.    temp:=0.0182*time/n;
  9952.    FOR i:=0 TO 65535 DO
  9953.     BEGIN
  9954.      ASM {berechne: "x := rand MOD 320" und "y := rand DIV 320"}
  9955.       XOR DX,DX
  9956.       MOV AX,rand
  9957.       MOV BX,XMAX+1
  9958.       DIV BX
  9959.       MOV y,AX
  9960.       MOV x,DX
  9961.      END;
  9962.      IF y<=YMAX
  9963.       THEN PutPixel(StartVirtualX+x,StartVirtualY+y,
  9964.                     PageGetPixel(StartVirtualX+x,StartVirtualY+y,pa));
  9965.      ASM {berechne: rand:=rand*k+1}
  9966.       MOV AX,rand
  9967.       MUL k
  9968.       INC AX
  9969.       MOV rand,AX
  9970.      END;
  9971.      INC(counter);
  9972.      WHILE ClockTicks<t+counter*temp DO BEGIN END;
  9973.     END; {of FOR i}
  9974.   END;
  9975.  
  9976.   PROCEDURE Chaos2(pa,time:WORD;m:BYTE);
  9977.   { in: pa    = Seite, deren Inhalt sichtbar gemacht werden soll}
  9978.   {     time  = ungefähre Zeit (in Millisekunden), in der das geschehen soll}
  9979.   {     m     = Art, wie das geschehen soll (1..1)}
  9980.   {     1-PAGE= (sichtbare) Grafikseite, auf die gezeichnet wird}
  9981.   {out: - }
  9982.   {rem: Der Inhalt der Seite "pa" wurde auf die Seite 1-PAGE kopiert}
  9983.   CONST n=Succ(XMAX)*Succ(YMAX);  {Anzahl Bildschirmpunkte}
  9984.         para:ARRAY[1..1] OF WORD=
  9985.          (39551);
  9986.   VAR i,k,x,y:WORD;
  9987.       counter:WORD;
  9988.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  9989.       t:LONGINT;
  9990.       temp:REAL;
  9991.       rand:WORD;
  9992.   BEGIN
  9993.    t:=ClockTicks;
  9994.    counter:=0;
  9995.    rand:=0;
  9996.    IF (m<1) OR (m>1) THEN m:=1;
  9997.    k:=para[m];
  9998.    temp:=0.0182*time/n;
  9999.    FOR i:=0 TO 65535 DO
  10000.     BEGIN
  10001.      ASM {berechne: "x:=rand MOD 320" und "y:=rand DIV 320"}
  10002.       XOR DX,DX
  10003.       MOV AX,rand
  10004.       MOV BX,XMAX+1
  10005.       DIV BX
  10006.       MOV y,AX
  10007.       MOV x,DX
  10008.      END;
  10009.      PutPixel(StartVirtualX+x,StartVirtualY+y,
  10010.               PageGetPixel(StartVirtualX+x,StartVirtualY+y,pa));
  10011.      ASM {berechne: rand:=(rand+k) MOD n}
  10012.       XOR DX,DX
  10013.       MOV AX,rand
  10014.       ADD AX,k
  10015.       JNC @normal
  10016.       ADD AX,(65536-n)  {Overflow, also korrigieren}
  10017.      @normal:
  10018.       CMP AX,n
  10019.       JB @ok
  10020.       SUB AX,n
  10021.      @ok:
  10022.       MOV rand,AX
  10023.      END;
  10024.      INC(counter);
  10025.      WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10026.     END; {of FOR i}
  10027.   END;
  10028.  
  10029.   PROCEDURE SweepVertical(pa,time:WORD; down:BOOLEAN);
  10030.   { in: pa    = Seite, deren Inhalt sichtbar gemacht werden soll}
  10031.   {     time  = ungefähre Zeit (in Millisekunden), in der das geschehen soll}
  10032.   {     down  = TRUE/FALSE für: von oben nach unten/umgekehrt   }
  10033.   {     1-PAGE= (sichtbare) Grafikseite, auf die gezeichnet wird}
  10034.   {out: - }
  10035.   {rem: Der Inhalt der Seite "pa" wurde auf die Seite 1-PAGE kopiert}
  10036.   CONST n=Succ(YMAX);      {Anzahl Aufrufe der Verzögerungsschleife }
  10037.   VAR y,counter:WORD;
  10038.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  10039.       t:LONGINT;
  10040.       temp:REAL;
  10041.       oldColor,step:BYTE;
  10042.       p:POINTER;
  10043.   BEGIN
  10044.    oldColor:=Color;
  10045.    Color:=white;
  10046.    t:=ClockTicks;
  10047.    counter:=0;
  10048.    temp:=0.0182*time/n;
  10049.    IF down
  10050.     THEN FOR y:=0+StartVirtualY TO YMAX+StartVirtualY DO
  10051.           BEGIN
  10052.            Line(StartVirtualX,y,StartVirtualX+XMAX,y,1-PAGE);
  10053.            INC(counter);
  10054.            WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10055.            p:=GetImage(StartVirtualX,y,StartVirtualX+XMAX,y,pa);
  10056.            PutImage(StartVirtualX,y,p,1-PAGE);
  10057.            FreeImageMem(p);
  10058.           END
  10059.     ELSE FOR y:=YMAX+StartVirtualY DOWNTO 0+StartVirtualY DO
  10060.           BEGIN
  10061.            Line(StartVirtualX,y,StartVirtualX+XMAX,y,1-PAGE);
  10062.            INC(counter);
  10063.            WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10064.            p:=GetImage(StartVirtualX,y,StartVirtualX+XMAX,y,pa);
  10065.            PutImage(StartVirtualX,y,p,1-PAGE);
  10066.            FreeImageMem(p);
  10067.           END;
  10068.    Color:=oldColor
  10069.   END;
  10070.  
  10071.   PROCEDURE SweepHorizontal(pa,time:WORD; left_to_right:BOOLEAN);
  10072.   { in: pa    = Seite, deren Inhalt sichtbar gemacht werden soll}
  10073.   {     time  = ungefähre Zeit (in Millisekunden), in der das geschehen soll}
  10074.   {     left_to_right=TRUE/FALSE für: von links nach rechts/umgekehrt}
  10075.   {     1-PAGE= (sichtbare) Grafikseite, auf die gezeichnet wird}
  10076.   {out: - }
  10077.   {rem: Der Inhalt der Seite "pa" wurde auf die Seite 1-PAGE kopiert}
  10078.   CONST n=Succ(XMAX);      {Anzahl Aufrufe der Verzögerungsschleife }
  10079.   VAR x,counter:WORD;
  10080.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  10081.       t:LONGINT;
  10082.       temp:REAL;
  10083.       oldColor,step:BYTE;
  10084.       p:POINTER;
  10085.   BEGIN
  10086.    oldColor:=Color;
  10087.    Color:=white;
  10088.    t:=ClockTicks;
  10089.    counter:=0;
  10090.    temp:=0.0182*time/n;
  10091.    IF left_to_right
  10092.     THEN FOR x:=0+StartVirtualX TO XMAX+StartVirtualX DO
  10093.           BEGIN
  10094.            Line(x,StartVirtualY,x,StartVirtualY+YMAX,1-PAGE);
  10095.            INC(counter);
  10096.            WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10097.            p:=GetImage(x,StartVirtualY,x,StartVirtualY+YMAX,pa);
  10098.            PutImage(x,StartVirtualY,p,1-PAGE);
  10099.            FreeImageMem(p);
  10100.           END
  10101.     ELSE FOR x:=XMAX+StartVirtualX DOWNTO 0+StartVirtualX DO
  10102.           BEGIN
  10103.            Line(x,StartVirtualY,x,StartVirtualY+YMAX,1-PAGE);
  10104.            INC(counter);
  10105.            WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10106.            p:=GetImage(x,StartVirtualY,x,StartVirtualY+YMAX,pa);
  10107.            PutImage(x,StartVirtualY,p,1-PAGE);
  10108.            FreeImageMem(p);
  10109.           END;
  10110.    Color:=oldColor
  10111.   END;
  10112.  
  10113.   PROCEDURE ScrollVertical(pa,time:WORD; up:BOOLEAN);
  10114.   { in: pa    = Seite, deren Inhalt sichtbar gemacht werden soll}
  10115.   {     time  = ungefähre Zeit (in Millisekunden), in der das geschehen soll}
  10116.   {     up    = TRUE/FALSE für: von unten nach oben/umgekehrt   }
  10117.   {     1-PAGE= (sichtbare) Grafikseite, auf die gezeichnet wird}
  10118.   {out: - }
  10119.   {rem: Der Inhalt der Seite "pa" wurde auf die Seite 1-PAGE kopiert}
  10120.   LABEL oneLine1,oneLine2,oneLine3,oneLine4;
  10121.   CONST n=Succ(YMAX);      {Anzahl Aufrufe der Verzögerungsschleife }
  10122.   VAR counter:WORD;
  10123.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  10124.       t:LONGINT;
  10125.       temp:REAL;
  10126.   BEGIN
  10127.    t:=ClockTicks;
  10128.    counter:=0;
  10129.    temp:=0.0182*time/n;
  10130.    IF pa<>BACKGNDPAGE
  10131.     THEN BEGIN
  10132.           IF up
  10133.            THEN BEGIN {nach oben scrollen}
  10134.                  ASM
  10135.                    MOV DX,3C4h
  10136.                    MOV AX,0F02h  {alle 4 Planes gleichzeitig ansprechen}
  10137.                    OUT DX,AX
  10138.                    MOV DX,3CEh
  10139.                    MOV AX,4105h  {Writemode 1 setzen}
  10140.                    OUT DX,AX
  10141.  
  10142.                    MOV SI,1
  10143.                    SUB SI,PAGE
  10144.                    AND SI,1
  10145.                    SHL SI,1
  10146.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10147.                    LODSW
  10148.                    MOV ES,AX     {ES := Segment_Adr[1 - PAGE] = ^sichtbare Seite}
  10149.  
  10150.                    MOV SI,pa
  10151.                    AND SI,3
  10152.                    SHL SI,1
  10153.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10154.                    LODSW         {AX := Segment_Adr[pa] = ^Quelladresse}
  10155.  
  10156.                    PUSH DS
  10157.                    MOV DX,AX
  10158.                    MOV BX,YMAX*LINESIZE+(LINESIZE-1)  {DX:BX = ^Quelldaten}
  10159.  
  10160.                    MOV AX,YMAX   {AX = Zeilenzähler}
  10161.  
  10162.                  oneLine2:
  10163.                    STD           {rückwärts moven!}
  10164.                    MOV SI,ES     {zuerst alten Inhalt nach oben rollen:}
  10165.                    MOV DS,SI     {DS = ES = sichtbare Grafikseite}
  10166.                    MOV SI,(YMAX-1)*LINESIZE+(LINESIZE-1)  {von vorletzter Grafikzeile}
  10167.                    MOV DI,YMAX*LINESIZE+(LINESIZE-1)      {nach letzter Grafikzeile}
  10168.                    MOV CX,YMAX*LINESIZE  {199 Zeilen}
  10169.                    REP MOVSB
  10170.  
  10171.                    MOV DS,DX     {jetzt neue Zeile sichtbar machen:}
  10172.                    MOV SI,BX     {DS:SI = ^zu movende Zeile}
  10173.                    MOV CX,LINESIZE  {1 Zeile}
  10174.                    REP MOVSB
  10175.  
  10176.                    SUB BX,LINESIZE  {Quellzeiger weitersetzen}
  10177.  
  10178.                    {--- Einschub für Zeitbedingung:}
  10179.                    PUSH AX       {alle noch relevanten Register retten}
  10180.                    PUSH BX
  10181.                    PUSH DX
  10182.                    PUSH ES
  10183.                    MOV AX,SEG @DATA {TP's Datensegment wiederherstellen}
  10184.                    MOV DS,AX
  10185.                    CLD           {sicher ist sicher!}
  10186.                  END;
  10187.                  INC(counter);
  10188.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10189.                  ASM;
  10190.                    POP ES
  10191.                    POP DX
  10192.                    POP BX
  10193.                    POP AX
  10194.                    {--- Einschub Ende}
  10195.  
  10196.                    DEC AX        {alle Zeilen durch?}
  10197.                    JNS oneLine2  {nein, nächste Zeile}
  10198.  
  10199.                    MOV DX,3CEh   {Writemode 0 wiederherstellen}
  10200.                    MOV AX,4005h
  10201.                    OUT DX,AX
  10202.                    POP DS
  10203.                  END;
  10204.                 END
  10205.            ELSE BEGIN {nach unten scrollen}
  10206.                  ASM
  10207.                    MOV DX,3C4h
  10208.                    MOV AX,0F02h  {alle 4 Planes gleichzeitig ansprechen}
  10209.                    OUT DX,AX
  10210.                    MOV DX,3CEh
  10211.                    MOV AX,4105h  {Writemode 1 setzen}
  10212.                    OUT DX,AX
  10213.  
  10214.                    MOV SI,1
  10215.                    SUB SI,PAGE
  10216.                    AND SI,1
  10217.                    SHL SI,1
  10218.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10219.                    LODSW
  10220.                    MOV ES,AX     {ES := Segment_Adr[1-PAGE] = ^sichtbare Seite}
  10221.  
  10222.                    MOV SI,pa
  10223.                    AND SI,3
  10224.                    SHL SI,1
  10225.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10226.                    LODSW         {AX := Segment_Adr[pa] = ^Quelladresse}
  10227.  
  10228.                    PUSH DS
  10229.                    MOV DX,AX
  10230.                    MOV BX,0*LINESIZE  {DX:BX = ^Quelldaten}
  10231.  
  10232.                    MOV AX,YMAX   {AX = Zeilenzähler}
  10233.  
  10234.                  oneLine1:
  10235.                    MOV SI,ES     {zuerst alten Inhalt nach oben rollen:}
  10236.                    MOV DS,SI     {DS = ES = sichtbare Grafikseite}
  10237.                    MOV SI,1*LINESIZE  {von Grafikzeile 1}
  10238.                    MOV DI,0*LINESIZE  {nach Grafikzeile 0}
  10239.                    MOV CX,YMAX*LINESIZE  {199 Zeilen}
  10240.                    REP MOVSB
  10241.  
  10242.                    MOV DS,DX     {jetzt neue Zeile sichtbar machen:}
  10243.                    MOV SI,BX     {DS:SI = ^zu movende Zeile}
  10244.                    MOV CX,LINESIZE  {1 Zeile}
  10245.                    REP MOVSB
  10246.  
  10247.                    ADD BX,LINESIZE  {Quellzeiger weitersetzen}
  10248.  
  10249.                    {--- Einschub für Zeitbedingung:}
  10250.                    PUSH AX       {alle noch relevanten Register retten}
  10251.                    PUSH BX
  10252.                    PUSH DX
  10253.                    PUSH ES
  10254.                    MOV AX,SEG @DATA {TP's Datensegment wiederherstellen}
  10255.                    MOV DS,AX
  10256.                  END;
  10257.                  INC(counter);
  10258.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10259.                  ASM;
  10260.                    POP ES
  10261.                    POP DX
  10262.                    POP BX
  10263.                    POP AX
  10264.                    {--- Einschub Ende}
  10265.  
  10266.                    DEC AX        {alle Zeilen durch?}
  10267.                    JNS oneLine1  {nein, nächste Zeile}
  10268.  
  10269.                    MOV DX,3CEh   {Writemode 0 wiederherstellen}
  10270.                    MOV AX,4005h
  10271.                    OUT DX,AX
  10272.                    POP DS
  10273.                  END;
  10274.                 END;
  10275.          END
  10276.  
  10277.     ELSE BEGIN {pa = BACKGNDPAGE, also von RAM nach VRAM}
  10278.           IF EMSused
  10279.            THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  10280.           IF up
  10281.            THEN BEGIN {nach oben scrollen}
  10282.                  ASM
  10283.                    MOV DX,3C4h
  10284.                    MOV AX,0F02h  {alle 4 Planes gleichzeitig ansprechen}
  10285.                    OUT DX,AX
  10286.  
  10287.                    MOV SI,1
  10288.                    SUB SI,PAGE
  10289.                    AND SI,1
  10290.                    SHL SI,1
  10291.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10292.                    LODSW
  10293.                    MOV ES,AX     {ES := Segment_Adr[1 - PAGE] = ^sichtbare Seite}
  10294.  
  10295.                    PUSH DS
  10296.  
  10297.                    MOV SI,pa
  10298.                    AND SI,3
  10299.                    SHL SI,1
  10300.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10301.                    LODSW         {AX := Segment_Adr[pa] = ^Quelladresse }
  10302.                                  {(für pa=BACKGNDPAGE id. zu BACKGNADR)}
  10303.                    PUSH BP
  10304.  
  10305.                    MOV BP,AX
  10306.                    MOV BX,YMAX*LINESIZE+(LINESIZE-1)-1  {BP:BX = ^Quelldaten}
  10307.  
  10308.                    MOV AX,YMAX   {AX = Zeilenzähler}
  10309.  
  10310.                  oneLine4:
  10311.                    MOV SI,AX
  10312.                    MOV DX,3CEh
  10313.                    MOV AX,4105h  {Writemode 1 setzen}
  10314.                    OUT DX,AX
  10315.                    MOV AX,SI
  10316.  
  10317.                    STD           {rückwärts moven!}
  10318.                    MOV SI,ES     {zuerst alten Inhalt nach oben rollen:}
  10319.                    MOV DS,SI     {DS = ES = sichtbare Grafikseite}
  10320.                    MOV SI,(YMAX-1)*LINESIZE+(LINESIZE-1)  {von vorletzter Grafikzeile}
  10321.                    MOV DI,YMAX*LINESIZE+(LINESIZE-1)      {nach letzter Grafikzeile}
  10322.                    MOV CX,YMAX*LINESIZE  {199 Zeilen}
  10323.                    REP MOVSB
  10324.  
  10325.                    PUSH AX
  10326.                    MOV DX,3CEh   {Writemode 0 setzen}
  10327.                    MOV AX,4005h
  10328.                    OUT DX,AX
  10329.  
  10330.                    MOV DX,3C4h
  10331.                    MOV AX,0102h  {Writeplane 0 selektieren}
  10332.                    OUT DX,AX
  10333.                    MOV DS,BP     {jetzt neue Zeile sichtbar machen:}
  10334.                    MOV SI,BX     {DS:SI = ^zu movende Zeile}
  10335.                    DEC DI        {DI um 1 verringern wg. Wort-Zugriffen}
  10336.                    MOV CX,LINESIZE / 2  {1 Zeile}
  10337.                    REP MOVSW
  10338.  
  10339.                    SHL AH,1      {Writeplane 1 selektieren}
  10340.                    OUT DX,AX
  10341.                    ADD SI,PAGESIZE+LINESIZE
  10342.                    MOV CX,LINESIZE / 2 {1 Zeile}
  10343.                    ADD DI,LINESIZE
  10344.                    REP MOVSW
  10345.  
  10346.                    SHL AH,1      {Writeplane 2 selektieren}
  10347.                    OUT DX,AX
  10348.                    ADD SI,PAGESIZE+LINESIZE
  10349.                    MOV CX,LINESIZE / 2 {1 Zeile}
  10350.                    ADD DI,LINESIZE
  10351.                    REP MOVSW
  10352.  
  10353.                    SHL AH,1      {Writeplane 3 selektieren}
  10354.                    OUT DX,AX
  10355.                    ADD SI,PAGESIZE+LINESIZE
  10356.                    MOV CX,LINESIZE / 2 {1 Zeile}
  10357.                    ADD DI,LINESIZE
  10358.                    REP MOVSW
  10359.  
  10360.                    MOV AH,$F     {alle 4 Planes selektieren}
  10361.                    OUT DX,AX
  10362.  
  10363.                    SUB BX,LINESIZE  {Quellzeiger weitersetzen}
  10364.                    POP AX
  10365.  
  10366.                    {--- Einschub für Zeitbedingung:}
  10367.                    MOV SI,BP     {temporäres BP}
  10368.                    POP BP        {altes BP von TP}
  10369.                    PUSH AX       {alle noch relevanten Register retten}
  10370.                    PUSH BX
  10371.                    PUSH SI
  10372.                    PUSH ES
  10373.                    MOV AX,SEG @DATA {TP's Datensegment wiederherstellen}
  10374.                    MOV DS,AX
  10375.                  END;
  10376.                  INC(counter);
  10377.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10378.                  ASM;
  10379.                    POP ES
  10380.                    POP SI
  10381.                    POP BX
  10382.                    POP AX
  10383.                    PUSH BP
  10384.                    MOV BP,SI
  10385.                    {--- Einschub Ende}
  10386.  
  10387.                    DEC AX        {alle Zeilen durch?}
  10388.                    JNS oneLine4  {nein, nächste Zeile}
  10389.  
  10390.                    POP BP
  10391.                    POP DS
  10392.                  END;
  10393.                 END
  10394.            ELSE BEGIN {nach unten scrollen}
  10395.                  ASM
  10396.                    MOV DX,3C4h
  10397.                    MOV AX,0F02h  {alle 4 Planes gleichzeitig ansprechen}
  10398.                    OUT DX,AX
  10399.  
  10400.                    MOV SI,1
  10401.                    SUB SI,PAGE
  10402.                    AND SI,1
  10403.                    SHL SI,1
  10404.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10405.                    LODSW
  10406.                    MOV ES,AX     {ES := Segment_Adr[1-PAGE] = ^sichtbare Seite}
  10407.  
  10408.                    PUSH DS
  10409.  
  10410.                    MOV SI,pa
  10411.                    AND SI,3
  10412.                    SHL SI,1
  10413.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10414.                    LODSW         {AX := Segment_Adr[pa] = ^Quelladresse }
  10415.                                  {(für pa=BACKGNDPAGE id. zu BACKGNADR)}
  10416.                    PUSH BP
  10417.  
  10418.                    MOV BP,AX
  10419.  
  10420.                    MOV DX,AX
  10421.                    MOV BX,0*LINESIZE  {DX:BX = ^Quelldaten}
  10422.  
  10423.                    MOV AX,YMAX   {AX = Zeilenzähler}
  10424.  
  10425.                  oneLine3:
  10426.                    MOV SI,AX
  10427.                    MOV DX,3CEh
  10428.                    MOV AX,4105h  {Writemode 1 setzen}
  10429.                    OUT DX,AX
  10430.                    MOV AX,SI
  10431.  
  10432.                    MOV SI,ES     {zuerst alten Inhalt nach oben rollen:}
  10433.                    MOV DS,SI     {DS = ES = sichtbare Grafikseite}
  10434.                    MOV SI,1*LINESIZE  {von Grafikzeile 1}
  10435.                    MOV DI,0*LINESIZE  {nach Grafikzeile 0}
  10436.                    MOV CX,YMAX*LINESIZE  {199 Zeilen}
  10437.                    REP MOVSB
  10438.  
  10439.                    PUSH AX
  10440.                    MOV DX,3CEh   {Writemode 0 setzen}
  10441.                    MOV AX,4005h
  10442.                    OUT DX,AX
  10443.  
  10444.                    MOV DX,3C4h
  10445.                    MOV AX,0102h  {Writeplane 0 selektieren}
  10446.                    OUT DX,AX
  10447.                    MOV DS,BP     {jetzt neue Zeile sichtbar machen:}
  10448.                    MOV SI,BX     {DS:SI = ^zu movende Zeile}
  10449.                    MOV CX,LINESIZE / 2 {1 Zeile}
  10450.                    REP MOVSW
  10451.  
  10452.                    SHL AH,1      {Writeplane 1 selektieren}
  10453.                    OUT DX,AX
  10454.                    ADD SI,PAGESIZE-LINESIZE
  10455.                    MOV CX,LINESIZE / 2 {1 Zeile}
  10456.                    SUB DI,LINESIZE
  10457.                    REP MOVSW
  10458.  
  10459.                    SHL AH,1      {Writeplane 2 selektieren}
  10460.                    OUT DX,AX
  10461.                    ADD SI,PAGESIZE-LINESIZE
  10462.                    MOV CX,LINESIZE / 2  {1 Zeile}
  10463.                    SUB DI,LINESIZE
  10464.                    REP MOVSW
  10465.  
  10466.                    SHL AH,1      {Writeplane 3 selektieren}
  10467.                    OUT DX,AX
  10468.                    ADD SI,PAGESIZE-LINESIZE
  10469.                    MOV CX,LINESIZE / 2  {1 Zeile}
  10470.                    SUB DI,LINESIZE
  10471.                    REP MOVSW
  10472.  
  10473.                    MOV AH,$F     {alle 4 Planes selektieren}
  10474.                    OUT DX,AX
  10475.  
  10476.                    ADD BX,LINESIZE  {Quellzeiger weitersetzen}
  10477.                    POP AX
  10478.  
  10479.                    {--- Einschub für Zeitbedingung:}
  10480.                    MOV SI,BP     {temporäres BP}
  10481.                    POP BP        {altes BP von TP}
  10482.                    PUSH AX       {alle noch relevanten Register retten}
  10483.                    PUSH BX
  10484.                    PUSH SI
  10485.                    PUSH ES
  10486.                    MOV AX,SEG @DATA {TP's Datensegment wiederherstellen}
  10487.                    MOV DS,AX
  10488.                  END;
  10489.                  INC(counter);
  10490.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10491.                  ASM;
  10492.                    POP ES
  10493.                    POP SI
  10494.                    POP BX
  10495.                    POP AX
  10496.                    PUSH BP
  10497.                    MOV BP,SI
  10498.                    {--- Einschub Ende}
  10499.  
  10500.                    DEC AX        {alle Zeilen durch?}
  10501.                    JNS oneLine3  {nein, nächste Zeile}
  10502.  
  10503.                    MOV DX,3CEh   {Writemode 0 wiederherstellen}
  10504.                    MOV AX,4005h
  10505.                    OUT DX,AX
  10506.  
  10507.                    POP BP
  10508.                    POP DS
  10509.                  END;
  10510.                 END;
  10511.          END;
  10512.   END;
  10513.  
  10514.   PROCEDURE ScrollHorizontal(pa,time:WORD; left:BOOLEAN);
  10515.   { in: pa    = Seite, deren Inhalt sichtbar gemacht werden soll}
  10516.   {     time  = ungefähre Zeit (in Millisekunden), in der das geschehen soll}
  10517.   {     left  = TRUE/FALSE für: von links nach rechts/umgekehrt }
  10518.   {     1-PAGE= (sichtbare) Grafikseite, auf die gezeichnet wird}
  10519.   {out: - }
  10520.   {rem: Der Inhalt der Seite "pa" wurde auf die Seite 1-PAGE kopiert}
  10521.   LABEL oneColumn1,oneColumn2,oneColumn3,oneColumn4;
  10522.   CONST n=Pred(LINESIZE);      {Anzahl Aufrufe der Verzögerungsschleife}
  10523.   VAR counter:WORD;
  10524.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  10525.       t:LONGINT;
  10526.       temp:REAL;
  10527.   BEGIN
  10528.    t:=ClockTicks;
  10529.    counter:=0;
  10530.    temp:=0.0182*time/n;
  10531.    IF pa<>BACKGNDPAGE
  10532.     THEN BEGIN
  10533.           IF left
  10534.            THEN BEGIN {nach links scrollen}
  10535.                  ASM
  10536.                    MOV DX,3C4h
  10537.                    MOV AX,0F02h  {alle 4 Planes gleichzeitig ansprechen}
  10538.                    OUT DX,AX
  10539.                    MOV DX,3CEh
  10540.                    MOV AX,4105h  {Writemode 1 setzen}
  10541.                    OUT DX,AX
  10542.  
  10543.                    MOV SI,1
  10544.                    SUB SI,PAGE
  10545.                    AND SI,1
  10546.                    SHL SI,1
  10547.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10548.                    LODSW
  10549.                    MOV ES,AX     {ES := Segment_Adr[1 - PAGE] = ^sichtbare Seite}
  10550.  
  10551.                    MOV SI,pa
  10552.                    AND SI,3
  10553.                    SHL SI,1
  10554.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10555.                    LODSW         {AX := Segment_Adr[pa] = ^Quelladresse}
  10556.  
  10557.                    PUSH DS
  10558.                    MOV DX,AX
  10559.                    MOV BX,0*LINESIZE+0 {DX:BX = ^Quelldaten}
  10560.  
  10561.                    MOV AX,LINESIZE-1   {AX = Spaltenzähler}
  10562.  
  10563.                  oneColumn2:     {alten Schirminhalt nach rechts schieben:}
  10564.                    MOV SI,ES
  10565.                    MOV DS,SI     {DS = ES = sichtbare Grafikseite}
  10566.                    MOV SI,PAGESIZE-2
  10567.                    MOV DI,PAGESIZE-1
  10568.                    MOV CX,PAGESIZE-1
  10569.                    STD
  10570.                    REP MOVSB
  10571.                    CLD
  10572.  
  10573.                    MOV CX,SEG @DATA
  10574.                    MOV DS,CX     {DS = ^TP Daten}
  10575.                    MOV CX,YMAX+1 {CX = Zeilenzähler}
  10576.  
  10577.                    MOV SI,AX
  10578.                    XOR DI,DI
  10579.                    MOV BX,LINESIZE-1
  10580.                    MOV DS,DX     {DS = ^Quelldaten}
  10581.  
  10582.                  @oneRow:        {erste Spalte erneuern:}
  10583.                    MOVSB
  10584.                    ADD SI,BX     {auf nächste Zeile positionieren:}
  10585.                    ADD DI,BX     {geht, weil BX + 1 = LINESIZE}
  10586.                    LOOP @oneRow
  10587.  
  10588.                    {--- Einschub für Zeitbedingung:}
  10589.                    PUSH AX       {alle noch relevanten Register retten}
  10590.                    PUSH DX
  10591.                    PUSH ES
  10592.                    MOV AX,SEG @DATA {TP's Datensegment wiederherstellen}
  10593.                    MOV DS,AX
  10594.                  END;
  10595.                  INC(counter);
  10596.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10597.                  ASM;
  10598.                    POP ES
  10599.                    POP DX
  10600.                    POP AX
  10601.                    {--- Einschub Ende}
  10602.  
  10603.                    DEC AX        {alle Spalten durch?}
  10604.                    JNS oneColumn2  {nein, nächste Spalte}
  10605.  
  10606.                    MOV DX,3CEh   {Writemode 0 wiederherstellen}
  10607.                    MOV AX,4005h
  10608.                    OUT DX,AX
  10609.                    POP DS
  10610.                  END;
  10611.                 END
  10612.            ELSE BEGIN {nach rechts scrollen}
  10613.                  ASM
  10614.                    MOV DX,3C4h
  10615.                    MOV AX,0F02h  {alle 4 Planes gleichzeitig ansprechen}
  10616.                    OUT DX,AX
  10617.                    MOV DX,3CEh
  10618.                    MOV AX,4105h  {Writemode 1 setzen}
  10619.                    OUT DX,AX
  10620.  
  10621.                    MOV SI,1
  10622.                    SUB SI,PAGE
  10623.                    AND SI,1
  10624.                    SHL SI,1
  10625.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10626.                    LODSW
  10627.                    MOV ES,AX     {ES := Segment_Adr[1 - PAGE] = ^sichtbare Seite}
  10628.  
  10629.                    MOV SI,pa
  10630.                    AND SI,3
  10631.                    SHL SI,1
  10632.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10633.                    LODSW         {AX := Segment_Adr[pa] = ^Quelladresse}
  10634.  
  10635.                    PUSH DS
  10636.                    MOV DX,AX
  10637.                    MOV BX,0*LINESIZE+0 {DX:BX = ^Quelldaten}
  10638.  
  10639.                    MOV AX,0      {AX = Spaltenzähler}
  10640.  
  10641.                  oneColumn1:     {alten Schirminhalt nach links schieben:}
  10642.                    MOV SI,ES
  10643.                    MOV DS,SI     {DS = ES = sichtbare Grafikseite}
  10644.                    MOV SI,1
  10645.                    XOR DI,DI
  10646.                    MOV CX,PAGESIZE-1
  10647.                    REP MOVSB
  10648.  
  10649.                    MOV CX,SEG @DATA
  10650.                    MOV DS,CX     {DS = ^TP Daten}
  10651.                    MOV CX,YMAX+1 {CX = Zeilenzähler}
  10652.  
  10653.                    MOV SI,AX
  10654.                    MOV DI,LINESIZE-1
  10655.                    MOV BX,LINESIZE-1
  10656.                    MOV DS,DX     {DS = ^Quelldaten}
  10657.  
  10658.                  @oneRow:        {letzte Spalte erneuern:}
  10659.                    MOVSB
  10660.                    ADD SI,BX     {auf nächste Zeile positionieren:}
  10661.                    ADD DI,BX     {geht, weil BX + 1 = LINESIZE}
  10662.                    LOOP @oneRow
  10663.  
  10664.                    {--- Einschub für Zeitbedingung:}
  10665.                    PUSH AX       {alle noch relevanten Register retten}
  10666.                    PUSH DX
  10667.                    PUSH ES
  10668.                    MOV AX,SEG @DATA {TP's Datensegment wiederherstellen}
  10669.                    MOV DS,AX
  10670.                  END;
  10671.                  INC(counter);
  10672.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10673.                  ASM;
  10674.                    POP ES
  10675.                    POP DX
  10676.                    POP AX
  10677.                    {--- Einschub Ende}
  10678.  
  10679.                    INC AX        {alle Spalten durch?}
  10680.                    CMP AX,LINESIZE
  10681.                    JB oneColumn1  {nein, nächste Spalte}
  10682.  
  10683.                    MOV DX,3CEh   {Writemode 0 wiederherstellen}
  10684.                    MOV AX,4005h
  10685.                    OUT DX,AX
  10686.                    POP DS
  10687.                  END;
  10688.                 END;
  10689.          END
  10690.     ELSE BEGIN {pa = BACKGNDPAGE, also von RAM nach VRAM}
  10691.           IF EMSused
  10692.            THEN EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  10693.           IF left
  10694.            THEN BEGIN {nach links scrollen}
  10695.                  ASM
  10696.                    MOV SI,1
  10697.                    SUB SI,PAGE
  10698.                    AND SI,1
  10699.                    SHL SI,1
  10700.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10701.                    LODSW
  10702.                    MOV ES,AX     {ES := Segment_Adr[1 - PAGE] = ^sichtbare Seite}
  10703.  
  10704.                    MOV SI,pa
  10705.                    AND SI,3
  10706.                    SHL SI,1
  10707.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10708.                    LODSW         {AX := Segment_Adr[pa] = ^Quelladresse}
  10709.  
  10710.                    PUSH DS
  10711.                    MOV DX,AX
  10712.                    MOV BX,0*LINESIZE+0 {DX:BX = ^Quelldaten}
  10713.                    MOV AX,LINESIZE-1 {Spaltenzaehler}
  10714.  
  10715.                  oneColumn4:     {alten Schirminhalt nach rechts schieben:}
  10716.                    MOV SI,DX
  10717.                    MOV DI,AX
  10718.                    MOV DX,3C4h
  10719.                    MOV AX,0F02h  {alle 4 Planes gleichzeitig ansprechen}
  10720.                    OUT DX,AX
  10721.                    MOV DX,3CEh
  10722.                    MOV AX,4105h  {Writemode 1 setzen}
  10723.                    OUT DX,AX
  10724.                    MOV DX,SI
  10725.                    MOV AX,DI
  10726.  
  10727.                    MOV SI,ES
  10728.                    MOV DS,SI     {DS = ES = sichtbare Grafikseite}
  10729.                    MOV SI,PAGESIZE-2
  10730.                    MOV DI,PAGESIZE-1
  10731.                    MOV CX,PAGESIZE-1
  10732.                    STD
  10733.                    REP MOVSB
  10734.                    CLD
  10735.  
  10736.                    MOV CX,SEG @DATA
  10737.                    MOV DS,CX     {DS = ^TP Daten}
  10738.                    MOV CX,YMAX+1 {CX = Zeilenzähler}
  10739.  
  10740.                    MOV SI,AX     {Spaltenzaehler}
  10741.                    XOR DI,DI
  10742.                    MOV BX,LINESIZE-1
  10743.                    MOV DS,DX     {DS = ^Quelldaten}
  10744.  
  10745.                    PUSH AX
  10746.                    PUSH DX
  10747.                    MOV DX,3CEh   {Writemode 0 setzen}
  10748.                    MOV AX,4005h
  10749.                    OUT DX,AX
  10750.  
  10751.                    MOV DX,3C4h
  10752.  
  10753.                  @oneRow0:       {erste Spalte erneuern:}
  10754.                    MOV AX,0802h  {Writeplane 3 selektieren}
  10755.                    OUT DX,AX
  10756.                    MOV AL,[SI +3*PAGESIZE]
  10757.                    MOV ES:[DI],AL
  10758.                    MOV AX,0402h  {Writeplane 2 selektieren}
  10759.                    OUT DX,AX
  10760.                    MOV AL,[SI +2*PAGESIZE]
  10761.                    MOV ES:[DI],AL
  10762.                    MOV AX,0202h  {Writeplane 1 selektieren}
  10763.                    OUT DX,AX
  10764.                    MOV AL,[SI +1*PAGESIZE]
  10765.                    MOV ES:[DI],AL
  10766.                    MOV AX,0102h  {Writeplane 0 selektieren}
  10767.                    OUT DX,AX
  10768.                    MOVSB
  10769.                    ADD SI,BX     {auf nächste Zeile positionieren:}
  10770.                    ADD DI,BX     {geht, weil BX + 1 = LINESIZE}
  10771.                    LOOP @oneRow0
  10772.  
  10773.  
  10774.                    {--- Einschub für Zeitbedingung:}
  10775.                    PUSH ES       {alle noch relevanten Register retten}
  10776.                    MOV AX,SEG @DATA {TP's Datensegment wiederherstellen}
  10777.                    MOV DS,AX
  10778.                  END;
  10779.                  INC(counter);
  10780.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10781.                  ASM;
  10782.                    POP ES
  10783.                    {--- Einschub Ende}
  10784.  
  10785.                    POP DX
  10786.                    POP AX
  10787.                    DEC AX        {alle Spalten durch?}
  10788.                    JNS oneColumn4  {nein, nächste Spalte}
  10789.  
  10790.                    MOV DX,3CEh   {Writemode 0 wiederherstellen}
  10791.                    MOV AX,4005h
  10792.                    OUT DX,AX
  10793.                    POP DS
  10794.                  END;
  10795.                 END
  10796.            ELSE BEGIN {nach rechts scrollen}
  10797.                  ASM
  10798.                    MOV SI,1
  10799.                    SUB SI,PAGE
  10800.                    AND SI,1
  10801.                    SHL SI,1
  10802.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10803.                    LODSW
  10804.                    MOV ES,AX     {ES := Segment_Adr[1 - PAGE] = ^sichtbare Seite}
  10805.  
  10806.                    MOV SI,pa
  10807.                    AND SI,3
  10808.                    SHL SI,1
  10809.                    ADD SI,OFFSET Segment_Adr-StartIndex*2
  10810.                    LODSW         {AX := Segment_Adr[pa] = ^Quelladresse}
  10811.  
  10812.                    PUSH DS
  10813.                    MOV DX,AX
  10814.                    MOV BX,0*LINESIZE+0 {DX:BX = ^Quelldaten}
  10815.  
  10816.                    MOV AX,0      {AX = Spaltenzähler}
  10817.  
  10818.                  oneColumn3:     {alten Schirminhalt nach links schieben:}
  10819.                    MOV SI,DX
  10820.                    MOV DI,AX
  10821.                    MOV DX,3C4h
  10822.                    MOV AX,0F02h  {alle 4 Planes gleichzeitig ansprechen}
  10823.                    OUT DX,AX
  10824.                    MOV DX,3CEh
  10825.                    MOV AX,4105h  {Writemode 1 setzen}
  10826.                    OUT DX,AX
  10827.                    MOV DX,SI
  10828.                    MOV AX,DI
  10829.  
  10830.                    MOV SI,ES
  10831.                    MOV DS,SI     {DS = ES = sichtbare Grafikseite}
  10832.                    MOV SI,1
  10833.                    XOR DI,DI
  10834.                    MOV CX,PAGESIZE-1
  10835.                    REP MOVSB
  10836.  
  10837.                    MOV CX,SEG @DATA
  10838.                    MOV DS,CX     {DS = ^TP Daten}
  10839.                    MOV CX,YMAX+1 {CX = Zeilenzähler}
  10840.  
  10841.                    MOV SI,AX
  10842.                    MOV DI,LINESIZE-1
  10843.                    MOV BX,LINESIZE-1
  10844.                    MOV DS,DX     {DS = ^Quelldaten}
  10845.  
  10846.                    PUSH AX
  10847.                    PUSH DX
  10848.                    MOV DX,3CEh   {Writemode 0 setzen}
  10849.                    MOV AX,4005h
  10850.                    OUT DX,AX
  10851.  
  10852.                    MOV DX,3C4h
  10853.  
  10854.                  @oneRow:        {letzte Spalte erneuern:}
  10855.                    MOV AX,0802h  {Writeplane 3 selektieren}
  10856.                    OUT DX,AX
  10857.                    MOV AL,[SI +3*PAGESIZE]
  10858.                    MOV ES:[DI],AL
  10859.                    MOV AX,0402h  {Writeplane 2 selektieren}
  10860.                    OUT DX,AX
  10861.                    MOV AL,[SI +2*PAGESIZE]
  10862.                    MOV ES:[DI],AL
  10863.                    MOV AX,0202h  {Writeplane 1 selektieren}
  10864.                    OUT DX,AX
  10865.                    MOV AL,[SI +1*PAGESIZE]
  10866.                    MOV ES:[DI],AL
  10867.                    MOV AX,0102h  {Writeplane 0 selektieren}
  10868.                    OUT DX,AX
  10869.                    MOVSB
  10870.                    ADD SI,BX     {auf nächste Zeile positionieren:}
  10871.                    ADD DI,BX     {geht, weil BX + 1 = LINESIZE}
  10872.                    LOOP @oneRow
  10873.  
  10874.                    {--- Einschub für Zeitbedingung:}
  10875.                    PUSH ES       {alle noch relevanten Register retten}
  10876.                    MOV AX,SEG @DATA {TP's Datensegment wiederherstellen}
  10877.                    MOV DS,AX
  10878.                  END;
  10879.                  INC(counter);
  10880.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10881.                  ASM;
  10882.                    POP ES
  10883.                    {--- Einschub Ende}
  10884.  
  10885.                    POP DX
  10886.                    POP AX
  10887.  
  10888.                    INC AX        {alle Spalten durch?}
  10889.                    CMP AX,LINESIZE
  10890.                    JB oneColumn3  {nein, nächste Spalte}
  10891.  
  10892.                    MOV DX,3CEh   {Writemode 0 wiederherstellen}
  10893.                    MOV AX,4005h
  10894.                    OUT DX,AX
  10895.                    POP DS
  10896.                  END;
  10897.                 END;
  10898.          END
  10899.   END;
  10900.  
  10901.   PROCEDURE CircleIn(pa,time:WORD);
  10902.   { in: pa    = Seite, deren Inhalt sichtbar gemacht werden soll}
  10903.   {     time  = ungefähre Zeit (in Millisekunden), in der das geschehen soll}
  10904.   {out: - }
  10905.   {rem: Der Inhalt der Seite "pa" wurde auf die Seite 1-PAGE kopiert}
  10906.   CONST centerX=XMAX DIV 2; {Bildschirmmitte}
  10907.         centerY=YMAX DIV 2;
  10908.         k=189;     {Anzahl Kreise := sqrt(centerX² + centerY²), aufgerundet}
  10909.         adjust=0.707106781; {Kompensation in Diagonalrichtung = 1/sqrt(2) }
  10910.         n=TRUNC(k/adjust);  {Anzahl Aufrufe der Verzögerungsschleife}
  10911.   VAR radqu,x,y,x0,y0,u1,u2,u3,u4,v1,v2,v3,v4:WORD;
  10912.       counter:WORD;
  10913.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  10914.       t:LONGINT;
  10915.       radius,temp:REAL;
  10916.   BEGIN
  10917.    t:=ClockTicks;
  10918.    counter:=0;
  10919.    temp:=0.0182*time/n;
  10920.    x0:=centerX + StartVirtualX;
  10921.    y0:=centerY + StartVirtualY;
  10922.    {FOR true_radius:=1 TO k STEP 1/adjust ist in Pascal leider nicht möglich!}
  10923.    radius:=0.0;
  10924.    REPEAT
  10925.     radqu:=TRUNC(sqr(radius));
  10926.     FOR x:=0 TO TRUNC(radius/sqrt(2)) DO {Oktanden berechnen}
  10927.      BEGIN
  10928.       y:=TRUNC(sqrt(radqu-sqr(x))); {Satz des Pythagoras}
  10929.       u1:=x0-x; v1:=y0-y;           {Achsen- und Punktsymmetrie ausnutzen}
  10930.       u2:=x0+x; v2:=y0+y;
  10931.       u3:=x0-y; v3:=y0-x;
  10932.       u4:=x0+y; v4:=y0+x;
  10933.       PutPixel(u1,v1,PageGetPixel(u1,v1,pa));
  10934.       PutPixel(u1,v2,PageGetPixel(u1,v2,pa));
  10935.       PutPixel(u2,v1,PageGetPixel(u2,v1,pa));
  10936.       PutPixel(u2,v2,PageGetPixel(u2,v2,pa));
  10937.       PutPixel(u3,v3,PageGetPixel(u3,v3,pa));
  10938.       PutPixel(u3,v4,PageGetPixel(u3,v4,pa));
  10939.       PutPixel(u4,v3,PageGetPixel(u4,v3,pa));
  10940.       PutPixel(u4,v4,PageGetPixel(u4,v4,pa));
  10941.      END;
  10942.     radius:=radius+adjust;
  10943.     INC(counter);
  10944.     WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10945.    UNTIL radius>=k;
  10946.   END;
  10947.  
  10948. BEGIN {of FadeIn}
  10949.  IF (pa<0) OR (pa>SCROLLPAGE)
  10950.   THEN Error:=Err_InvalidPageNumber
  10951.   ELSE CASE style OF
  10952.         Fade_Squares :WipeIn(pa,ti);
  10953.         Fade_Moiree1..Fade_Moiree14:Chaos(pa,ti,style+1-Fade_Moiree1);
  10954.         Fade_SweepInFromTop:    SweepVertical(pa,ti,TRUE);
  10955.         Fade_SweepInFromBottom: SweepVertical(pa,ti,FALSE);
  10956.         Fade_SweepInFromLeft:   SweepHorizontal(pa,ti,TRUE);
  10957.         Fade_SweepInFromRight:  SweepHorizontal(pa,ti,FALSE);
  10958.         Fade_ScrollInFromTop:   ScrollVertical(pa,ti,TRUE);
  10959.         Fade_ScrollInFromBottom:ScrollVertical(pa,ti,FALSE);
  10960.         Fade_ScrollInFromLeft:  ScrollHorizontal(pa,ti,TRUE);
  10961.         Fade_ScrollInFromRight: ScrollHorizontal(pa,ti,FALSE);
  10962.         Fade_Circles : CircleIn(pa,ti);
  10963.         Fade_Moiree15:Chaos2(pa,ti,style+1-Fade_Moiree15);
  10964.         else Error:=Err_InvalidFade
  10965.        END;
  10966. END;
  10967.  
  10968.  
  10969. PROCEDURE IntroScroll(n,wait:WORD);
  10970. { in: n    = Anzahl Zeilen, die hochgescrollt werden sollen}
  10971. {     wait = Zeit (in ms), die nach jeder Zeile gewartet werden soll}
  10972. {rem: Das Scrollen beginnt immer auf Seite 0 (=$A000:0000)}
  10973. {     Hinterher muß das Kommando "Screen(1-page)" ausgeführt werden!}
  10974. BEGIN
  10975.  Screen(0); {auf $A000:0000 positionieren}
  10976.  ASM
  10977.     XOR SI,SI                {Adresse von Seite 0 ermitteln = $A000:0000}
  10978.     AND SI,3                 {Pagewert * 2 (da Worteinträge!)}
  10979.     SHL SI,1                 {dazu Startadresse des Feldes addieren}
  10980.     ADD SI,OFFSET Offset_Adr-StartIndex*2  {evtl. Verschiebung korrigieren}
  10981.     LODSW                    {und Wert holen}
  10982.     MOV BX,AX
  10983.     MOV CX,n
  10984.     MOV SI,wait
  10985.  
  10986.   @oneline:
  10987.     ADD BX,LINESIZE
  10988.  
  10989.     CLI                      {Darf keinenfalls unterbrochen werden!}
  10990.     MOV DX,StatusReg
  10991.     @WaitNotHSyncLoop:
  10992.       in   al,dx
  10993.       and  al,1
  10994.       jz  @WaitNotHSyncLoop
  10995.     @WaitHSyncLoop:
  10996.       in   al,dx
  10997.       and  al,1
  10998.       jz   @WaitHSyncLoop
  10999.  
  11000.     MOV DX,CRTAddress        {CRT-Controller}
  11001.     MOV AL,$0D               {LB-Startadress-Register}
  11002.     OUT DX,AL
  11003.     INC DX
  11004.  
  11005.     MOV AL,BL
  11006.     OUT DX,AL                {LB der neuen Startadresse setzen}
  11007.     DEC DX
  11008.     MOV AL,$0C
  11009.     OUT DX,AL
  11010.     INC DX
  11011.     MOV AL,BH                {HB der neuen Startadresse setzen}
  11012.     OUT DX,AL
  11013.     STI
  11014.  
  11015.     PUSH BX
  11016.     PUSH CX
  11017.     PUSH SI
  11018.     PUSH SI
  11019.     CALL CRT.Delay
  11020.     POP SI
  11021.     POP CX
  11022.     POP BX
  11023.     LOOP @oneline
  11024.  
  11025.  END;
  11026. END;
  11027.  
  11028. PROCEDURE CopyVRAMtoVRAM(source,dest:POINTER; len:WORD); ASSEMBLER;
  11029. { in: source = Startadresse}
  11030. {     dest   = Zieladresse }
  11031. {     len    = Länge des zu kopierenden Bereichs}
  11032. {out: - }
  11033. {rem: Die beiden Bereiche dürfen sich nicht überlappen}
  11034. {     Es wird der WriteMode1 verwendet, weshalb die Länge "len" für}
  11035. {     je 4 Bytes zählt: bspw. würde ein Aufruf der Form}
  11036. {     CopyVRAMtoVRAM(Ptr($A000,0),Ptr($A000,PAGESIZE),PAGESIZE) }
  11037. {     eine ganze Seite (4*PAGESIZE = 64000 Bytes) kopieren}
  11038. ASM
  11039.   MOV AX,4105h   {Writemode1 einschalten}
  11040.   MOV DX,3CEh
  11041.   OUT DX,AX
  11042.   MOV AX,0F02h   {alle 4 Planes gleichzeitig ansprechen}
  11043.   MOV DX,3C4h
  11044.   OUT DX,AX
  11045.  
  11046.   MOV BX,DS
  11047.  
  11048.   LES DI,dest
  11049.   LDS SI,source
  11050.   MOV CX,len
  11051.   CLD
  11052.   REP MOVSB
  11053.  
  11054.   MOV DS,BX
  11055.  
  11056.   MOV AX,4005h
  11057.   MOV DX,3CEh
  11058.   OUT DX,AX
  11059. END;
  11060.  
  11061.  
  11062. PROCEDURE InitRoutines;
  11063. { in: USEEMS = TRUE für: EMS-Speicher für BACKGNDPAGE benutzen}
  11064. {out: SpriteN[],SPRITEAD[],SPRITEPTR[],SPRITESIZE[],BackTile[] wurden auf}
  11065. {     "gänzlich leer" initialisiert}
  11066. {     NextSprite[] wurde so gesetzt, daß jedes Sprite sein eigener}
  11067. {     Nachfolger ist}
  11068. {     PAGE, PAGEADR wurden auf die Grafikseite 0 eingestellt}
  11069. {     BACKGNDADR wurde auf die Hintergrundseite gesetzt     }
  11070. {     SCROLLADR wurde auf die scrollbare Hintergrundseite gesetzt}
  11071. {     BACKGROUNDMODE wurde auf STATIC gesetzt}
  11072. {     Als Defaulttile für den scrollbaren Hintergrund wurde #0 gesetzt   }
  11073. {     StartVirtualX,StartVirtualY = 0 (d.h.: virtuelle = absolute Koord.)}
  11074. {     oldMode = alter Grafikmodus}
  11075. {     Error wurde gesetzt, falls keine VGA-Karte im System enthalten ist }
  11076. {     CycleTime = 0, d.h.: keine Mindestzeit für einen Animationszyklus  }
  11077. {     Color = 15, d.h.: weiß }
  11078. {     CurrentFont = Zeiger auf internen Font}
  11079. {     FontHeight, FontWidth = Abmessungen des internen Fonts}
  11080. {     FontType = dessen Typ}
  11081. {     ActualColors = Defaultfarbpalette des Modus $13 }
  11082. {     CRTAddress = Portadresse des CRT-Adressregisters}
  11083. {     StatusReg  = Portadresse des Statusregisters    }
  11084. {     EMSused = TRUE, wenn EMS-Speicher für BACKGNDPAGE alloziert wurde  }
  11085. {     BackgroundEMSHandle = Handle auf allozierten EMS-Block (wenn EMSused=TRUE)}
  11086. {     buf = Zeiger auf allozierten Heap-Block (wenn EMSused=FALSE)       }
  11087. {     Win* Koordinaten wurden auf das gesamte Animationsfenster gesetzt  }
  11088. {     SplitIndex wurde so gesetzt, daß alle Sprites auf das Animations-  }
  11089. {     fenster zurechtgeclipt werden}
  11090. {rem: Diese Prozedur sollte einmal - ganz zu Beginn - zur Initialisierung}
  11091. {     des Animationspaketes aufgerufen werden                            }
  11092. TYPE rec=RECORD lw,hw:WORD END;
  11093. VAR i,adj:WORD;
  11094.  
  11095.     FUNCTION IsVGA:BOOLEAN;
  11096.     BEGIN
  11097.      WITH Regs DO
  11098.       BEGIN
  11099.        AX:=$1A00;
  11100.        Intr($10,Regs);
  11101.        IsVGA:=(AL=$1A) AND    {VGA Identify-Adapter-Function unterstützt?}
  11102.               ( (BL=7) OR (BL=8) )  {VGAMono oder VGAColor - Adapter}
  11103.       END;
  11104.     END;
  11105.  
  11106. BEGIN
  11107.  IF IsVGA THEN Error:=Err_None
  11108.           ELSE BEGIN
  11109.                 Error:=Err_NoVGA;
  11110.                 exit
  11111.                END;
  11112.  
  11113.  SetCycleTime(0);
  11114.  
  11115.  FillChar(SpriteN,SizeOf(SpriteN),0);
  11116.  FillChar(SPRITEAD,SizeOf(SPRITEAD),0);
  11117.  FillChar(SPRITESIZE,SizeOf(SPRITESIZE),0);
  11118.  FillChar(BackTile,SizeOf(BackTile),0);
  11119.  
  11120.  FOR i:=0 TO LoadMAX DO
  11121.   BEGIN
  11122.    NextSprite[i]:=i;
  11123.    SPRITEPTR[i]:=NIL
  11124.   END;
  11125.  
  11126.  BACKGNDADR:=Segment_Adr[BACKGNDPAGE];  {Segmentadresse der Hintergrundseite}
  11127.  
  11128.  PAGE:=0;   {Seite, auf der gezeichnet werden soll}
  11129.  PAGEADR:=Segment_Adr[PAGE];
  11130.  SCROLLADR:=Segment_Adr[SCROLLPAGE];
  11131.  SetBackgroundMode(STATIC);
  11132.  SetOffscreenTile(0);
  11133.  
  11134.  StartVirtualX:=0; StartVirtualY:=0;    {virtuelle = absolute Koordinaten}
  11135.  Color:=white;            {aktuelle Zeichenfarbe sei Weiß }
  11136.  regs.ah:=$f; intr($10,regs); oldMode:=regs.al;
  11137.  
  11138.  ActualColors:=DefaultColors;
  11139.  {SetShadowTab(Schatten) kann entfallen, da Defaultwerte schon gesetzt!}
  11140.  
  11141.  ASM  {ermitteln, ob Farb- oder Monochromdarstellung}
  11142.    MOV DX,3CCh  {Output-Register befragen:}
  11143.    IN AL,DX
  11144.    TEST AL,1    {ist es ein Farbbildschirm?}
  11145.    MOV DX,3D4h
  11146.    JNZ @L1      {ja  }
  11147.    MOV DX,3B4h  {nein}
  11148.   @L1:          {DX = 3B4h / 3D4h = CRTAddress-Register für monochrom/Farbe}
  11149.    MOV CRTAddress,DX
  11150.    ADD DX,6     {DX = 3BAh / 3DAh = Status-Register für monochrom/Farbe}
  11151.    MOV StatusReg,DX
  11152.  END; {of ASM}
  11153.  
  11154.  LoadFont('');  {internen Font laden}
  11155.  SetAnimateWindow(0,0,319,199);
  11156.  
  11157.  EMSused:=FALSE;
  11158.  
  11159.  IF EmsInstalled(BACKGNDADR) AND
  11160.     EMSIsHardWareEMS AND
  11161.     (EMSPagesAvailable>=4) AND
  11162.     (EMSError=0)
  11163.   THEN BEGIN {EMS verwenden}
  11164.         BackgroundEMSHandle:=EMSAllocate(4); {64K allozieren}
  11165.         If EmsError<>0
  11166.          THEN BEGIN {doch nicht}
  11167.                WriteLn ('EMS-Allozierungsfehler!' );
  11168.                EMSRelease(BackgroundEMSHandle);
  11169.               END
  11170.          ELSE BEGIN
  11171.                EMSused:=TRUE;
  11172.                buf:=Ptr(BACKGNDADR,0)
  11173.               END;
  11174.        END;
  11175.  
  11176.  IF NOT EMSused
  11177.   THEN BEGIN {kein, nicht genügend oder falscher EMS-Speicher, Heap benutzen:}
  11178.         New(buf)
  11179.        END
  11180.   ELSE EMSFillFrame(BackgroundEMSHandle); {Zugriff auf EMS vorbereiten}
  11181.  
  11182.  FillChar(buf^,SizeOf(buf^),0);
  11183.  adj:=rec(buf).lw DIV 16;
  11184.  IF (rec(buf).lw MOD 16)<>0
  11185.   THEN inc(adj); {zu aufsteigenden Adressen hin runden}
  11186.  inc(rec(buf).hw,adj);
  11187.  rec(buf).lw:=0;
  11188.  
  11189.  IF rec(buf).lw<>0
  11190.   THEN BEGIN
  11191.         WRITELN('Fehler: buf^ liegt nicht auf Segmentgrenze');
  11192.         Halt
  11193.        END
  11194.   ELSE BEGIN
  11195.         Segment_Adr[BACKGNDPAGE]:=rec(buf).hw;
  11196.         Offset_Adr[BACKGNDPAGE]:=0;
  11197.         BACKGNDADR:=rec(buf).hw
  11198.        END;
  11199.  SetAnimateWindow(0,0,XMAX,YMAX);
  11200.  SetSplitIndex(-1); {=alle clippen}
  11201. END;
  11202.  
  11203. PROCEDURE CloseRoutines;
  11204. { in: oldMode = alter Grafikmodus, auf den zurückgeschaltet werden soll}
  11205. {     EMSused = wurde EMS-Speicher für BACKGNDPAGE verwendet?}
  11206. {     BackgroundEMSHandle = falls ja, so ist dies das Handle darauf}
  11207. {     buf       = falls nein, dann ist dies der Pointer auf normalen Heap}
  11208. {out: - }
  11209. BEGIN
  11210.  regs.al:=oldMode; regs.ah:=0; intr($10,regs);
  11211.  IF EMSused
  11212.   THEN EMSRelease(BackgroundEMSHandle)
  11213.   ELSE Release(buf);
  11214. END;
  11215.  
  11216. FUNCTION GetErrorMessage:STRING;
  11217. { in: Error = Nummer des aufgetretenen Fehlers}
  11218. {out: den Fehler in Worten}
  11219. BEGIN
  11220.  CASE Error OF
  11221.   Err_None:GetErrorMessage:='No Error';
  11222.   Err_NotEnoughMemory:GetErrorMessage:='Not enough memory available on heap';
  11223.   Err_FileIO:GetErrorMessage:='I/O-error with file';
  11224.   Err_InvalidSpriteNumber:GetErrorMessage:='Invalid sprite number used';
  11225.   Err_NoSprite:GetErrorMessage:='No (or corrupted) sprite file';
  11226.   Err_InvalidPageNumber:GetErrorMessage:='Invalid page number used';
  11227.   Err_NoVGA:GetErrorMessage:='No VGA-card found';
  11228.   Err_NoPicture:GetErrorMessage:='No (or corrupted) picture file';
  11229.   Err_InvalidPercentage:GetErrorMessage:='Percentage value must be 0..100';
  11230.   Err_NoTile:GetErrorMessage:='No (or corrupted) tile/sprite file';
  11231.   Err_InvalidTileNumber:GetErrorMessage:='Invalid tile number used';
  11232.   Err_InvalidCoordinates:GetErrorMessage:='Invalid coordinates used';
  11233.   Err_BackgroundToBig:GetErrorMessage:='Background to big for tile-buffer';
  11234.   Err_InvalidMode:GetErrorMessage:='Only STATIC or SCROLLING allowed here';
  11235.   Err_InvalidSpriteLoadNumber:GetErrorMessage:='Invalid spriteload number used';
  11236.   Err_NoPalette:GetErrorMessage:='No (or corrupted) palette file';
  11237.   Err_PaletteWontFit:GetErrorMessage:='Attempt to write beyond palette end';
  11238.   Err_InvalidFade:GetErrorMessage:='Invalid fade style used';
  11239.   Err_NoFont:GetErrorMessage:='No (or corrupted) font file';
  11240.   Err_EMSError:GetErrorMessage:='Problems with EMS memory';
  11241.   ELSE GetErrorMessage:='Unknown error';
  11242.  END;
  11243. END;
  11244.  
  11245. FUNCTION FindFile(P:PathStr):PathStr;
  11246. { in: P = zu suchende Datei, mit Anfangspfad}
  11247. {out: vollständiger Pfad zur Datei}
  11248. {rem: Steht die Datei nicht im angegebenen Verzeichnis, so werden alle}
  11249. {     rekursiv alle Unterverzeichnisse abgesucht.}
  11250. {     Endet die Suche auch dort ergebnislos, so wird '' zurückgegeben}
  11251. VAR D: DirStr;
  11252.     N: NameStr;
  11253.     E: ExtStr;
  11254.     DateiName:STRING[12];
  11255.     temp:PathStr;
  11256.  
  11257.  FUNCTION SearchFile(Pfad:PathStr):PathStr;
  11258.  { in: DateiName = zu suchende Datei}
  11259.  {     Pfad = Anfangssuchpfad für diese Datei}
  11260.  {out: vollständiger Pfad zur Datei oder '', falls Datei nicht gefunden}
  11261.  VAR Datei,Dir:SearchRec;
  11262.  BEGIN
  11263.   FindFirst(Pfad+DateiName,AnyFile,Datei);
  11264.   WHILE DosError=0 DO
  11265.    BEGIN
  11266.     IF (Datei.Attr AND Directory)<>Directory
  11267.      THEN BEGIN
  11268.            SearchFile:=Pfad+Datei.Name;
  11269.            Exit
  11270.           END;
  11271.     FindNext(Datei)
  11272.    END;
  11273.  
  11274.   {hierher, wenn Suche im aktuellen Verzeichnis erfolglos}
  11275.   FindFirst(Pfad+'*.*',Directory,Dir);
  11276.   WHILE DosError=0 DO
  11277.    BEGIN
  11278.     IF ((Dir.Attr AND Directory)=Directory) AND (Dir.Name[1]<>'.')
  11279.      THEN BEGIN {durchsuche nächstes Directory}
  11280.            temp:=SearchFile(Pfad+Dir.Name+'\');
  11281.            IF temp<>''
  11282.         THEN BEGIN {rekursiv gefunden!}
  11283.                   SearchFile:=temp;
  11284.                   Exit
  11285.                  END;
  11286.           END;
  11287.     FindNext(Dir);
  11288.    END;
  11289.   SearchFile:='';
  11290.  END;
  11291.  
  11292. BEGIN
  11293.  FSplit(P,D,N,E);
  11294.  DateiName:=N+E;
  11295.  FindFile:=SearchFile(D)
  11296. END;
  11297.  
  11298.  
  11299.  
  11300. BEGIN
  11301.  
  11302.  InitRoutines;
  11303.  
  11304. END.
  11305.