home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / pascal / anivga12 / anivga.eng < prev    next >
Text File  |  1993-07-11  |  329KB  |  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. {Programtool for the realization of fast animations on the VGA-graphic-     }
  6. {card by: Kai Rohrbacher, 1988-1993, Turbo-Pascal 6.0                       }
  7.  
  8. { Features:                                                                 }
  9.  
  10. { - flickerfree animation by using page-flipping, watching retrace          }
  11. {   signals and usage of a special 256 color mode of the VGA                }
  12. { - sprite movement in any pixel-increments you want                        }
  13. { - arbitrary background image for the animation                            }
  14. { - animations may be restricted to a screen window                         }
  15. { - full use of the VGA's 256-color mode                                    }
  16. { - several sprite display methods available:                               }
  17. {   - sprites can be declared to be transparent with regard to the background  }
  18. {     or other sprites pixel by pixel                                                     }
  19. {   - sprites can change their color depending on the underlying background    }
  20. {     image (-> shadow function)                                            }
  21. { - pixel precise hit-detection routine for sprite collisions built in           }
  22. { - correct clipping of sprites at the screen boundaries when moving on- or }
  23. {   offscreen                                                               }
  24. { - up to 32767 different sprites (default: 1000)                           }
  25. { - up to 32767 sprites may be simultaneously active (default: 500)         }
  26. { - maximal size of each sprite = 64k                                                }
  27. { - maximal size of all loaded sprites only restricted by available memory  }
  28. { - works with virtual coordinates in the range -16000..+16000, thus        }
  29. {   simple creation of horizontal/vertical "scrolling" applications                 }
  30. { - scrolling background image, too                                         }
  31. { - restricting the animations to a window is possible                      }
  32. { - several supporting routines to: draw lines (with built in clipping-     }
  33. {   algorithm), points and graphic-text (incl. clipping), automatic heap    }
  34. {   management for storing/loading sprites, handling background images,     }
  35. {   changing sprite display modes at runtime, adjustment for different      }
  36. {   CPU-clocks, ...                                                         }
  37.  
  38. UNIT ANIVGA;
  39. INTERFACE
  40.  
  41. USES CRT,DOS,Compression;
  42.  
  43. CONST ANIVGAVersion=12; {version number}
  44.       NMAX=499;
  45.       XMAX=319;
  46.       YMAX=199;
  47.       LoadMAX=1000;  {max. number of simultaneously active sprites   }
  48.       LINESIZE=(XMAX+1) DIV 4;    {size of one row = 80 bytes  }
  49.       PAGESIZE=(YMAX+1)*LINESIZE; {200 rows at 320/4 bytes each}
  50.       BACKGNDPAGE=2;
  51.       SCROLLPAGE=3;
  52.  
  53.       STATIC=0;      {constants for background mode}
  54.       SCROLLING=1;
  55.  
  56.       MaxTiles=10000;   {max. number of background tiles   }
  57.       StartVirtualX:INTEGER=0;  {upper left image corner   }
  58.       StartVirtualY:INTEGER=0;
  59.  
  60.       {supported display modes for sprites:       }
  61.       Display_NORMAL=0;   {normal  : transparent for color 0 }
  62.       Display_FAST  =1;   {fast    : don't take background into account}
  63.       Display_SHADOW=2;   {shadow  : color lookup driven by background data}
  64.       Display_SHADOWEXACT=3; {color 0 is transparent for shadows, too   }
  65.       Display_UNKNOWN=255;{error value}
  66.  
  67.       {error codes of the animation package: }
  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);    {possible text directions      }
  99. CONST GraphTextOrientation:FontOrient=horizontal; {actual output direction }
  100.       GraphTextColor:BYTE=white;           {actual text color         }
  101.       GraphTextBackground:BYTE=white;
  102.       CurrentFont:POINTER=NIL;             {pointer to actual font   }
  103.       UpdateOuterArea:BYTE=2;              {update outer background area}
  104.       WinClip:BOOLEAN=FALSE;               {clip pixel to window?     }
  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=       {default palette colors of 256color mode}
  119.  (                                 {read out by using the VGA-BIOS-calls:  }
  120.   (red:  0; green:  0; blue:  0),  { MOV AX,1017h ;read palette registers}
  121.   (red:  0; green:  0; blue: 42),  { XOR BX,BX    ;start with color 0 }
  122.   (red:  0; green: 42; blue:  0),  { MOV CX,100h  ;all 256 colors }
  123.   (red:  0; green: 42; blue: 42),  { LES DX,Ziel  ;to ES:DX   }
  124.   (red: 42; green:  0; blue:  0),  { INT 10h }
  125.   (red: 42; green:  0; blue: 42),  {Attention! These values can/could be   }
  126.   (red: 42; green: 21; blue:  0),  {read out directly from the VGA card not}
  127.   (red: 42; green: 42; blue: 42),  {until a graphic mode has been activated;  }
  128.   (red: 21; green: 21; blue: 21),  {thus, here they were defined "static"}
  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; {global error variable }
  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;           {drawing color for lines}
  385.     ActualColors:Palette; {actual color palette}
  386.     was_cut:BOOLEAN;      {TRUE/FALSE, if "GetImage" had to clip image   }
  387.     left_cut,             {variables set by "GetImage"; if "was_cut" has}
  388.     right_cut,            {been set to TRUE, they will report, where and   }
  389.     top_cut,              {how much of the image had to been clipped      }
  390.     bottom_cut:WORD;      {away                                         }
  391.  
  392.     WinXMIN,WinYMIN,WinXMAX,WinYMAX,WinWidth,WinHeight:WORD;
  393.  
  394.     BackgroundMode:BYTE;
  395.     BackTile:ARRAY[0..MaxTiles] OF BYTE;    {tile memory    }
  396.     XTiles,YTiles:INTEGER;                  {width, height of defined area  }
  397.     BackX1,BackY1,BackX2,BackY2:INTEGER;    {coordinates of the defined area }
  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. {---- for EMS-routines ----}
  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; {buffer for a page    }
  429. VAR  buf:^Puffer; {pointer to it}
  430.  
  431. Const EMSInt = $67;   {interrupt used for EMS     }
  432.       USEEMS = TRUE;  {if FALSE: no usage of existing EMS,          }
  433.                       {if TRUE : use EMS, if it exists   }
  434.  
  435. Var EMSError:BYTE;    {<>0 means: there was an error     }
  436.     BackgroundEMSHandle:WORD;   {access handle for allocated EMS-block   }
  437.     EMSused:BOOLEAN;  {tells, whether EMS is really used or not   }
  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'; {version number}
  502.       StartIndex=0;
  503.       EndIndex=StartIndex+3;
  504.       {offset addresses of the graphic pages (in segment $A000):}
  505.       Offset_Adr:Array[StartIndex..EndIndex] OF WORD=($0000,$3E80,$7D00,$BB80);
  506.       {segment addresses of graphic pages (with offset = 0):}
  507.       Segment_Adr:ARRAY[StartIndex..EndIndex] OF WORD=($A000,$A3E8,$A7D0,$ABB8);
  508.  
  509.       {sprite header format:  }
  510.  
  511.       {0..1   DW offset_ptr_to_plane0_data}
  512.       {2..3   DW offset_ptr_to_plane1_data}
  513.       {4..5   DW offset_ptr_to_plane2_data}
  514.       {6..7   DW offset_ptr_to_plane3_data}
  515.       {8..9   DW width (in groups of 4)     (in German: "Breite")}
  516.       {10..11 DW heigth in rows             (in German: "Höhe")}
  517.       {12..15 DB 1,2,4,8      ; translate-table used for port-accesses}
  518.       {16..17 DW SpriteLength ; length of this sprite in bytes}
  519.       {                       ; now follows the scratch area reserved for temporary}
  520.       {                       ; variables: ("a|b" = used for a and b)}
  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'      ; tag used for sprites}
  532.       {40     DB 1            ; version number}
  533.       {41     DB 0            ; display mode used for sprite}
  534.       {42..43 DW offset_ptr_to_left_boundary_data }
  535.       {44..45 DW offset_ptr_to_right_boundary_data}
  536.       {46..47 DW offset_ptr_to_top_boundary_data }
  537.       {48..49 DW offset_ptr_to_bottom_boundary_data}
  538.       {50..?? DB data }
  539.  
  540.       {for ex.:   xxrxxxxx, with: r=red=40, g=green=45, b=blue=35, x=white=30}
  541.       {           xrgrxxxx}
  542.       {           rbgbrxxx}
  543.  
  544.       {addresses of important values inside the sprite header:   }
  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.       {addresses of temporary variables for the drawing routines:}
  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.       {addresses of temporary variables for the collision detection routine:}
  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); {For mask addressing   }
  580.       PICHeader:STRING[3]='PIC'; {tag in picture files    }
  581.       Schatten :BYTE=70;         {default brightness of shadows  }
  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= {used font:                }
  600.      ((  0,  0,  0,  0,  0,  0), {#0}          {selfmade 6x6 font (ugly..)}
  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;        {determines, which algorithm will be used  }
  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;     {normalized segment addresses }
  867.     SPRITEPTR :ARRAY[0..LoadMAX] OF POINTER;  {full 32-bit-pointers         }
  868.     SPRITESIZE:ARRAY[0..LoadMAX] OF WORD;     {allocated sprite size }
  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 of all Win* variables}
  877.  
  878.     SplitIndex,SplitIndex_mal2:INTEGER; {split index for sprites & clipping}
  879.  
  880.  
  881. {-----------------------------------------------------}
  882.  
  883. PROCEDURE ShadowTab; ASSEMBLER;
  884. {Pseudo-procedure to store the color lookup table into the code segment }
  885. {DO NOT TRY TO CALL THIS "PROCEDURE"!!!                        }
  886. {default values correspond to a darkening to 70% of the original brightness}
  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. {Small pseudo-procedure to store lookup table for the bitmasks into  }
  908. {the code segment, too}
  909. ASM
  910.  DB 1,2,4,8
  911. END;
  912.  
  913. PROCEDURE SetShadowTab(brightness:BYTE);
  914. { in: brightness = wanted brightness for the colors in the shadow area, }
  915. {                  in percentage of the brightness of the original colors    }
  916. {out: ShadowTab  = (best approximation) color table for wanted dimming   }
  917. {     Schatten   = new brightness (Schatten is a global variable!)      }
  918. {rem: default value of ShadowTab is 70% of the original color brightness!  }
  919. {     This routine takes some time (about 4 sec on a 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, as the assembler messes up accesses to the SS-segment}
  931.  ASM
  932.    MOV CX,256 {outer loop-counter     }
  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 {will be addressed via SS!  }
  939.    MOV DL,100
  940.    DIV DL         {AL = tempColors[i].red * brightness DIV 100}
  941.    MOV BL,AL      {BL := AL = new red part   }
  942.  
  943.    LODSB          {dto., for green}
  944.    MUL brightness
  945.    MOV DL,100
  946.    DIV DL
  947.    MOV BH,AL      {...into BH}
  948.  
  949.    LODSB          {dto., for blue}
  950.    MUL brightness
  951.    MOV DL,100
  952.    DIV DL
  953.    MOV DH,AL      {...into DH}
  954.  
  955.      {BL / BH / DH = RGB-parts of the color, for which we are seeking an approximation}
  956.      PUSH CX
  957.      PUSH SI
  958.      PUSH DI
  959.      PUSH BP
  960.  
  961.      MOV DI,65535 {min. square of error (up to now)         }
  962.      MOV CX,256   {walk through all 256 default colors}
  963.      MOV SI,OFFSET ActualColors  {DS:SI = ^ActualColors[]}
  964.     @searchloop:
  965.      MOV AL,BL    {compute difference in red part  }
  966.      SUB AL,[SI]
  967.      JL @noNewMin {new color may not be brighter!    }
  968.      MUL AL       {compute square of error}
  969.      MOV BP,AX
  970.  
  971.      MOV AL,BH    {dto., for green part}
  972.      SUB AL,[SI+1]
  973.      JL @noNewMin
  974.      MUL AL
  975.      ADD BP,AX
  976.      JC @noNewMin {don't tolerate huge color differences     }
  977.  
  978.      MOV AL,DH    {dto., for blue part }
  979.      SUB AL,[SI+2]
  980.      JL @noNewMin
  981.      MUL AL
  982.      ADD AX,BP
  983.      JC @noNewMin
  984.  
  985.      CMP AX,DI      {did we find a better approximation?}
  986.      JAE @noNewMin  {no  }
  987.      MOV DI,AX      {yes, store square of error and color}
  988.      MOV DL,CL
  989.      OR DI,DI       {square of error = 0?}
  990.      JZ @ColorDone  {yes, we can't find any better solution than that! }
  991.  
  992.     @noNewMin:
  993.      ADD SI,3
  994.      LOOP @searchloop
  995.  
  996.        CMP DI,65535   {no color found?      }
  997.        JNE @ColorDone {but yes, nothing to do!}
  998.        MOV CX,256     {no, thus search again    }
  999.        MOV SI,OFFSET ActualColors  {DS:SI = ^ActualColors[]}
  1000.       @searchloop2:
  1001.        LODSB
  1002.        SUB AL,BL      {Diff ≈±2^6 -> square ≈2^12 -> 3 * square < MaxInt   }
  1003.        IMUL AL        {so no overflow is possible}
  1004.        MOV BP,AX
  1005.  
  1006.        LODSB          {dto., for green part}
  1007.        SUB AL,BH
  1008.        IMUL AL
  1009.        ADD BP,AX
  1010.  
  1011.        LODSB          {dto., for blue part }
  1012.        SUB AL,DH
  1013.        IMUL AL
  1014.        ADD AX,BP
  1015.  
  1016.        CMP AX,DI      {did we find a better approximation?}
  1017.        JAE @noNewMin2 {no  }
  1018.        MOV DI,AX      {yes, store square of error and color}
  1019.        MOV DL,CL
  1020.  
  1021.       @noNewMin2:
  1022.        LOOP @searchloop2
  1023.  
  1024.  
  1025.     @ColorDone:     {100h - DL = optimal color found     }
  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      {store into neue_Tabelle[i]  }
  1032.    NEG AL         {AL = 100h - DL = best approximation}
  1033.    STOSB
  1034.  
  1035.    DEC CX         {replacement for "LOOP @outerloop"; next color!}
  1036.    JCXZ @fertig
  1037.    JMP @outerloop
  1038.   @fertig:
  1039.  
  1040.  END; {of ASM}
  1041.  MOVE(neue_Tabelle,@ShadowTab^,256); {activate new color table}
  1042.  Schatten:=brightness
  1043. END;
  1044.  
  1045. PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
  1046. { in: pal = pointer to palette to be set   }
  1047. {     update = TRUE/FALSE for: recompute/don't recompute ShadowTab}
  1048. {out: ActualColors = actual color palette  }
  1049. {rem: palette has been set and evtl., ShadowTab has been recomputed }
  1050. BEGIN
  1051.  IF @pal<>@ActualColors
  1052.   THEN ActualColors:=pal;  {copy palette into ActualColors        }
  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 = pointer to palette memory  }
  1096. {out: pal = actually set 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 = actually set color palette  }
  1114. {     destPal  = destination palette}
  1115. {     AnzSteps = number of interleaving steps}
  1116. {out: ActualColors = destPal}
  1117. {rem: The procedure fades from ActualColors to destPal, and it uses  }
  1118. {     AnzSteps steps to do so}
  1119. {     setting of the palette is synchronised to the vertical retrace,  }
  1120. {     thus each interleaving step lasts 1/70 sec             }
  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; {bring steps into the same segment as pal}
  1129.  oldColors:=ActualColors;
  1130.  pal:=destpal;    {bring pal and oldColors into the same segment}
  1131.  s:=@pal; d:=@ActualColors;
  1132.  FOR i:=0 TO steps-1 DO
  1133.   BEGIN
  1134.    {now use assembler to compute the following sequence:}
  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 = pointer to ActualColors-table  }
  1147.     LDS SI,s  {DS:SI = pointer to pal-table  }
  1148.     MOV BX,OFFSET oldColors-OFFSET pal -1  {DS:SI+BX+1 = pointer to 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. for green}
  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. for blue}
  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 of the palette file (type: "*.PAL") to load  }
  1193. {     number = number for the first color being read in from the file     }
  1194. {     ActualColors = actually set color palette }
  1195. {out: number of colors read from the file (0 = an error occured)          }
  1196. {     pal = color palette read from the file, evtl. filled up}
  1197. {rem: All entries in "pal" which get not overwritten by the file's contents }
  1198. {     will become set to the actually set colors of "ActualColors"; the   }
  1199. {     palette will only become loaded, not actually set!}
  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;  {number of palette entries read in til now    }
  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  {File doesn't exist or at least not with that path }
  1215.         Error:=Err_FileIO;
  1216.         CompressError:=CompressErr_NoError;
  1217.         LoadPalette:=0; exit
  1218.        END;
  1219.  len:=_filesize(f);  {determine file length}
  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; {preset temporary palette with actual colors       }
  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.   {everything went alright: return palette}
  1255.   pal:=TempPal;
  1256.   count:=len DIV 3;
  1257.  
  1258. quitloop: ;
  1259.  _close(f);
  1260.  LoadPalette:=count
  1261. END;
  1262.  
  1263. {Now, all the code pieces follow, which can be used to display a sprite; }
  1264. {the interface used is the same for all of them:                         }
  1265. { in: CX    = number of bytes, which have to be copied from...                                   }
  1266. {     DS:SI = (pointer to source address) to...                          }
  1267. {     ES:BX = (pointer to destination address)                           }
  1268. {     DI    = bitplane (0..3) (=X-coordinate AND 3)                      }
  1269. {     The proper bitmask for selecting the correct write plane has al-   }
  1270. {     ready been set, but not that for the evtl. needed read plane!                }
  1271. {     The routines can rely upon CX being <>0                            }
  1272. {rem: Every routine MUST CONSIST OF EXACTLY 16 bytes and BE FULLY RELO-  }
  1273. {     CATIBLE and MAY NOT CHANGE registers BP,DS and ES!!!!!!!!!!!!!!!!!  }
  1274. {     Besides that, for distinguishtability, the routines must be pair-   }
  1275. {     wise disjoint in their first two bytes!                            }
  1276.  
  1277. PROCEDURE Modus0; ASSEMBLER;
  1278. {mode 0 considers color 0 being transparent for background data     }
  1279. ASM
  1280.    INC CX
  1281.    STC                {change BX such that (together with SI) it can be }
  1282.    SBB BX,SI          {used for accessing the target address}
  1283.  @L1:
  1284.    LODSB              {fetch sprite byte }
  1285.    OR AL,AL           {is it zero?      }
  1286.    LOOPZ @L1          {yes, ignore it   }
  1287.    JCXZ @L2           {all bytes done?  }
  1288.    MOV ES:[BX+SI],AL  {no, store to screen }
  1289.    JMP @L1 {short}    {work on next byte    }
  1290.  @L2:
  1291. END;
  1292.  
  1293. PROCEDURE Modus1; ASSEMBLER;
  1294. {mode 1 writes the data directly to the screen, without further processing }
  1295. ASM
  1296.    MOV DI,BX          {set DI so that the string instructions can be used    }
  1297.    XOR AX,AX          {set AX := 0   }
  1298.    SHR CX,1           {number of words to move       }
  1299.    REP MOVSW          {move data as one block at once }
  1300.    ADC CX,AX          {does a single byte remain?     }
  1301.    REP MOVSB
  1302.    MOV AX,AX          {4 filling bytes; faster than 4 NOPs}
  1303.    MOV AX,AX
  1304. END;
  1305.  
  1306. PROCEDURE Modus2Work; ASSEMBLER;
  1307. {Continuation of mode2 - everything, which didn't fit in the reserved 16    }
  1308. {bytes is placed here}
  1309. ASM
  1310.    OUT DX,AX          {enable read access for correct plane      }
  1311.  
  1312.    PUSH DS            {DS still points to sprite data, but must    }
  1313.                       {point to background!                         }
  1314.    MOV AX,ES          {DS:SI := ES:DI  (source ptr:=dest. ptr)     }
  1315.    MOV DS,AX
  1316.    MOV SI,DI
  1317.    MOV BX,OFFSET ShadowTab   {set pointer to color lookup table    }
  1318.  
  1319.  @L4:
  1320.    LODSB              {get background color...    }
  1321.    SEGCS XLAT         {...use color lookup table to transform}
  1322.    STOSB              {...and display on actual graphic page}
  1323.    LOOP @L4
  1324.  
  1325.    POP DS
  1326. END;
  1327.  
  1328. PROCEDURE Modus2; ASSEMBLER;
  1329. {mode 2 is thought for "shadows" and the like: the sprite's data itself   }
  1330. {will be ignored; instead, the background data underneath the sprite's  }
  1331. {position is read in and these color values will be exchanged against   }
  1332. {those of the color lookup table "ShadowTab" (e.g.: to realize shadows,    }
  1333. {this table should hold a darker color for each of the original colors)  }
  1334. ASM
  1335.    MOV AX,DI          {bring bitplane for read access to AX    }
  1336.    MOV DI,BX          {put dest. addr. into DI for 8086's string instructions}
  1337.    MOV AH,AL          {bring bitplane to highbyte   }
  1338.    MOV AL,4
  1339.    MOV DX,3CEh
  1340.    MOV SI,OFFSET Modus2Work  {sort of hack: "CALL Modus2Work" would be coded }
  1341.    CALL SI                   {RELATIVE - and thus, jump to ever-neverland!  }
  1342. END;
  1343.  
  1344. PROCEDURE Modus3Work; ASSEMBLER;
  1345. {continuation of Modus3 - everything, which didn't fit into 16 bytes        }
  1346. {bytes is placed here}
  1347. ASM
  1348.    STC           
  1349.    SBB BX,SI      {address source and destination with only 1 index register}
  1350.    MOV DX,BP      {save BP-register  }
  1351.    MOV BP,BX
  1352.    MOV BX,OFFSET ShadowTab   {set pointer to color lookup table   }
  1353.  
  1354.  @L1:
  1355.    LODSB              {get sprite data...    }
  1356.    OR AL,AL           {  (ignore color 0 as "transparent")      }
  1357.    LOOPZ @L1
  1358.    JCXZ @L2
  1359.    MOV AL,ES:[BP+SI]  {get background color...    }
  1360.    SEGCS XLAT         {...use color lookup table to transform}
  1361.    MOV ES:[BP+SI],AL  {...and display on actual graphic page}
  1362.    JMP @L1
  1363.  @L2:
  1364.    MOV BP,DX          {restore old contents of BP register }
  1365. END;
  1366.  
  1367. PROCEDURE Modus3; ASSEMBLER;
  1368. {Modus3 is thought for "shadows", too: in this mode, all sprite              }
  1369. {pixels with color <>0 will be processed: the background color, which is     }
  1370. {underneath these pixels will be replaced by the corresponding color entry   }
  1371. {from the table "ShadowTab"                                                  }
  1372. {in other words: this mode is the same as mode 2, with the difference, that  }
  1373. {sprite color 0 is treated as being transparent for shadows!                 }
  1374. ASM
  1375.    MOV DX,3CEh        {prepare access to read plane:     }
  1376.    MOV AX,DI          
  1377.    MOV AH,AL          {load read plane into AH  }
  1378.    MOV AL,4
  1379.    OUT DX,AX          {enable read access for correct plane      }
  1380.    INC CX             {inc. number of bytes by 1 (-> LODSB!) }
  1381.    MOV AX,OFFSET Modus3Work  {trick to make code relocatible!   }
  1382.    CALL AX
  1383. END;
  1384.  
  1385. PROCEDURE Adressen; ASSEMBLER;
  1386. {table with the starting addresses of the 3 routines in the code segment}
  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. {table with graphic rows starting addresses (offset part)}
  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 = segment address of EMS-frame  }
  1429. {     TRUE/FALSE for: EMS installed/not installed      }
  1430. {     Error, EMSError = evtl. error code}
  1431. {rem: If USEEMS=FALSE, the return value will always be FALSE  }
  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-driver exists, but ist must be at least V3.2:}
  1444.         WITH Regs DO
  1445.          BEGIN
  1446.           AH:=$46; Intr(EMSInt,Regs); {fetch version number}
  1447.           EMSInstalled:=AL>=$32;      {version >= 3.2 ?  }
  1448.  
  1449.           AH:=$41; Intr(EMSInt,Regs); {BX=segment address, AH=evtl. error}
  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: returns number of available EMS pages (each one = 16K)}
  1461. {     Error, EMSError = evtl. error code}
  1462. {rem: only call this routine, if EMSinstalled = TRUE!}
  1463. BEGIN
  1464.  WITH Regs DO
  1465.   BEGIN
  1466.    AH:=$42; Intr(EMSInt,Regs); {determine number of available pages}
  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 = #number of pages to allocate}
  1475. {out: handle to this EMS-block     }
  1476. {     Error, EMSError = evtl. error code}
  1477. {rem: pages may not exceed the number of available pages PagesAvail!}
  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 to an allocated EMS-block   }
  1492. {     LogicNr    = logical page number within that EMS-block  }
  1493. {     PhysicalNr = physical page number of the EMS-frame (=0..3) }
  1494. {out: Error, EMSError = evtl. error code}
  1495. {rem: Maps the logical page "LogicNr" of the EMS-area, which has been  }
  1496. {     allocated with the handle "EMSHandle" into the physical EMS-page   }
  1497. {     "PhysicalNr"}
  1498. {     Afterwards, accesses of the form MEM[BACKGNDADR:PhysicalNr*$4000] }
  1499. {     are possible}
  1500. {     PhysicalNr = 0..3}
  1501. {     LogicNr    = 0..allocated number of pages-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 to the EMS-frame (4 pages in size)}
  1514. {out: EMS-frame-buffer has been filled with the first 4 pages}
  1515. {     Error, EMSError = evtl. error code}
  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 to the EMS-block to release   }
  1530. {out: Error, EMSError = evtl. error code}
  1531. {rem: EMS-block has been released}
  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, if EMS is only simulated  }
  1546. {     Error, EMSError = evtl. error code}
  1547. {rem: This check is done as suggested in the LIM4.0-specifications:     }
  1548. {     one logical page is mapped into all physical pages and then the   }
  1549. {     resulting configuration is checked}
  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  {someone having a better idea?}
  1556.   ELSE BEGIN
  1557.         handle:=EMSAllocate(1);
  1558.         With Regs do
  1559.          BEGIN {map logical page #0 into all 4 physical pages          }
  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 of allocated EMS-block   }
  1583. {     BACKGNDADR:0 = start of EMS-frame  }
  1584. {out: The EMS-frame is filled with the wanted 64K data}
  1585. BEGIN
  1586.  EMSFillFrame(EMSHandle); {prepare EMS access         }
  1587. END;
  1588.  
  1589. FUNCTION AT:BOOLEAN;
  1590. { in: - }
  1591. {out: TRUE/FALSE, if the machine is (at least) an AT       }
  1592. BEGIN
  1593.  AT:=MEM[$F000:$FFFE]=$FC
  1594. END;
  1595.  
  1596.  
  1597. PROCEDURE SetCycleTime(milliseconds:WORD);
  1598. { in: min. time for one animation cycle in milliseconds  }
  1599. {out: CycleTime := that value in microseconds  }
  1600. {     TimeFlag  := $80}
  1601. {rem: Because of TimeFlag:=$80, the timing mechanism won't work  }
  1602. {     for the very first animation cycle, yet!                   }
  1603. {     If you don't use the timing mechanism (by supplying a value}
  1604. {     of 0 milliseconds), this will result in IsAT:=$80, that is }
  1605. {     the routine will fake "computer is a PC". Else IsAT=0      }
  1606. BEGIN
  1607.  TimeFlag:=$80;
  1608.  CycleTime:=LONGINT(milliseconds)*LONGINT(1000);
  1609.  IF (milliseconds<>0) AND AT
  1610.   THEN IsAT:=0     {yes, time control mechanism shall be used  }
  1611.   ELSE IsAT:=$80   {no, none possible or not wanted          }
  1612. END;
  1613.  
  1614. PROCEDURE SetSpriteCycle(nr,len:WORD);
  1615. { in: nr  = spriteloadnumber of the first sprite in the cycle      }
  1616. {     len = length of your sprite cycle                         }
  1617. {out: NextSprite[nr] through NextSprite[nr+len-1] have been set }
  1618. {     set such that they build a "ring", that is: together      }
  1619. {     they make up a sprite cycle}
  1620. {rem: If the sprite cycle shall consist of (physical) sprites     }
  1621. {     whose load numbers aren't consecutive, then you           }
  1622. {     have to make the appropriate entries into NextSprite[]    }
  1623. {     yourself manually }
  1624. {     This routine uses spriteLOADnumbers!      }
  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  {last sprite points to first one}
  1632.        END;
  1633. END;
  1634.  
  1635.  
  1636. FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:WORD):POINTER;
  1637. { in: (x1,y1) = upper left corner of the area which shall be stored       }
  1638. {     (x2,y2) = according lower right corner (in virtual coordinates!)    }
  1639. {     pa      = graphic page from which the image should be taken (0..3)  }
  1640. {     StartVirtualX,StartVirtualY = upper left image corner               }
  1641. {out: pointer to heap address where the copied screen area is stored      }
  1642. {     left_cut= evtl. needed left cut off of the image (this determines,  }
  1643. {               how many pixels at the left edge of the image lie outside      }
  1644. {               of the screen)                                            }
  1645. {     right_cut, top_cut, bottom_cut = dto., for other edges              }
  1646. {     was_cut = TRUE/FALSE, if it was necessary/not necessary to clip the }
  1647. {               fetched image                                             }
  1648. {rem: The memory needed will be reserved by the routine automatically     }
  1649. {     If that is impossible (or the image is completely offscreen), the   }
  1650. {     routine will return NIL!                                            }
  1651. {     Only if "was_cut" is set to TRUE, the (global) "..._cut" variables  }
  1652. {     will be set to something other then 0, that is: if the window lies  }
  1653. {     _completely_ offscreen (this means: returned ptr=NIL), then the     }
  1654. {     routine still returns "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);   {compute screen coordinates     }
  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  {window is offscreen                    }
  1663.         GetImage:=NIL;
  1664.         exit
  1665.        END;
  1666.  {cut clipping according to visible screen:}
  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; add 2 words for width & height   }
  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)  {page number must be 0..3  }
  1681.   THEN BEGIN
  1682.         Error:=Err_InvalidPageNumber;
  1683.         GetImage:=NIL;
  1684.         exit
  1685.        END
  1686.   ELSE SegmAdr:=Segment_Adr[pa];
  1687.  GetMem(p,len);         {get memory from the heap      }
  1688.  IF pa<>BACKGNDPAGE
  1689.   THEN ASM {VRAM to RAM  }
  1690.         CLD
  1691.         LES DI,p        {ES:DI = pointer to the acquired memory   }
  1692.         MOV AX,breite
  1693.         STOSW           {store width first...    }
  1694.         MOV AX,hoehe
  1695.         STOSW           {...then store height, followed by the data    }
  1696.  
  1697.         MOV BX,AX       {BX := hoehe (to be used later) }
  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 := offset part of the start address}
  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       {select start plane }
  1713.         MOV DS,SegmAdr
  1714.  
  1715.         {DS:SI = pointer to first byte to store; ES:DI = its target address    }
  1716.         {AH = startplane, AL = 4, BX = number of rows to process  }
  1717.  
  1718.         MOV DX,breite
  1719.         ADD DX,3
  1720.         SHR DX,1
  1721.         SHR DX,1        {DX = number of bytes to store for each row}
  1722.  
  1723.       @L1:
  1724.         MOV CX,DX       {store data of one row        }
  1725.         SHR CX,1        {faster than "REP MOVSB"  }
  1726.         REP MOVSW
  1727.         ADC CX,CX
  1728.         REP MOVSB
  1729.         MOV SI,actualAdr  {increment source pointer by 1 graphic row}
  1730.         ADD SI,LINESIZE
  1731.         MOV actualAdr,SI
  1732.         DEC BX          {decrease row counter   }
  1733.         JNE @L1
  1734.  
  1735.         INC AH          {select next plane     }
  1736.         CMP AH,4
  1737.         JNE @nowrap1    {wrapping the bitplane means: start address  }
  1738.         MOV AH,0        {needs mending: increment address by 1! }
  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        {faster than "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        {faster than "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        {faster than "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); {prepare EMS access         }
  1825.        ASM {RAM to RAM  }
  1826.         CLD
  1827.         LES DI,p        {ES:DI = pointer to the acquired memory   }
  1828.         MOV AX,breite
  1829.         STOSW           {store width first...    }
  1830.         MOV AX,hoehe
  1831.         STOSW           {...then store height, followed by the data    }
  1832.  
  1833.         MOV DX,AX       {DX := hoehe (to be used later) }
  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 := offset part of the start address}
  1842.         AND BX,3
  1843.         MOV AH,BL
  1844.         SHL BX,1
  1845.         ADD SI,[OFFSET BACKTab + BX]  {select start plane }
  1846.         MOV StartAdr,SI
  1847.         MOV actualAdr,SI
  1848.         MOV DS,SegmAdr
  1849.         MOV BX,DX
  1850.  
  1851.         {DS:SI = pointer to first byte to store; ES:DI = its target address    }
  1852.         {AH = startplane, BX = number of rows to process  }
  1853.  
  1854.         MOV DX,breite
  1855.         ADD DX,3
  1856.         SHR DX,1
  1857.         SHR DX,1        {DX = number of bytes to store for each row}
  1858.  
  1859.       @L1:
  1860.         MOV CX,DX       {store data of one row        }
  1861.         SHR CX,1        {faster than "REP MOVSB"  }
  1862.         REP MOVSW
  1863.         ADC CX,CX
  1864.         REP MOVSB
  1865.         MOV SI,actualAdr  {increment source pointer by 1 graphic row}
  1866.         ADD SI,LINESIZE
  1867.         MOV actualAdr,SI
  1868.         DEC BX          {decrease row counter   }
  1869.         JNE @L1
  1870.  
  1871.         INC AH          {select next plane     }
  1872.         ADD StartAdr,PAGESIZE
  1873.         CMP AH,4
  1874.         JNE @nowrap1    {wrapping the bitplane means: start address  }
  1875.         MOV AH,0        {needs mending: increment address by 1! }
  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        {faster than "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        {faster than "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        {faster than "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) = upper left corner of destination (virtual coordinates)    }
  1961. {     p     = pointer to the cutting (returned by GetImage)             }
  1962. {     pa    = graphic page to which the cutting shall be pasted to      }
  1963. {     StartVirtualX,StartVirtualY = upper left image corner             }
  1964. {out: - }
  1965. {rem: The cutting has been properly clipped before being displayed      }
  1966. {     If you supply NIL as pointer, the routine will display nothing    }
  1967. {     That is useful when you are going to use the routine directly on  }
  1968. {     the return value of GetImage!}
  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);   {compute screen coordinates     }
  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;  {Width of a row for the first, second,    }
  1995.  breite2:=(breite + 2) shr 2;  {third and fourth bitplane, respectively}
  1996.  breite3:=(breite + 1) shr 2;
  1997.  breite4:=(breite + 0) shr 2;
  1998.  
  1999.  {Compute starting addresses of the 4 bitplanes; take into account evtl.  }
  2000.  {left cutoff (+4 bytes to jump over "breite" (width) and "hoehe" (height)     }
  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 determines the order in which the points must be read    }
  2008.  {of the heap: 0 = plane order (1,2,3,4); 1 = plane order (2,3,4,1);      }
  2009.  {note that the widths of the bitplane tables                             }
  2010.  {are (and remain) linked to these; therefore, they will be swapped       }
  2011.  {to accomplish that!     }
  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     {displacement of 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, thus: increment address by 1, which   }
  2025.     INC DX             {corresponds to an ajustment of 4 points           }
  2026.     MOV pl_adr1,AX     {(e.g.: pixels (1,5,9,...),(2,6,10,...),(3,7,11,...)}
  2027.     MOV pl_adr2,BX     {and (0,4,8,...); the last bitplane needs a cor-   }
  2028.     MOV pl_adr3,CX     {rection of +1 byte: this results in (4,8,12,...)    }
  2029.     MOV pl_adr4,DX     {(read planes top-down, in alternating order!)     }
  2030.     MOV AX,breite2     {Now the plane widths: }
  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     {displacement of 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. for plane widths: }
  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     {displacement of 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. for plane widths: }
  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:        {precondition here: (pl_adr?,breite?) contain the   }
  2078.                        {source bitplanes/-widths in the correct order      }
  2079.  
  2080.     MOV AX,topcut
  2081.     SUB hoehe,AX       {evtl. adjust height for upper cutoff   }
  2082.     MOV AX,licutoff
  2083.     SUB breite,AX      {dto. for width and left cutoff   }
  2084.  
  2085.     MOV AX,x           {if image would spread over the right screen }
  2086.     ADD AX,breite      {boundary: compute right cutoff              }
  2087.     SUB AX,XMAX+1
  2088.     JLE @no_recutoff
  2089.     SUB breite,AX      {cut off AX points at the right}
  2090.   @no_recutoff:
  2091.  
  2092.     MOV AX,y           {exactly the same for the lower screen border}
  2093.     ADD AX,hoehe
  2094.     SUB AX,YMAX+1
  2095.     JLE @no_bocutoff
  2096.     SUB hoehe,AX       {cut off AX rows at the bottom}
  2097.   @no_bocutoff:
  2098.  END;
  2099.  
  2100.  IF pa<>BACKGNDPAGE
  2101.   THEN ASM {RAM to VRAM  }
  2102.         LDS SI,p
  2103.         ADD pl_adr2,SI     {add pointer's offset part to the plane address    }
  2104.         ADD pl_adr3,SI
  2105.         ADD pl_adr4,SI
  2106.  
  2107.         ADD SI,pl_adr1     {width, height and parts above the screen        }
  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         {use it as write plane    }
  2126.  
  2127.         MOV DX,hoehe
  2128.         MOV DI,actualAdr
  2129.  
  2130.         {DS:SI = pointer to data, ES:DI = dest. address on screen for them  }
  2131.         {AH = bitmask for access, 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        {faster than "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          {select next bitplane; if wrap-around occurs  }
  2154.         CMP AH,16         {from bitplane 3 to bitplane 0, then the start-}
  2155.         JNE @nowrap1      {ing address must be incremented by 1 byte       }
  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        {faster than "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        {faster than "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        {faster than "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); {prepare EMS access         }
  2257.        ASM {RAM to RAM  }
  2258.         MOV AX,DS
  2259.         MOV ES,AX         {ES := old data segment  }
  2260.  
  2261.         LDS SI,p
  2262.         ADD pl_adr2,SI    {add pointer's offset part to the plane address    }
  2263.         ADD pl_adr3,SI
  2264.         ADD pl_adr4,SI
  2265.  
  2266.         ADD SI,pl_adr1    {width, height and parts above the screen        }
  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         {store copy to AL   }
  2279.         SHL BX,1
  2280.         ADD DI,ES:[OFFSET BACKTab + BX]  {use it as write plane    }
  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 = pointer to data, ES:DI = dest. address on screen for them  }
  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        {faster than "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                {select next bitplane; if wrap-around occurs  }
  2312.         ADD StartAdr,PAGESIZE {from bitplane 3 to bitplane 0, then the start-}
  2313.         AND AL,3              {ing address must be incremented by 1 byte       }
  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        {faster than "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                {select next bitplane; if wrap-around occurs  }
  2344.         ADD StartAdr,PAGESIZE {from bitplane 3 to bitplane 0, then the start-}
  2345.         AND AL,3              {ing address must be incremented by 1 byte       }
  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        {faster than "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                {select next bitplane; if wrap-around occurs  }
  2375.         ADD StartAdr,PAGESIZE {from bitplane 3 to bitplane 0, then the start-}
  2376.         AND AL,3              {ing address must be incremented by 1 byte       }
  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        {faster than "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 = pointer to image memory, allocated by GetImage()}
  2411. {out: - }
  2412. {rem: the heap memory allocated for the image has been released   }
  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 = screen page to display (0..1)       }
  2419. {out: - }
  2420. {rem: The display has been switched to graphic page pa                 }
  2421. {     The routine does NOT synchronize on any retrace-signal           }
  2422. {     Sensible page values are only 0 or 1 here, but the routine       }
  2423. {     doesn't make any checks!}
  2424. BEGIN
  2425.  ASM
  2426.     MOV DX,CRTAddress        {CRT-Controller}
  2427.     MOV AL,$0D               {LB-startaddress-register}
  2428.     CLI                      {May not be interrupted!              }
  2429.     OUT DX,AL
  2430.     INC DX
  2431.                              {realize "AX := Offset_Adr[pa]":   }
  2432.     MOV BX,pa
  2433.     MOV SI,BX
  2434.     AND SI,3                 {page value *2 (word-sized entries!)}
  2435.     SHL SI,1                 {add start address of array to that   }
  2436.     ADD SI,OFFSET Offset_Adr-StartIndex*2  {evtl. correct displacement    }
  2437.     LODSW                    {and fetch value}
  2438.     OUT DX,AL                {set LB of new starting address  }
  2439.     DEC DX
  2440.     MOV AL,$0C
  2441.     OUT DX,AL
  2442.     INC DX
  2443.     MOV AL,AH                {set HB of new starting address  }
  2444.     OUT DX,AL
  2445.     STI
  2446.  END;
  2447. END;
  2448.  
  2449. PROCEDURE InitGraph;
  2450. { in: PAGE = actual graphic page }
  2451. {out: - }
  2452. {rem: switches the VGA-card into 320x200x256x4-mode; ATTENTION!      }
  2453. {     This mode is different from mode $13 of the VGA-BIOS!!!          }
  2454. {     The display will be switched to graphic page 1-PAGE            }
  2455. {     The default colors of mode $13 will be set!                    }
  2456. BEGIN
  2457.  ASM
  2458.     MOV AX,0013h   {use BIOS to set graphic mode $13 (320x200x256)   }
  2459.     INT 10h        
  2460.     MOV DX,03C4h   {select memory-mode-register at sequencer port    }
  2461.     MOV AL,04      
  2462.     OUT DX,AL      
  2463.     INC DX         {read in data via the according data register     }
  2464.     IN  AL,DX      
  2465.     AND AL,0F7h    {bit 3 := 0: don't chain the 4 planes}
  2466.     OR  AL,04      {bit 2 := 1: no odd/even mechanism }
  2467.     OUT DX,AL      {activate new settings    }
  2468.     MOV DX,03C4h   {s.a.: address sequencer reg. 2 (=map-mask),...   }
  2469.     MOV AL,02      
  2470.     OUT DX,AL      
  2471.     INC DX
  2472.     MOV AL,0Fh     {...and allow access to all 4 bit maps            }
  2473.     OUT DX,AL      
  2474.     MOV AX,0A000h  {starting with segment A000h, set 8000h logical     }
  2475.     MOV ES,AX      {words = 4*8000h physical words (because of 4     }
  2476.     SUB DI,DI      {bitplanes) to 0                                  }
  2477.     MOV AX,DI      
  2478.     MOV CX,8000h   
  2479.     CLD
  2480.     REP STOSW
  2481.  
  2482.     MOV DX,CRTAddress  {address the underline-location-register at }
  2483.     MOV AL,14h         {the CRT-controller port, read out the according      }
  2484.     OUT DX,AL          {data register:                            }
  2485.     INC DX
  2486.     IN  AL,DX
  2487.     AND AL,0BFh    {bit 6:=0: no double word addressing scheme in}
  2488.     OUT DX,AL      {video RAM                              }
  2489.     DEC DX         
  2490.     MOV AL,17h     {select mode control register     }
  2491.     OUT DX,AL      
  2492.     INC DX
  2493.     IN  AL,DX
  2494.     OR  AL,40h     {bit 6 := 1: memory access scheme=linear bit array      }
  2495.     OUT DX,AL      
  2496.  END;
  2497.  Screen(1-PAGE);   {ALWAYS is the non-actual graphic page the visible one!}
  2498.  SetPalette(DefaultColors,FALSE)  {set default palette, just to be sure!    }
  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 = coordinates of two points, }
  2505. {     Color       = color (0..255)             }
  2506. {     StartVirtualX,StartVirtualY = upper left image corner   }
  2507. {     WinClip     = TRUE, if line shall be clipped to the window area      }
  2508. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
  2509. {out: - }
  2510. {rem: A line has been drawn between the VIRTUAL points (x1,y1) and (x2,y2)  }
  2511. {     using the color COLOR; the routine will take care of transforming    }
  2512. {     the coordinates to absolute screen coordinates and evtl. necessary   }
  2513. {     clipping actions.                                                    }
  2514. {     The line will NOT automatically be taken over into the background      }
  2515. {     image, that is: it will be visible only for one animation cycle (if      }
  2516. {     it shall stay permanent, you have to draw it into the background!)  }
  2517. {     (For that reason, you should call this routine AFTER calling ANIMATE }
  2518. {     because otherwise, the drawn line will vanish at once!)              }
  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); {prepare EMS access         }
  2526.  
  2527.   {first clip line to visible window; use Sutherland-Cohen-algorithm:  }
  2528.   {use 4 bit-codes for: left|right|top|bottom                         }
  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 or 0|XMAX|0|YMAX  }
  2544.  
  2545.      MOV CL,$F         {start with %1111  }
  2546.      MOV AX,x2
  2547.      SUB AX,StartVirtualX   {transform x2 into absolute coordinates}
  2548.      MOV x2,AX
  2549.      CMP AX,BX         {x2 < WindowX1 ?}
  2550.      JL @GC1Punkt2     {yes, don't change flag for "point is left of window"}
  2551.      AND CL,CodeLinks  {no, reset flag       }
  2552.    @GC1Punkt2:
  2553.      CMP AX,SI         {x2 > WindowX2 ?}
  2554.      JG @GC2Punkt2     {yes, don't change flag for "point is right of window"}
  2555.      AND CL,CodeRechts {no, reset flag       }
  2556.    @GC2Punkt2:
  2557.      MOV AX,y2
  2558.      SUB AX,StartVirtualY   {transform y2 into absolute coordinates}
  2559.      MOV y2,AX
  2560.      CMP AX,DI         {y2 < WindowY1 ?}
  2561.      JL @GC3Punkt2     {yes, don't change flag for "point is above window"}
  2562.      AND CL,CodeOben   {no, reset flag       }
  2563.    @GC3Punkt2:
  2564.      CMP AX,WindowY2   {y2 > WindowY2 ?}
  2565.      JG @GC4Punkt2     {yes, don't change flag for "point is below window"}
  2566.      AND CL,CodeUnten
  2567.    @GC4Punkt2:         {CL holds the area code for point 2          }
  2568.  
  2569.      MOV AX,x1
  2570.      SUB AX,StartVirtualX   {transform x1 into absolute coordinates}
  2571.      MOV x1,AX
  2572.      MOV AX,y1
  2573.      SUB AX,StartVirtualY   {transform y1 into absolute coordinates}
  2574.      MOV y1,AX
  2575.  
  2576.    @Punkt1:
  2577.      MOV CH,$F         {start with %1111  }
  2578.      MOV AX,x1
  2579.      CMP AX,BX         {x1 < WindowX1 ?}
  2580.      JL @GC1Punkt1     {yes, don't change flag for "point is left of window"}
  2581.      AND CH,CodeLinks  {no, reset flag       }
  2582.    @GC1Punkt1:
  2583.      CMP AX,SI         {x1 > WindowX2 ?}
  2584.      JG @GC2Punkt1     {yes, don't change flag for "point is right of window"}
  2585.      AND CH,CodeRechts {no, reset flag       }
  2586.    @GC2Punkt1:
  2587.      MOV AX,y1
  2588.      CMP AX,DI         {y1 < WindowY1 ?}
  2589.      JL @GC3Punkt1     {yes, don't change flag for "point is above window"}
  2590.      AND CH,CodeOben   {no, reset flag       }
  2591.    @GC3Punkt1:
  2592.      CMP AX,WindowY2   {y1 > WindowY2 ?}
  2593.      JG @GC4Punkt1     {yes, don't change flag for "point is below window"}
  2594.      AND CH,CodeUnten
  2595.    @GC4Punkt1:         {CH holds the area code for point 1          }
  2596.  
  2597.    {CL holds the area code for point 2, CH the one for point 1}
  2598.  
  2599.      MOV AX,CX
  2600.      AND AL,AH         {Code1 AND Code2 <> 0 ?}
  2601.      JNZ @LineReady    {yes, line is completely outside the window}
  2602.      MOV AX,CX
  2603.      OR AL,AH          {Code1 OR Code2 = 0 ?}
  2604.      JZ @DrawLine      {yes, line is completely inside the window}
  2605.  
  2606.    {Now do the clipping itself:          }
  2607.      MOV AX,CX
  2608.      OR AH,AH          {Code1 =0 ?}
  2609.      JNZ @CL3          {no, everything ok}
  2610.      MOV AX,x1         {yes, swap points!      }
  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     {point1 left of window?   }
  2627.      JZ @CL4                   {no  }
  2628.      {yes, compute new coordinates:  }
  2629.      { y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX1 - X1) }
  2630.      { and 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    {point1 right of window?   }
  2641.      JZ @CL5                   {no  }
  2642.      {yes, compute:}
  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      {point1 above window?        }
  2654.      JZ @CL6                   {no  }
  2655.      {yes, compute:}
  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     {point below window?         }
  2667.      JZ @Punkt1                {no  }
  2668.      {yes, compute:}
  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.    {precondition here: both points have been clipped to the visible window;}
  2681.    {if the line is completely offscreen, the program jumped directly to    }
  2682.    {@LineReady, instead!            }
  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 = new deltaY, DX = new 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 (if CX=0), AX = deltaX, DX = deltaY (if 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 = new X1, CX = new X2, DX = new Y1, BX = new 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, because word-sized}
  2741.      ADD DI,[OFFSET BACKTab + BX]  {get mask for access: BX * 16000    }
  2742.  
  2743.      MOV BL,BACKGNDPAGE   {BH = 0 -> BX = drawing page}
  2744.      SHL BX,1
  2745.      MOV ES,[BX + OFFSET Segment_Adr -StartIndex*2] {ES:DI=pointer to 1.pixel}
  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  {back in plane0, but 1 byte down     }
  2759.    @nowrap1:  {AL = color, SI = ±LINESIZE, ES:DI=pointer to 1.pixel}
  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 = new Y1, BX = new X2, CX = new Y2, DX = new 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, because word-sized}
  2803.      ADD DI,[OFFSET BACKTab + BX]  {get mask for access: BX * 16000    }
  2804.  
  2805.      MOV BL,BACKGNDPAGE   {BH = 0 -> BX = drawing page}
  2806.      SHL BX,1
  2807.      MOV ES,[BX + OFFSET Segment_Adr -StartIndex*2] {ES:DI=pointer to 1.pixel}
  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.      {increment SI<0, check for underflow: }
  2826.      ADD DI,SI
  2827.      JC @nowrap2
  2828.      ADD DI,4*PAGESIZE-1
  2829.      JMP @nowrap2
  2830.    @plus:
  2831.      {increment SI>0, check vor overflow & >= 4*PAGESIZE }
  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 = coordinates of two points, }
  2850. {     Color       = color (0..255)             }
  2851. {     StartVirtualX,StartVirtualY = upper left image corner            }
  2852. {     pa          = graphic page to be drawn upon (0..3)               }
  2853. {     WinClip     = TRUE, if line shall be clipped to the window area      }
  2854. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
  2855. {out: - }
  2856. {rem: A line has been drawn between the VIRTUAL points (x1,y1) and (x2,y2)  }
  2857. {     using the color COLOR; the routine will take care of transforming    }
  2858. {     the coordinates to absolute screen coordinates and evtl. necessary   }
  2859. {     clipping actions.                                                    }
  2860. {     The line will NOT automatically be taken over into the background      }
  2861. {     image, that is: it will be visible only for one animation cycle (if      }
  2862. {     it shall stay permanent, you have to draw it into the background!)  }
  2863. {     (For that reason, you should call this routine AFTER calling ANIMATE }
  2864. {     because otherwise, the drawn line will vanish at once!)              }
  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.   {first clip line to visible window; use Sutherland-Cohen-algorithm:  }
  2876.   {use 4 bit-codes for: left|right|top|bottom                         }
  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 or 0|XMAX|0|YMAX  }
  2892.  
  2893.      MOV CL,$F         {start with %1111  }
  2894.      MOV AX,x2
  2895.      SUB AX,StartVirtualX   {transform x2 into absolute coordinates}
  2896.      MOV x2,AX
  2897.      CMP AX,BX         {x2 < WindowX1 ?}
  2898.      JL @GC1Punkt2     {yes, don't change flag for "point is left of window"}
  2899.      AND CL,CodeLinks  {no, reset flag       }
  2900.    @GC1Punkt2:
  2901.      CMP AX,SI         {x2 > WindowX2 ?}
  2902.      JG @GC2Punkt2     {yes, don't change flag for "point is right of window"}
  2903.      AND CL,CodeRechts {no, reset flag       }
  2904.    @GC2Punkt2:
  2905.      MOV AX,y2
  2906.      SUB AX,StartVirtualY   {transform y2 into absolute coordinates}
  2907.      MOV y2,AX
  2908.      CMP AX,DI         {y2 < WindowY1 ?}
  2909.      JL @GC3Punkt2     {yes, don't change flag for "point is above window"}
  2910.      AND CL,CodeOben   {no, reset flag       }
  2911.    @GC3Punkt2:
  2912.      CMP AX,WindowY2   {y2 > WindowY2 ?}
  2913.      JG @GC4Punkt2     {yes, don't change flag for "point is below window"}
  2914.      AND CL,CodeUnten
  2915.    @GC4Punkt2:         {CL holds the area code for point 2          }
  2916.  
  2917.      MOV AX,x1
  2918.      SUB AX,StartVirtualX   {transform x1 into absolute coordinates}
  2919.      MOV x1,AX
  2920.      MOV AX,y1
  2921.      SUB AX,StartVirtualY   {transform y1 into absolute coordinates}
  2922.      MOV y1,AX
  2923.  
  2924.    @Punkt1:
  2925.      MOV CH,$F         {start with %1111  }
  2926.      MOV AX,x1
  2927.      CMP AX,BX         {x1 < WindowX1 ?}
  2928.      JL @GC1Punkt1     {yes, don't change flag for "point is left of window"}
  2929.      AND CH,CodeLinks  {no, reset flag       }
  2930.    @GC1Punkt1:
  2931.      CMP AX,SI         {x1 > WindowX2 ?}
  2932.      JG @GC2Punkt1     {yes, don't change flag for "point is right of window"}
  2933.      AND CH,CodeRechts {no, reset flag       }
  2934.    @GC2Punkt1:
  2935.      MOV AX,y1
  2936.      CMP AX,DI         {y1 < WindowY1 ?}
  2937.      JL @GC3Punkt1     {yes, don't change flag for "point is above window"}
  2938.      AND CH,CodeOben   {no, reset flag       }
  2939.    @GC3Punkt1:
  2940.      CMP AX,WindowY2   {y1 > WindowY2 ?}
  2941.      JG @GC4Punkt1     {yes, don't change flag for "point is below window"}
  2942.      AND CH,CodeUnten
  2943.    @GC4Punkt1:         {CH holds the area code for point 1          }
  2944.  
  2945.    {CL holds the area code for point 2, CH the one for point 1}
  2946.  
  2947.      MOV AX,CX
  2948.      AND AL,AH         {Code1 AND Code2 <> 0 ?}
  2949.      JNZ @LineReady    {yes, line is completely outside the window}
  2950.      MOV AX,CX
  2951.      OR AL,AH          {Code1 OR Code2 = 0 ?}
  2952.      JZ @DrawLine      {yes, line is completely inside the window}
  2953.  
  2954.    {Now do the clipping itself:          }
  2955.      MOV AX,CX
  2956.      OR AH,AH          {Code1 =0 ?}
  2957.      JNZ @CL3          {no, everything ok}
  2958.      MOV AX,x1         {yes, swap points!      }
  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     {point1 left of window?   }
  2975.      JZ @CL4                   {no  }
  2976.      {yes, compute new coordinates:  }
  2977.      { y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX1 - X1) }
  2978.      { and 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    {point1 right of window?   }
  2989.      JZ @CL5                   {no  }
  2990.      {yes, compute:}
  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      {point1 above window?        }
  3002.      JZ @CL6                   {no  }
  3003.      {yes, compute:}
  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     {point below window?         }
  3015.      JZ @Punkt1                {no  }
  3016.      {yes, compute:}
  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.    {precondition here: both points have been clipped to the visible window;}
  3029.    {if the line is completely offscreen, the program jumped directly to    }
  3030.    {@LineReady, instead!            }
  3031.    @DrawLine:
  3032.      PUSH BP
  3033.      MOV Steigung,0  {reset Flag       }
  3034.      MOV CX,x2
  3035.      SUB CX,x1       {point1 right of point2 ?  }
  3036.      JGE @posDX      {no  }
  3037.      NEG CX          {yes, swap points      }
  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]  {get mask for VRAM-access    }
  3057.      MOV DL,2
  3058.  
  3059.      MOV BX,pa       {BX = drawing page}
  3060.      SHL BX,1
  3061.      MOV ES,[BX +OFFSET Segment_Adr -StartIndex*2]
  3062.  
  3063.      {ES:DI=pointer to graphic address of point1, DX=access mask for it}
  3064.      MOV SI,LINESIZE
  3065.      MOV BX,y2
  3066.      SUB BX,y1       {point1 below point2 ?        }
  3067.      JG @posDY       {no  }
  3068.      NEG BX          {yes, negate deltaY and row-increment   }
  3069.      NEG SI
  3070.  
  3071.    @posDY:
  3072.      CMP BX,CX       {deltaY > deltaX ?}
  3073.      JLE @flach      {no: small slope, <=1        }
  3074.      XCHG BX,CX      {yes, swap deltas and set flag         }
  3075.      MOV Steigung,1
  3076.  
  3077.    {compute Bresenham-parameters: 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 := number of pixels}
  3086.      MOV BL,Color
  3087.      MOV BH,1
  3088.      CMP Steigung,0  {steep line?  }
  3089.      JNZ @high1      {yes}
  3090.  
  3091.    @low1:            {no  }
  3092.      MOV AX,3C4h
  3093.      XCHG AX,DX
  3094.      OUT DX,AX       {select correct bitplane   }
  3095.      MOV DX,AX       {save mask to DX again      }
  3096.      MOV AL,BL       {get color of point   }
  3097.      STOSB           {draw point  }
  3098.      SHL DH,1        {compute mask for next point            }
  3099.      CMP DH,16       {still addressable with the same address?}
  3100.      JE @nextbyte1   {no, address had to be incremented by 1  }
  3101.      DEC DI          {yes, make incrementing DI undone       }
  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       {restore mask to 1       }
  3110.      JMP @low1b      {rest as above  }
  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    = VIRTUAL pixel coordinates of the point to be read   }
  3157. {     PAGEADR= graphic page(segment) to be read from               }
  3158. {     StartVirtualX, StartVirtualY = upper left image corner       }
  3159. {out: color of the point}
  3160. {rem: If the pixel lies outside the visible window, the routine    }
  3161. {     will return "0" as the result       }
  3162. {     Attention! As PAGEADR always specifies the NOT visible gra-     }
  3163. {     phic page, this routine will read from there, too!           }
  3164. ASM
  3165.  XOR AL,AL              {preset AL with 0    }
  3166.  MOV DI,y
  3167.  SUB DI,StartVirtualY   {transform y into absolute coordinates}
  3168.  JS @offscrn
  3169.  CMP DI,YMAX
  3170.  JG @offscrn
  3171.  MOV BX,x
  3172.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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 = plane to read from}
  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   = VIRTUAL pixel coordinates of the point to be read   }
  3199. {     StartVirtualX, StartVirtualY = upper left image corner      }
  3200. {out: color of the point in the background page}
  3201. {rem: If the pixel lies outside the visible window, the routine   }
  3202. {     will return "0" as the result       }
  3203. {     Because BACKGNDADR is used as background page, calling      }
  3204. {     this routine only makes sense when using mode STATIC!       }
  3205. {     If EMS is used then the calling routine must take care      }
  3206. {     that the EMS page frame contains the proper data (which can    }
  3207. {     be done by the statement "IF EMSused THEN EnsureEMSConsistency()")}
  3208. ASM
  3209.  XOR AL,AL              {preset AL with 0    }
  3210.  MOV DI,y
  3211.  SUB DI,StartVirtualY   {transform y into absolute coordinates}
  3212.  JS @offscrn
  3213.  CMP DI,YMAX
  3214.  JG @offscrn
  3215.  MOV BX,x
  3216.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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, because word-sized}
  3229.  ADD DI,[OFFSET BACKTab + BX]  {get mask for access: 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   = VIRTUAL pixel coordinates of the point to be read   }
  3237. {     pa    = graphic page (0..3), from which the point shall be    }
  3238. {             read out   }
  3239. {     StartVirtualX, StartVirtualY = upper left image corner      }
  3240. {out: color of the point in the background page}
  3241. {rem: If the pixel lies outside the visible window, the routine   }
  3242. {     will return "0" as the result       }
  3243. {     If you want to read from the actually VISIBLE page, then    }
  3244. {     then you must call the routine with pa=1-PAGE!                   }
  3245. {     Sensible values for "pa" are either 0 or 1 (and evtl. BACK-  }
  3246. {     GNDPAGE, if you are using background mode STATIC), however, }
  3247. {     the routine doesn't check that!       }
  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              {preset AL with 0    }
  3258.  MOV DI,y
  3259.  SUB DI,StartVirtualY   {transform y into absolute coordinates}
  3260.  JS @offscrn
  3261.  CMP DI,YMAX
  3262.  JG @offscrn
  3263.  MOV BX,x
  3264.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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 = plane to read from; BH = 0}
  3276.  MOV AL,4
  3277.  MOV AH,BL
  3278.  MOV BX,pa    {BX = graphic page}
  3279.  AND BX,3     {only pages 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    = VIRTUAL pixel coordinates of the point to be written }
  3294. {     color  = color value for the pixel to be drawn}
  3295. {     1-PAGE = graphic page to be drawn upon              }
  3296. {     StartVirtualX, StartVirtualY = upper left image corner   }
  3297. {     WinClip= TRUE, if line shall be clipped to the window area     }
  3298. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
  3299. {out: - }
  3300. {rem: The point (x,y) has been transformed to absolute screen coordinates  }
  3301. {     and has been drawn (if it lies within the visible window)            }
  3302. {     The pixel will NOT automatically be overtaken into the background    }
  3303. {     image, that is: it will be visible only for one animation cycle!       }
  3304. {     (For that reason, you should call this routine AFTER calling ANIMATE }
  3305. {     because otherwise, the drawn point will vanish at once!)             }
  3306. ASM
  3307.  CMP WinClip,TRUE
  3308.  JE @goClip
  3309.  
  3310.  MOV DI,y
  3311.  SUB DI,StartVirtualY   {transform y into absolute coordinates}
  3312.  JS @offscrn
  3313.  CMP DI,YMAX
  3314.  JG @offscrn
  3315.  MOV BX,x
  3316.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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], because 1-PAGE = visible page}
  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  {faster than STOSB on >=386!}
  3341.  STI
  3342.  JMP @ende
  3343.  
  3344. @goClip: {to this address, if clipping active}
  3345.  MOV DI,y
  3346.  SUB DI,StartVirtualY   {transform y into absolute coordinates}
  3347.  CMP DI,WinYMIN
  3348.  JL @offscrn
  3349.  CMP DI,WinYMAX
  3350.  JG @offscrn
  3351.  MOV BX,x
  3352.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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], because 1-PAGE = visible page}
  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  {faster than STOSB on >=386!}
  3378.  STI
  3379. @offscrn:
  3380. @ende:
  3381. END;
  3382.  
  3383. PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
  3384. { in: x,y   = VIRTUAL pixel coordinates of the point to be written }
  3385. {     color = color value for the pixel to be drawn}
  3386. {     StartVirtualX, StartVirtualY = upper left image corner   }
  3387. {     WinClip=TRUE, if line shall be clipped to the window area     }
  3388. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
  3389. {out: - }
  3390. {rem: The point (x,y) has been transformed to absolute screen coordinates and}
  3391. {     has been drawn into the background (if it is onscreen)                 }
  3392. {     The pixel will NOT be visible until the next animation cycle takes     }
  3393. {     place (but then, it remains permanent) (For that reason, you should    }
  3394. {     call this routine BEFORE calling ANIMATE. That way, evtl. changes to   }
  3395. {     the background will be visible "at once"!)                             }
  3396. {     Because BACKGNDPAGE is used as background page, calling this           }
  3397. {     routine only makes sense when using background mode STATIC!}
  3398. {     If EMS is used then the calling routine must take care      }
  3399. {     that the EMS page frame contains the proper data (which can    }
  3400. {     be done by the statement "IF EMSused THEN EnsureEMSConsistency()")}
  3401. ASM
  3402.  CMP WinClip,TRUE
  3403.  JE @goClip
  3404.  
  3405.  MOV DI,y
  3406.  SUB DI,StartVirtualY   {transform y into absolute coordinates}
  3407.  JS @offscrn
  3408.  CMP DI,YMAX
  3409.  JG @offscrn
  3410.  MOV BX,x
  3411.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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, because word-sized}
  3424.  ADD DI,[OFFSET BACKTab + BX]  {get mask for access: BX * 16000    }
  3425.  MOV ES,BACKGNDADR
  3426.  MOV AL,color
  3427.  MOV ES:[DI],AL  {faster than STOSB on >=386!}
  3428.  JMP @ende
  3429.  
  3430. @goClip: {to this address, if clipping active}
  3431.  MOV DI,y
  3432.  SUB DI,StartVirtualY   {transform y into absolute coordinates}
  3433.  CMP DI,WinYMIN
  3434.  JL @offscrn
  3435.  CMP DI,WinYMAX
  3436.  JG @offscrn
  3437.  MOV BX,x
  3438.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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, because word-sized}
  3452.  ADD DI,[OFFSET BACKTab + BX]  {get mask for access: BX * 16000    }
  3453.  MOV ES,BACKGNDADR
  3454.  MOV AL,color
  3455.  MOV ES:[DI],AL  {faster than STOSB on >=386!}
  3456. @offscrn:
  3457. @ende:
  3458. END;
  3459.  
  3460. PROCEDURE PagePutPixel(x,y:INTEGER; color:BYTE; pa:WORD); ASSEMBLER;
  3461. { in: x,y    = VIRTUAL pixel coordinates of the point to be written }
  3462. {     color  = color value for the pixel to be drawn}
  3463. {     pa     = graphic page (0..3) to be drawn upon                 }
  3464. {     PAGEADR= graphic page(segment) to be drawn upon               }
  3465. {     StartVirtualX, StartVirtualY = upper left image corner   }
  3466. {     WinClip= TRUE, if line shall be clipped to the window area     }
  3467. {     WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
  3468. {out: - }
  3469. {rem: The point (x,y) has been transformed to absolute screen coordinates  }
  3470. {     and has been drawn (if it lies within the visible window)            }
  3471. {     If you want to draw at the actually VISIBLE graphic page     }
  3472. {     then you must call the routine with pa=1-PAGE!                    }
  3473. {     Again, the drawn pixel will _NOT_ automatically be taken     }
  3474. {     over into the background image, that is: it will be visible  }
  3475. {     only until the next animation cycle (=till calling ANIMATE)     }
  3476. {     (For that reason, you should call this routine AFTER         }
  3477. {     calling ANIMATE, because otherwise, your drawn pixel will    }
  3478. {     vanish at once!)                                             }
  3479. {     Sensible values for "pa" are either 0 or 1 (and evtl. BACK-   }
  3480. {     GNDPAGE, if you are using background mode STATIC), however,  }
  3481. {     the routine doesn't check that!       }
  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   {transform y into absolute coordinates}
  3497.  JS @offscrn
  3498.  CMP DI,YMAX
  3499.  JG @offscrn
  3500.  MOV BX,x
  3501.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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 = graphic page}
  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  {faster than STOSB on >=386!}
  3524.  STI
  3525.  JMP @ende
  3526.  
  3527. @goClip: {to this address, if clipping active}
  3528.  MOV DI,y
  3529.  SUB DI,StartVirtualY   {transform y into absolute coordinates}
  3530.  CMP DI,WinYMIN
  3531.  JL @offscrn
  3532.  CMP DI,WinYMAX
  3533.  JG @offscrn
  3534.  MOV BX,x
  3535.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  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, coordinates admissible}
  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 = graphic page}
  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  {faster than STOSB on >=386!}
  3559.  STI
  3560. @offscrn:
  3561. @ende:
  3562. END;
  3563.  
  3564. PROCEDURE LoadFont(s:STRING);
  3565. { in: s = name of the font file to load, '' for: internal font }
  3566. {     FontType = type of the actual font}
  3567. {out: CurrentFont=pointer to new font   }
  3568. {     FontType = type of the font loaded}
  3569. {     FontHeight=its height in rows     }
  3570. {     FontWidth =its width in pixels    }
  3571. {     FontProportion = TagProportional, if proportional font     }
  3572. {     FontWidthTable[] = font widths for each letter   }
  3573. {The initial call to "ResetToInternalFont" assures that FontType     }
  3574. {     always has a defined value when this routine gets called!      }
  3575.  
  3576.     PROCEDURE ResetToInternalFont;
  3577.     VAR i,j:BYTE;
  3578.     BEGIN
  3579.      IF CurrentFont<>NIL  {very first call?   }
  3580.       THEN BEGIN {no!  }
  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 {not even enough memory for internal 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 {switch to internal font     }
  3611.         ResetToInternalFont;
  3612.         exit
  3613.        END;
  3614.  
  3615.  tempName:=FindFile(s);
  3616.  IF tempName<>'' THEN s:=tempName;
  3617.  
  3618.  {open font file and read in header:   }
  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, if proportional   }
  3640.  newFontType:=newFontType AND Pred(TagProportional);
  3641.  
  3642.  size:=Length(Tag)+3; {length of header }
  3643.  IF newFontType=TagMonoFont
  3644.   THEN inc(size,((newFontWidth+7) SHR 3)*newFontHeight SHL 8)
  3645.   ELSE inc(size,newFontWidth*newFontHeight SHL 8); {256 chars  }
  3646.  
  3647.  IF newFontProp=TagProportional THEN inc(size,256); {font widths}
  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 {no 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.  {now release old memory and allocate new mem:    }
  3668.  IF FontType<>newFontType
  3669.   THEN BEGIN {only necessary, if old and new fonts are different:  }
  3670.         IF FontType=TagMonoFont THEN FreeMem(CurrentFont,SizeOf(MonoFont))
  3671.         ELSE {FontType=TagColorFont} FreeMem(CurrentFont,SizeOf(ColorFont));
  3672.         IF MaxAvail<size
  3673.          THEN BEGIN {not enough memory   }
  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;  {reset evtl. compress error        }
  3712. END;
  3713.  
  3714. FUNCTION OutTextLength(s:STRING):WORD;
  3715. { text string       }
  3716. {     Font* = the font describing datas}
  3717. {out: Width of the given string in _pixels_; proportional fonts  }
  3718. {     will be treated correctly          }
  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)  = (virtual) starting coordinates for the text to be written}
  3733. {     s      = textstring to be displayed                             }
  3734. {     pa     = graphic page where the text shall be written         }
  3735. {     GraphTextColor=color for text                                      }
  3736. {     GraphTextBackground=color to be used for text background; if  }
  3737. {            this value equals GraphTextColor, only the text-pixels }
  3738. {             themselves will be drawn while the surrounding ones   }
  3739. {             don't change (=normal behaviour of TP's OutText-procs)  }
  3740. {     GraphTextOrientation="vertical" or "horizontal"               }
  3741. {     StartVirtualX,StartVirtualY = upper left image corner         }
  3742. {     Font* = the font describing datas}
  3743. {out: text has been written to the screen                           }
  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); {prepare EMS access         }
  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: Functionally equivalent to OutTextXY(), but the text will be    }
  3795. {     written to the background page instead of page PAGEADR!                 }
  3796. {     Because BACKGNDADR is used as background page, calling          }
  3797. {     this routine only makes sense when using mode STATIC!}
  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); {prepare EMS access         }
  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  = text which shall be converted into a sprite}
  3844. {     nr = spriteLOADnumber for the sprite to be generated}
  3845. {     Font*, CurrentFont = actual font   }
  3846. {     GraphTextOrientation = orientation of the font}
  3847. {Sprite?[nr] has been defined so, that it represents the contents      }
  3848. {     of "s" as a sprite}
  3849. {rem: The routine behaves like 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; {use standard values }
  3879.  WITH header DO
  3880.   BEGIN
  3881.    IF GraphTextOrientation=horizontal
  3882.     THEN BEGIN {sprite height is only 1 char cell}
  3883.           Hoehe_in_Zeilen:=FontHeight;
  3884.           Breite_in_4er_Gruppen:=(OutTextLength(s)+3) SHR 2
  3885.          END
  3886.     ELSE BEGIN {sprite width is only 1 char cell}
  3887.           Hoehe_in_Zeilen:=FontHeight*Length(s);
  3888.           Breite_in_4er_Gruppen:=(FontWidth+3) SHR 2
  3889.          END;
  3890.    SpriteLength:=Kopf+  {size of sprite header}
  3891.                  2*(2*Hoehe_in_Zeilen)+  {le. & ri. boundaries  }
  3892.                  2*(2*4*Breite_in_4er_Gruppen)+  {up. & lo. boundaries  }
  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-upper bound=65521}
  3896.     THEN BEGIN
  3897.           Error:=Err_NoSprite;
  3898.           Exit
  3899.          END;
  3900.    {enough space left?  }
  3901.    IF (Header.SpriteLength+15>MaxAvail+SPRITESIZE[nr])
  3902.     THEN BEGIN
  3903.           Error:=Err_NotEnoughMemory;
  3904.           Exit
  3905.          END
  3906.     ELSE FreeSpriteMem(nr);  {evtl. release old memory      }
  3907.  
  3908.    getmem(p1,Header.SpriteLength+15);       {get enough space       }
  3909.    SPRITESIZE[nr]:=Header.SpriteLength+15;
  3910.    SPRITEPTR [nr]:=p1;
  3911.    IF (LONGINT(p1) mod 16)=0
  3912.     THEN p2:=p1                             {make p2 fall on segment boundary}
  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);  {store sprite header to heap  }
  3936.  
  3937.    {now compute sprite data: do this by drawing pixels into the memory}
  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.         {pixel (a,b) -> b*Breite_in_4er_Gruppen+(x div 4) at 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 {evtl. re-compute boundaries}
  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.        {pixel (a,b) -> b*Breite_in_4er_Gruppen+(x div 4) at 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 {evtl. re-compute boundaries}
  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 = sprite position numbers of two sprites}
  4015. {     SpriteN[s1],SpriteX[s1],SpriteY[s1] = sprite data of sprite s1     }
  4016. {     SpriteN[s2],SpriteX[s2],SpriteY[s2] = sprite data of sprite s2     }
  4017. {out: TRUE/FALSE for "sprites collide"/"sprites do not collide"          }
  4018. {rem: This check is pixel-precise and doesn't depend on the sprites being   }
  4019. {     visible (=onscreen) or not!                                        }
  4020. {     inactive sprites (SpriteN[s?]=0) cannot collide                    }
  4021. {     A sprite can't collide with itself, (thus:  s1=s2 -> FALSE)        }
  4022. ASM
  4023.      MOV SI,s1               {get 1st parameter s1 from stack}
  4024.      MOV DI,s2               {get 2nd parameter s2 from stack}
  4025.      CMP SI,DI
  4026.      JE  @NOHIT1             {sprite can't collide with itself     }
  4027.      SHL SI,1
  4028.      mov cx,[SI + OFFSET SpriteN]
  4029.      jcxz @NOHIT1            {sprite <>0, that is: sprite active?}
  4030.      SHL DI,1
  4031.      MOV BX,[DI + OFFSET SpriteN]
  4032.      OR  BX,BX               {dto. for other sprite  }
  4033.      JNE @PRUEF2
  4034.    @NOHIT1:
  4035.      JMP @NOHIT7             {inactive sprites can't collide    }
  4036.                              {either -> return FALSE            }
  4037. {here: SI (DI) = pointer to 1. (2.) sprite in ?YWRTD[..],}
  4038. {      CX (BX) = spritenumber of sprite 1 (2)            }
  4039. {(a bit later, DS (ES) becomes segment addr. of sprite data of 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 = sprite number2 * 2}
  4046.      mov es,[BX + OFFSET SPRITEAD] {ES = segment of 2nd sprite's data}
  4047.      mov bx,cx                     {(CX = spritenumber1)}
  4048.      shl bx,1                      {BX = spritenumber1 * 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 = pointer to left boundary data}
  4061.      mov CS:WORD PTR @lirand2+1,ax
  4062.      mov ax,es:[Right]             {AX = pointer to right boundary data}
  4063.      mov CS:WORD PTR @rerand2+1,ax
  4064.      mov ax,es:[Top]               {AX = pointer to upper boundary data}
  4065.      mov CS:WORD PTR @orand2+1,ax
  4066.      mov ax,es:[Bottom]            {AX = pointer to lower boundary data}
  4067.      mov CS:WORD PTR @urand2+1,ax
  4068.      mov ax,es:[Breite]            {AX = max. width in groups of 4 }
  4069.      shl al,1
  4070.      shl al,1
  4071.      mov CS:WORD PTR @breite2+1,ax {*4 = width in points  }
  4072.      mov ax,es:[Hoehe]
  4073.      mov CS:WORD PTR @hoehe2+1,ax  {height of sprite2 in points}
  4074.  
  4075.      MOV AX,[Left]                 {AX = pointer to left boundary data}
  4076.      MOV CS:WORD PTR @LIRAND1+1,AX
  4077.      MOV AX,[Right]                {AX = pointer to right boundary data}
  4078.      MOV CS:WORD PTR @RERAND1+1,AX
  4079.      MOV AX,[Top]                  {AX = pointer to upper boundary data}
  4080.      MOV CS:WORD PTR @ORAND1+1,AX
  4081.      MOV AX,[Bottom]               {AX = pointer to lower boundary data}
  4082.      MOV CS:WORD PTR @URAND1+1,AX
  4083.      MOV BX,[Breite]               {BX = max. width in groups of 4 }
  4084.      SHL BX,1
  4085.      SHL BX,1                      {*4 = width in points  }
  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                  {dummy value}
  4091.      mov cx,bp                     {CX = breite2 will be needed again later on}
  4092.      lea bp,[di+bp-1]              {BP := x2 + breite2 - 1  (=x2last)}
  4093.      cmp bx,bp
  4094.      jle @noex1
  4095.      mov bp,bx
  4096.    @noex1:                         {here: BP = max(x1last,x2last)  (=maxx)}
  4097.      cmp si,di
  4098.      jle @X1_klgl_X2
  4099.      xchg si,di
  4100.    @X1_klgl_X2:                    {here: SI = min(x1,x2)  (=minx)}
  4101.      stc
  4102.      sbb si,bp                     {SI := minx - maxx - 1 = - (maxx - minx + 1)}
  4103.    @breite1:
  4104.      add cx,1234h                  {(dummy value)  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                    {no collision, if 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:                         {here: AX = max(y1last,y2last)  (=maxy)}
  4124.      mov dx,[y2]
  4125.      cmp di,dx                     {(DI = y1)}
  4126.      jle @noex3
  4127.      mov di,dx
  4128.    @noex3:                         {here: 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                    {no collision, if ueberlappy <= 0  }
  4133.      mov [ueberlappy_1],ax
  4134.  
  4135. {here: AX = ueberlappy - 1, CX = ueberlappx - 1}
  4136.    @x2_x1:
  4137.      mov dx,1234h                  {dummy value}
  4138.      xor bx,bx                     {from now on: 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. {jump-rail for NOHIT (this is a good place)}
  4146.    @NOHIT2:
  4147.      JMP @NOHIT7
  4148.  
  4149. {now back at "normal" program  }
  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:                         {here: AX = ueberlappy - 1}
  4156.    @y2_y1:
  4157.      mov dx,1234h                  {dummy value}
  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. {Now check the overlapping rows and columns more closely by iteration: }
  4169.    @iterate:
  4170.      mov cx,[ueberlappy_1]         {number of rows -1 to compare          }
  4171.      shl cx,1                      {*2, because word-sized!}
  4172.    @lirand1:
  4173.      mov si,1234h                  {dummy value}
  4174.    @lirand2:
  4175.      mov di,1234h                  {dummy value}
  4176.    @rerand1:
  4177.      mov bx,1234h                  {dummy value}
  4178.    @rerand2:
  4179.      mov bp,1234h                  {dummy value}
  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 := 1st row where sprite1 overlaps sprite2 }
  4185.      mov ax,[hit2yfirst]
  4186.      shl ax,1
  4187.      add di,ax                  {DI := 1st row where sprite2 overlaps sprite1 }
  4188.      add si,cx                  {dto., last row    }
  4189.      add di,cx
  4190.    @one_line:
  4191.      mov ax,[si]                   {DS:AX := x1li[row]  }
  4192.      mov dx,es:[di]                {ES:DX := x2li[row]  }
  4193.      add ax,[x1]                   {AX := x1li[row] + x1  (=c)  }
  4194.      add dx,[x2]                   {DX := x2li[row] + x2  (=d)  }
  4195.      cmp ax,dx
  4196.      jge @C_grgl_D
  4197.      mov ax,dx
  4198.    @C_grgl_D:                      {here: AX = max(c,d)}
  4199.      mov cx,[si+bx]                {DS:CX := x1re[row]  }
  4200.      mov dx,es:[di+bp]             {ES:DX := x2re[row]  }
  4201.      add cx,[x1]                   {CX := x1re[row] + x1  (=a)  }
  4202.      add dx,[x2]                   {DX := x2re[row] + x2  (=b)  }
  4203.      cmp cx,dx
  4204.      jle @A_klgl_B
  4205.      mov cx,dx
  4206.    @A_klgl_B:                      {here: CX = min(a,b)}
  4207.      cmp cx,ax                     {min(a,b) >= max(c,d) ?}
  4208.      jge @found_Xhit               {yes: collision in X-direction found!}
  4209.      dec si                        {next row (-> word-sized values!)}
  4210.      dec si
  4211.      dec di
  4212.      dec di
  4213.      dec WORD PTR [ueberlappy_1]
  4214.      jns @one_line
  4215. {no collision in X-direction -> no collision at all!  }
  4216.      jmp @NOHIT7
  4217.  
  4218. {otherwise: collision in X-direction, now check Y-dir. also (as above) and  }
  4219. {report "collision!" only, if there is at least 1 collision in Y-dir., too  }
  4220.    @found_Xhit:
  4221.      mov cx,[ueberlappx_1]         {number of columns -1 to compare        }
  4222.      shl cx,1                      {*2, because word-sized!}
  4223.    @orand1:
  4224.      mov si,1234h                  {dummy value}
  4225.    @orand2:
  4226.      mov di,1234h                  {dummy value}
  4227.    @urand1:
  4228.      mov bx,1234h                  {dummy value}
  4229.    @urand2:
  4230.      mov bp,1234h                  {dummy value}
  4231.      sub bx,si                     {BX := urand1 - orand1}
  4232.      sub bp,di                     {BP := urand2 - orand2}
  4233.      mov ax,[hit1xfirst]
  4234.      shl ax,1                      {*2, because word-sized!}
  4235.      add si,ax                     {SI := orand1 + 2 * hit1xfirst}
  4236.      mov ax,[hit2xfirst]
  4237.      shl ax,1                      {*2, because word-sized!}
  4238.      add di,ax                     {DI := orand2 + 2 * hit2xfirst}
  4239.      add si,cx
  4240.      add di,cx
  4241.    @one_column: mov ax,[si]        {AX := y1ob[column]}
  4242.      cmp ax,16000                  {dummy value for "empty column"?}
  4243.      je @next_column               {yes, thus: surely no collision  }
  4244.      mov dx,es:[di]                {DX := y2ob[column]}
  4245.      cmp dx,16000                  {check 2nd sprite too: "empty column"?}
  4246.      je @next_column               {yes, no collision}
  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:                     {here: AX = max(c,d)}
  4253.      mov cx,[si+bx]                {DS:CX := y1un[column]}
  4254.      mov dx,es:[di+bp]             {ES:DX := y2un[column]}
  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:                     {here: CX = min(a,b)}
  4261.      cmp cx,ax                     {min(a,b) >= max(c,d) ?}
  4262.      jge @HIT2                     {yes: collision detected!}
  4263.    @next_column:
  4264.      dec si                        {no, next column (-> word-sized values!)}
  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                     {return 0 = FALSE as result        }
  4273.      JMP @TREFF_END  {SHORT}
  4274.    @HIT2:
  4275.      MOV AX,1                      {return 1 = TRUE as result        }
  4276.  
  4277.    @TREFF_END:
  4278.    {$IFOPT G+}
  4279.      mov bp,sp                     {only necessary for compiler switch G+!}
  4280.    {$ENDIF}
  4281.      mov dx,seg @DATA              {else, BP will be restored by TP itself}
  4282.      mov ds,dx
  4283. END;
  4284.  
  4285. PROCEDURE SetSplitIndex(number:INTEGER);
  4286. { in: number = index number of the sprite, up to which sprites won't }
  4287. {              be clipped to the animation windows             }
  4288. {out: - }
  4289. {rem: After calling the routine, sprites SpriteN[0..number] will not-}
  4290. {     and SpriteN[number+1..NMAX] will be clipped to the animation window}
  4291. {     If number <0 oder >NMAX, then all sprites will be clipped!}
  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: Actually set value of SplitIndex      }
  4301. BEGIN
  4302.  GetSplitIndex:=SplitIndex
  4303. END;
  4304.  
  4305. PROCEDURE SetAnimateWindow(x1,y1,x2,y2:INTEGER);
  4306. { in: (x1,y1) = left upper corner of the animation window to set     }
  4307. {     (x2,y2) = dto., lower right corner}
  4308. {out: Win* have been updated accordingly }
  4309. {     BWin* = backups of the most important values}
  4310. {rem: The points must be specified in absolute coordinates       }
  4311. {     The window's size must at least be 32x32 pixels,           }
  4312. {     x1 and x2-x1+1 must be multiples of 4 (or they will be made so)!         }
  4313. BEGIN
  4314.  x1:=x1 AND $FFFC;  {make x1 a multiple of 4        }
  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;  {make the width a multiple of 4    }
  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; {make backups   }
  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 = actual graphicpage(address) on which to draw upon            }
  4338. {     BACKGNDADR = background page(address)   }
  4339. {     BACKGROUNDMODE = STATIC/SCROLLING for solid/scrollable background   }
  4340. {     SpriteN[] = spritenumber of sprite to be displayed    }
  4341. {     SpriteX[],SpriteY[] = their (virtual) coordinates               }
  4342. {     StartVirtualX,StartVirtualY = upper left image corner     }
  4343. {     (PAGE = actually displayed graphic page)  }
  4344. {     Win* = data describing the animation window }
  4345. {out: PAGE = 0/1, if PAGE has been 1/0, respectively }
  4346. {     PAGEADR = new, actual graphic page(address)    }
  4347. {rem: Animate erases the old contents of the page (using the background page  }
  4348. {     information), draws all visible sprites, synchronizes to the display-  }
  4349. {     enable signal and switches the display to that now completed page      }
  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); {prepare EMS access         }
  4367.  ASM
  4368.     CLD
  4369.     {first copy the background picture to the actual graphic page:}
  4370.     CMP BackgroundMode,STATIC   {which background mode?   }
  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.1st start/dest. address}
  4378.     MOV DI,SI
  4379.  
  4380.     MOV ES,PAGEADR              {fill graphic page with background pattern}
  4381.     CMP UpdateOuterArea,0       {update outer background area?}
  4382.     MOV DS,BACKGNDADR
  4383.  
  4384.     je @skip_outer
  4385.     {do inner and outer background area in one         }
  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 {write plane 0 }
  4393.     mov cx,bx
  4394.     rep movsw
  4395.     mov ah,2
  4396.     out dx,ax {write plane 1 }
  4397.     mov cx,bx
  4398.     xor di,di
  4399.     rep movsw
  4400.     mov ah,4
  4401.     out dx,ax {write plane 2 }
  4402.     mov cx,bx
  4403.     xor di,di
  4404.     rep movsw
  4405.     mov ah,8
  4406.     out dx,ax {write plane 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:  {draw only inner area}
  4415.  
  4416.     PUSH BP
  4417.  
  4418.     CMP DX,XMAX+1               {window from the very left to the very right?}
  4419.     JNE @innen
  4420.  
  4421.     MOV AX,0102h
  4422.     MOV DX,3C4h
  4423.     SHL BX,1                    {yes, can be done by a REP MOVSB instruction }
  4424.     MOV BX,CS:[OFFSET gadr + BX]   {BX := WinHeight * LINESIZE}
  4425.     MOV BP,PAGESIZE
  4426.     SUB BP,BX
  4427.  
  4428.     OUT DX,AX  {select write plane 0   }
  4429.     MOV CX,BX
  4430.     SHR CX,1
  4431.     REP MOVSW
  4432.     ADC CX,CX
  4433.     REP MOVSB
  4434.     SUB DI,BX  {reset DI        }
  4435.     ADD SI,BP  {set si to the next "plane"   }
  4436.  
  4437.     MOV AH,2
  4438.     OUT DX,AX  {select write plane 1   }
  4439.     MOV CX,BX
  4440.     SHR CX,1
  4441.     REP MOVSW
  4442.     ADC CX,CX
  4443.     REP MOVSB
  4444.     SUB DI,BX  {reset DI        }
  4445.     ADD SI,BP  {set si to the next "plane"   }
  4446.  
  4447.     MOV AH,4
  4448.     OUT DX,AX  {select write plane 2   }
  4449.     MOV CX,BX
  4450.     SHR CX,1
  4451.     REP MOVSW
  4452.     ADC CX,CX
  4453.     REP MOVSB
  4454.     SUB DI,BX  {reset DI        }
  4455.     ADD SI,BP  {set si to the next "plane"   }
  4456.  
  4457.     MOV AH,8
  4458.     OUT DX,AX  {select write plane 3   }
  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 per row }
  4470.     MOV BP,LINESIZE
  4471.     SUB BP,DX                   {BP := offset to the next row   }
  4472.     MOV BH,DL
  4473.     XOR CH,CH
  4474.  
  4475.     MOV AX,0102h
  4476.     MOV DX,3C4h
  4477.     OUT DX,AX    {select write plane 0   }
  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                   {move data of one row }
  4489.     ADD SI,BP                   {position to next row           }
  4490.     ADD DI,BP
  4491.     DEC BL                      {one row completed}
  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                   {move data of one row }
  4509.     ADD SI,BP                   {position to next row           }
  4510.     ADD DI,BP
  4511.     DEC BL                      {one row completed}
  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                   {move data of one row }
  4529.     ADD SI,BP                   {position to next row           }
  4530.     ADD DI,BP
  4531.     DEC BL                      {one row completed}
  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                   {move data of one row }
  4547.     ADD SI,BP                   {position to next row           }
  4548.     ADD DI,BP
  4549.     DEC BL                      {one row completed}
  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:      {now: create background image from tiles        }
  4562.  
  4563.    {Do we have to redraw the outer area of the animation window?}
  4564.     cmp UpdateOuterArea,0
  4565.     je @old_scrolling_bckgnd
  4566.     {Yes, but perhaps there is nothing to draw at all?:}
  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      {upper left corner of the window = pixel (0,0)?}
  4574.     jne @do_outer {no: draw outer area           }
  4575.     cmp bx,YMAX SHL 8 +LINESIZE  {lower right corner = pixel (XMAX,YMAX)?}
  4576.     je @old_scrolling_bckgnd  {yes, thus no outer area to draw       }
  4577.  
  4578.   @do_outer: {now draw the outer area    }
  4579.     {╔════════════════╗ outer area is divided into 3 regions:      }
  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 {write plane 0 }
  4598.     out dx,ax
  4599.     xor si,si   {region 1 starts at 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    {write plane 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    {write plane 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    {write plane 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; works, because                   }
  4642.     mov dl,LINESIZE
  4643.     sub dl,al {DL:=LINESIZE-WinWidth DIV 4}
  4644.     jz @region3 {does window run continously from the far left to right?}
  4645.     mov dh,bh {DH:=WinHeight-1}
  4646.     or dh,dh
  4647.     jz @region3 {window height only 1 row?}
  4648.     xor ch,ch
  4649.     mov bx,ax
  4650.     add di,ax
  4651.     mov si,di {destination address=starting address due to memory layout}
  4652.  
  4653.     mov bp,di
  4654.     push dx
  4655.   @region2a:
  4656.     {DL = width of one row of the animation window }
  4657.     {DH = WinHeight-1}
  4658.     {BX = WinWidth DIV 4 = width of the animation window DIV 4 }
  4659.     {CX:=width of one row from the right edge of the animation window to}
  4660.     {the left edge of the animation window in the next row:   }
  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 for plane #0, SI=DI+1*PAGESIZE for 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 {there is no region 3 to be drawn}
  4731.     mov bx,cx  {store copy in BX   }
  4732.     mov si,PAGESIZE
  4733.     sub si,cx  {starting address of region 3}
  4734.     mov di,si  {destination address:=starting address (works due to memory layout!)}
  4735.     mov bp,di  {save destination address }
  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  {reset SI to its old value, then increment it to the next page}
  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  {reset SI to its old value, then increment it to the next page}
  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  {reset SI to its old value, then increment it to the next page}
  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  {reset UpdateOuterArea     }
  4782.  
  4783.   @old_scrolling_bckgnd:
  4784.  
  4785.   MOV DI,$F  {often used constants      }
  4786.   MOV CX,4
  4787.  
  4788.  {#tiles, really being cut off at their left edge:}
  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.  {pixels, which are cut off at the left of the 1st (partially) visible tile: }
  4800.  {really: leftcut := ((StartVirtualX + WinXMIN - BackX1) MOD 16) AND $F      }
  4801.  {but this is equivalent to: }
  4802.  {leftcut := (StartVirtualX + WinXMIN - BackX1) AND $F}
  4803.  {the "AND $F" is for 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.  {compute 1st read plane of the left-cut tiles: leftcut AND 3         }
  4813.  { Then compute the according mask out of this     }
  4814.   MOV AH,BL      {BL = leftcut}
  4815.   AND AH,3
  4816.   MOV AL,4
  4817.   MOV StartLesePlane,AX   {must lie in the stack segment!}
  4818.  
  4819.  {dto., for last (partially) visible tile at the right}
  4820.  {really: rightcut := (16 - leftcut - (WinWidth MOD 16)) AND $F, aber s.o.!    }
  4821.  {rightcut := (16 - leftcut - WinWidth) AND $F}
  4822.  {the "AND $F" is for 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.  {#_non-cut_ tiles in the inner area of the window per row:}
  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=additonal factor to position from the rightmost (evtl. only partially}
  4842.  {       visible) tile of one line to the 1st tile of the next row, which   }
  4843.  {       is _not_ cut at its left side}
  4844.  {stepX2=dto., but to the 1st (evtl. only partially) visible tile of the      }
  4845.  {       next row      }
  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      {for the given range holds: leftcut = 0 <-> -leftcut and $F=0}
  4854.   JE @nodec
  4855.   DEC AX
  4856.  @nodec:
  4857.   MOV stepX2,AX
  4858.  
  4859.  
  4860.  {compute 1st write plane of the non-left-cut tiles:           }
  4861.  { (WinXMIN - leftcut) AND 3, is computed here by     }
  4862.  { (WinXMIN - (leftcut AND $F) AND 3}
  4863.  { Then compute the according mask out of this     }
  4864.   ADD BX,WinXMIN
  4865.   AND BX,3
  4866.   MOV AH,CS:[OFFSET CS_TranslateTab +BX]
  4867.   MOV AL,2
  4868.   MOV StartWritePlane,AX  {must lie in the stack segment!}
  4869.  
  4870.  
  4871.  {now the same thing for the Y-direction:}
  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.  {compute index of 1st (evtl. only partially) visible tile:      }
  4883.  {actIndex := KachelnWegOben * XTiles + KachelnWegLinks + 1;}
  4884.  {The "+1" is to accomodate that the BackTile[]-counting starts at 0}
  4885.  {to keep OffscreenTile free!    }
  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.   {---now draw!---      }
  4914.  
  4915.   { has been set in SetAnimateWindow() already:        }
  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 = copy of KachelnWegLinks = xtil  }
  4933.   MOV AX,KachelnWegOben
  4934.   MOV ytil,AX
  4935.  
  4936.   MOV ES,PAGEADR  {once and for all!}
  4937.   MOV BX,leftcut
  4938.   {here: AX = ytil, BX = Leftcut, CX = xtil, ES = ^graphic segment   }
  4939.   TEST BL,3      {leftcut MOD 4 = 0 ?}
  4940.   JZ @useMode1   {yes, write mode 1 can be used}
  4941.   JMP @useMode0  {no, use write mode 0      }
  4942.  
  4943.  @useMode1:
  4944.   {Go here if width of window is a multiple of 4, leftcut mod 4=0 and the left }
  4945.   {edge of the window is a multiple of 4 (this also makes rightcut mod 4=0)       }
  4946.   {then write mode 1 can be used    }
  4947.   MOV AX,4105h   {enable write mode 1   }
  4948.   MOV DX,3CEh
  4949.   OUT DX,AX
  4950.   MOV AX,0F02h   {access all 4 planes at once          }
  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 =correct value, if we have to jump!    }
  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 with WriteMode1: }
  4983.     { in: WinXMIN,WinYMIN = screen coordinates   }
  4984.     {     ES = ^graphic segment}
  4985.     {     SI = tile index    }
  4986.     {     BX = leftcut       }
  4987.     {     CX = xtil          }
  4988.     {     leftcut MOD 4 = 0  }
  4989.     {     topcut, Win*, SCROLLADR,...}
  4990.     {out: ES = ^graphic segment}
  4991.     {rem: WriteMode1 has been set already and remains set  }
  4992.     MOV AL,[OFFSET BackTile +SI]
  4993.     XOR AH,AH      {compute offset address of tile:    }
  4994.     MOV CL,6       {each tile is 64 bytes in size, thus}
  4995.     SHL AX,CL      {AX := tile * 64 = tile SHL 6       }
  4996.  
  4997.     MOV SI,topcut  {additionaly: the rows cut off at the upper edge:}
  4998.     MOV CX,16      {4 bytes for each row  }
  4999.     SUB CX,SI      {CX := 16 - topcut = rows to draw        }
  5000.     SHL SI,1
  5001.     SHL SI,1
  5002.     ADD SI,AX      {SI = pointer to first tile *row* to copy       }
  5003.  
  5004.     MOV AX,BX      {BX = leftcut}
  5005.     SHR AX,1       {increment SI by left cut-off = leftcut DIV 4    }
  5006.     SHR AX,1
  5007.     ADD SI,AX      {SI = pointer to first tile *byte* to copy       }
  5008.  
  5009.     MOV DI,WinYMINmLINESIZEaWinXMINdiv4 {ES:DI = destination address}
  5010.     MOV DS,SCROLLADR  {DS:SI = source address}
  5011.     {Now there is no further variable on stack, so BP can be used   }
  5012.     {for other purposes!}
  5013.     PUSH BP        {will be needed when leaving the procedure! }
  5014.     MOV BP,16      {BP := (16 - leftcut) DIV 4 = bytes per tile row  }
  5015.     SUB BP,BX
  5016.     SHR BP,1
  5017.     SHR BP,1
  5018.  
  5019.     MOV AX,LINESIZE  {is a constant     }
  5020.     SUB AX,BP
  5021.     MOV DX,4
  5022.     SUB DX,BP
  5023.     MOV BX,CX      {number of rows   }
  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.   {position to next tile to the right of it:     }
  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 = ^destination address}
  5050.  
  5051.   {now draw innerTilesX tiles, which are only cut at their top:}
  5052.   MOV AX,innerTilesX
  5053.   OR AX,AX
  5054.   JBE @m1UpperInnerTilesDone
  5055.   MOV counter,AX
  5056.  
  5057.   MOV BX,16        {adjustment factor, to set DI one tile row higher and }
  5058.   SUB BX,topcut    {one tile column further    }
  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 with WriteMode1: }
  5078.     { in: ES:DI = ^destination address}
  5079.     {     SI = tile index     }
  5080.     {     BX = correction for row}
  5081.     {     leftcut MOD 4 = 0   }
  5082.     {     topcut, Win*, SCROLLADR,...}
  5083.     {out: ES:DI = ^destination address of the next tile to the right of it}
  5084.     {rem: WriteMode1 has been set already and remains set  }
  5085.     {     BX will not be changed }
  5086.     MOV AL,[OFFSET BackTile +SI]
  5087.     XOR AH,AH      {compute offset address of tile:    }
  5088.     MOV CL,6       {each tile is 64 bytes in size, thus}
  5089.     SHL AX,CL      {AX := tile * 64 = tile SHL 6       }
  5090.  
  5091.     MOV SI,topcut  {additionaly: the rows cut off at the upper edge:}
  5092.     MOV CX,16      {4 bytes for each row  }
  5093.     SUB CX,SI      {CX := 16 - topcut = rows to draw        }
  5094.     SHL SI,1
  5095.     SHL SI,1
  5096.     ADD SI,AX      {SI = pointer to first tile *row* to copy       }
  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 of the next row below the tile, now position to the next    }
  5111.     {tile:         }
  5112.     {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  5113.     {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  5114.     {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  5115.     { ▀                      }
  5116.     MOV DS,DX         {POP BP isn't necessary}
  5117.     ADD DI,BX
  5118.  
  5119.   {position to next tile to the right of it:     }
  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 = ^first row of the upper right corner tile}
  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 with WriteMode1: }
  5145.     { in: ES:DI = ^destination address}
  5146.     {     SI = tile index     }
  5147.     {     rightcut MOD 4 = 0  }
  5148.     {     topcut, Win*, SCROLLADR,...}
  5149.     {out: ES = ^graphic segment }
  5150.     {rem: WriteMode1 has been set already and remains set  }
  5151.     MOV AL,[OFFSET BackTile +SI]
  5152.     XOR AH,AH      {compute offset address of tile:    }
  5153.     MOV CL,6       {each tile is 64 bytes in size, thus}
  5154.     SHL AX,CL      {AX := tile * 64 = tile SHL 6       }
  5155.  
  5156.     MOV SI,topcut  {additionaly: the rows cut off at the upper edge:}
  5157.     MOV CX,16      {4 bytes for each row  }
  5158.     SUB CX,SI      {CX := 16 - topcut = rows to draw        }
  5159.     SHL SI,1
  5160.     SHL SI,1
  5161.     ADD SI,AX      {SI = pointer to first tile *row* to copy       }
  5162.  
  5163.     MOV AX,rightcut
  5164.     MOV DS,SCROLLADR  {DS:SI = source address}
  5165.     {Now there is no further variable on stack, so BP can be used   }
  5166.     {for other purposes!}
  5167.     PUSH BP        {will be needed when leaving the procedure! }
  5168.     MOV BP,16      {BP := (16 - rightcut) DIV 4 = bytes per tile row  }
  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 := rows to draw        }
  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.   {position on first left tile, which is not cut at its top:                 }
  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 = ^destination address of the 1st tile of the 1st tile row not being cut at its top}
  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   {correction factor for AX}
  5225.   MOV LINESIZE_sub_BytesPerPlane,AX
  5226.   MOV BytesPerPlane,DX  {bytes to move }
  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  {is a constant for the tile column}
  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 with WriteMode1: }
  5262.     { in: ES:DI = ^destination address}
  5263.     {     SI = tile index     }
  5264.     {     BX = BytesPerPlane  }
  5265.     {     leftcut MOD 4 = 0   }
  5266.     {     LINESIZE_sub_BytesPerPlane}
  5267.     {     leftcutDIV4, Win*, SCROLLADR,...}
  5268.     {out: ES:DI = ^destination address of the next tile below}
  5269.     {rem: WriteMode1 has been set already and remains set   }
  5270.     {     BX will not be changed }
  5271.     MOV AL,[OFFSET BackTile +SI]
  5272.     XOR AH,AH      {compute offset address of tile:    }
  5273.     MOV CL,6       {each tile is 64 bytes in size, thus}
  5274.     SHL AX,CL      {AX := tile * 64 = tile 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.row  }
  5285.     REP MOVSB
  5286.     ADD SI,DX
  5287.     ADD DI,AX   {ES:DI = ^next tile     }
  5288.  
  5289.     MOV CX,BX   {2.row  }
  5290.     REP MOVSB
  5291.     ADD SI,DX
  5292.     ADD DI,AX
  5293.  
  5294.     MOV CX,BX   {3.row  }
  5295.     REP MOVSB
  5296.     ADD SI,DX
  5297.     ADD DI,AX
  5298.  
  5299.     MOV CX,BX   {4.row  }
  5300.     REP MOVSB
  5301.     ADD SI,DX
  5302.     ADD DI,AX
  5303.  
  5304.     MOV CX,BX   {5.row  }
  5305.     REP MOVSB
  5306.     ADD SI,DX
  5307.     ADD DI,AX
  5308.  
  5309.     MOV CX,BX   {6.row  }
  5310.     REP MOVSB
  5311.     ADD SI,DX
  5312.     ADD DI,AX
  5313.  
  5314.     MOV CX,BX   {7.row  }
  5315.     REP MOVSB
  5316.     ADD SI,DX
  5317.     ADD DI,AX
  5318.  
  5319.     MOV CX,BX   {8.row  }
  5320.     REP MOVSB
  5321.     ADD SI,DX
  5322.     ADD DI,AX
  5323.  
  5324.     MOV CX,BX   {9.row  }
  5325.     REP MOVSB
  5326.     ADD SI,DX
  5327.     ADD DI,AX
  5328.  
  5329.     MOV CX,BX  {10.row  }
  5330.     REP MOVSB
  5331.     ADD SI,DX
  5332.     ADD DI,AX
  5333.  
  5334.     MOV CX,BX  {11.row  }
  5335.     REP MOVSB
  5336.     ADD SI,DX
  5337.     ADD DI,AX
  5338.  
  5339.     MOV CX,BX  {12.row  }
  5340.     REP MOVSB
  5341.     ADD SI,DX
  5342.     ADD DI,AX
  5343.  
  5344.     MOV CX,BX  {13.row  }
  5345.     REP MOVSB
  5346.     ADD SI,DX
  5347.     ADD DI,AX
  5348.  
  5349.     MOV CX,BX  {14.row  }
  5350.     REP MOVSB
  5351.     ADD SI,DX
  5352.     ADD DI,AX
  5353.  
  5354.     MOV CX,BX  {15.row  }
  5355.     REP MOVSB
  5356.     ADD SI,DX
  5357.     ADD DI,AX
  5358.  
  5359.     MOV CX,BX  {16.row  }
  5360.     REP MOVSB
  5361.     ADD SI,DX
  5362.     ADD DI,AX
  5363.  
  5364.     MOV AX,SEG @DATA
  5365.     MOV DS,AX
  5366.  
  5367.   {next tile; DI already has the right value}
  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 = ^destination address of the first inner tile (still)}
  5392.  
  5393.   MOV AX,innerTilesY   {are there any inner tiles at all?}
  5394.   OR AX,AX
  5395.   JBE @m1SkipInnerTiles
  5396.   CMP innerTilesX,0
  5397.   JB @m1SkipInnerTiles
  5398.  
  5399.   MOV counter,AX
  5400.   MOV AX,actIndex     {make copies of the actual values  }
  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 = ^first right edge-tile which is not cut at its top      }
  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 with WriteMode1: }
  5457.     { in: ES:DI = ^destination address}
  5458.     {     SI = tile index     }
  5459.     {     BX = BytesPerPlane  }
  5460.     {     rightcut MOD 4 = 0  }
  5461.     {     innerTilesY >= 1    }
  5462.     {     LINESIZE_sub_BytesPerPlane}
  5463.     {     SCROLLADR,...}
  5464.     {out: ES:DI = ^destination address of the next tile below}
  5465.     {rem: WriteMode1 has been set already and remains set   }
  5466.     {     BX will not be changed }
  5467.     MOV AL,[OFFSET BackTile +SI]
  5468.     XOR AH,AH      {compute offset address of tile:    }
  5469.     MOV CL,6       {each tile is 64 bytes in size, thus}
  5470.     SHL AX,CL      {AX := tile * 64 = tile 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.row  }
  5480.     REP MOVSB
  5481.     ADD SI,DX
  5482.     ADD DI,AX   {ES:DI = ^next tile     }
  5483.  
  5484.     MOV CX,BX   {2.row  }
  5485.     REP MOVSB
  5486.     ADD SI,DX
  5487.     ADD DI,AX
  5488.  
  5489.     MOV CX,BX   {3.row  }
  5490.     REP MOVSB
  5491.     ADD SI,DX
  5492.     ADD DI,AX
  5493.  
  5494.     MOV CX,BX   {4.row  }
  5495.     REP MOVSB
  5496.     ADD SI,DX
  5497.     ADD DI,AX
  5498.  
  5499.     MOV CX,BX   {5.row  }
  5500.     REP MOVSB
  5501.     ADD SI,DX
  5502.     ADD DI,AX
  5503.  
  5504.     MOV CX,BX   {6.row  }
  5505.     REP MOVSB
  5506.     ADD SI,DX
  5507.     ADD DI,AX
  5508.  
  5509.     MOV CX,BX   {7.row  }
  5510.     REP MOVSB
  5511.     ADD SI,DX
  5512.     ADD DI,AX
  5513.  
  5514.     MOV CX,BX   {8.row  }
  5515.     REP MOVSB
  5516.     ADD SI,DX
  5517.     ADD DI,AX
  5518.  
  5519.     MOV CX,BX   {9.row  }
  5520.     REP MOVSB
  5521.     ADD SI,DX
  5522.     ADD DI,AX
  5523.  
  5524.     MOV CX,BX  {10.row  }
  5525.     REP MOVSB
  5526.     ADD SI,DX
  5527.     ADD DI,AX
  5528.  
  5529.     MOV CX,BX  {11.row  }
  5530.     REP MOVSB
  5531.     ADD SI,DX
  5532.     ADD DI,AX
  5533.  
  5534.     MOV CX,BX  {12.row  }
  5535.     REP MOVSB
  5536.     ADD SI,DX
  5537.     ADD DI,AX
  5538.  
  5539.     MOV CX,BX  {13.row  }
  5540.     REP MOVSB
  5541.     ADD SI,DX
  5542.     ADD DI,AX
  5543.  
  5544.     MOV CX,BX  {14.row  }
  5545.     REP MOVSB
  5546.     ADD SI,DX
  5547.     ADD DI,AX
  5548.  
  5549.     MOV CX,BX  {15.row  }
  5550.     REP MOVSB
  5551.     ADD SI,DX
  5552.     ADD DI,AX
  5553.  
  5554.     MOV CX,BX  {16.row  }
  5555.     REP MOVSB
  5556.     ADD SI,DX
  5557.     ADD DI,AX
  5558.  
  5559.     MOV AX,SEG @DATA
  5560.     MOV DS,AX
  5561.  
  5562.   {next tile; DI already has the right value}
  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 = ^destination address of the first inner tile (still)}
  5587.   {innerTilesX >= 0, innerTilesX >= 1 -> it would suffice to check innerTilesX=0:}
  5588.  
  5589.   CMP innerTilesX,0    {IF (innerTilesX <= 0) OR (innerTilesY <= 0) THEN skip}
  5590.   JBE @m1SkipInnerTiles  {If there are no inner tiles, then the            }
  5591.                          {actual position is located at the 1st tile which    }
  5592.                          {is not cut at its left and is in the lowest tile row }
  5593.  
  5594.  
  5595.   {now "FOR x:=1 TO innerTilesX DO FOR y:=1 TO innerTilesY DO .."            }
  5596.   MOV oldDI,DI        {make temporary copies of DI and actIndex    }
  5597.   MOV BX,actIndex     {BX corresponds to "oldActIndex"}
  5598.  
  5599.   MOV AX,innerTilesX
  5600.   MOV counter,AX  {counter for X-direction}
  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  {is a constant for the tile column}
  5612.  
  5613.   MOV AX,innerTilesY
  5614.   MOV CH,AL          {CH is used as a counter for the Y-direction}
  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 with WriteMode1: }
  5631.     { in: ES:DI = ^destination address}
  5632.     {     SI = tile index     }
  5633.     {     CL = 6              }
  5634.     {     SCROLLADR}
  5635.     {out: ES:DI = ^destination address of the next tile below}
  5636.     {     CL = 6}
  5637.     {rem: WriteMode1 has been set already and remains set   }
  5638.     {     CH, BX may not be changed!           }
  5639.     MOV AL,[OFFSET BackTile +SI]
  5640.     XOR AH,AH      {compute offset address of tile:    }
  5641.                    {each tile is 64 bytes in size, thus}
  5642.     SHL AX,CL      {AX := tile * 64 = tile SHL 6       }
  5643.     MOV SI,AX
  5644.  
  5645.     MOV DX,DS   {save DS to DX    }
  5646.     MOV DS,SCROLLADR
  5647.  
  5648.     MOVSB       {1.row  }
  5649.     MOVSB
  5650.     MOVSB
  5651.     MOVSB
  5652.     ADD DI,LINESIZE-4   {ES:DI = ^next tile     }
  5653.  
  5654.     MOVSB       {2.row  }
  5655.     MOVSB
  5656.     MOVSB
  5657.     MOVSB
  5658.     ADD DI,LINESIZE-4
  5659.  
  5660.     MOVSB       {3.row  }
  5661.     MOVSB
  5662.     MOVSB
  5663.     MOVSB
  5664.     ADD DI,LINESIZE-4
  5665.  
  5666.     MOVSB       {4.row  }
  5667.     MOVSB
  5668.     MOVSB
  5669.     MOVSB
  5670.     ADD DI,LINESIZE-4
  5671.  
  5672.     MOVSB       {5.row  }
  5673.     MOVSB
  5674.     MOVSB
  5675.     MOVSB
  5676.     ADD DI,LINESIZE-4
  5677.  
  5678.     MOVSB       {6.row  }
  5679.     MOVSB
  5680.     MOVSB
  5681.     MOVSB
  5682.     ADD DI,LINESIZE-4
  5683.  
  5684.     MOVSB       {7.row  }
  5685.     MOVSB
  5686.     MOVSB
  5687.     MOVSB
  5688.     ADD DI,LINESIZE-4
  5689.  
  5690.     MOVSB       {8.row  }
  5691.     MOVSB
  5692.     MOVSB
  5693.     MOVSB
  5694.     ADD DI,LINESIZE-4
  5695.  
  5696.     MOVSB       {9.row  }
  5697.     MOVSB
  5698.     MOVSB
  5699.     MOVSB
  5700.     ADD DI,LINESIZE-4
  5701.  
  5702.     MOVSB      {10.row  }
  5703.     MOVSB
  5704.     MOVSB
  5705.     MOVSB
  5706.     ADD DI,LINESIZE-4
  5707.  
  5708.     MOVSB      {11.row  }
  5709.     MOVSB
  5710.     MOVSB
  5711.     MOVSB
  5712.     ADD DI,LINESIZE-4
  5713.  
  5714.     MOVSB      {12.row  }
  5715.     MOVSB
  5716.     MOVSB
  5717.     MOVSB
  5718.     ADD DI,LINESIZE-4
  5719.  
  5720.     MOVSB      {13.row  }
  5721.     MOVSB
  5722.     MOVSB
  5723.     MOVSB
  5724.     ADD DI,LINESIZE-4
  5725.  
  5726.     MOVSB      {14.row  }
  5727.     MOVSB
  5728.     MOVSB
  5729.     MOVSB
  5730.     ADD DI,LINESIZE-4
  5731.  
  5732.     MOVSB      {15.row  }
  5733.     MOVSB
  5734.     MOVSB
  5735.     MOVSB
  5736.     ADD DI,LINESIZE-4
  5737.  
  5738.     MOVSB      {16.row  }
  5739.     MOVSB
  5740.     MOVSB
  5741.     MOVSB
  5742.     ADD DI,LINESIZE-4
  5743.  
  5744.  
  5745.     MOV DS,DX
  5746.  
  5747.   {next tile; DI already has the right value}
  5748.   ADD BX,XTiles     {set temp. actIndex to next row        }
  5749.   INC ytil
  5750.   { MOV AX,16 }
  5751.   { ADD y,AX }
  5752.  
  5753.   DEC CH
  5754.   JNZ @m1yloop
  5755.  
  5756.   {actIndex still has its old value as only "oldActIndex" has been changed   }
  5757.   INC actIndex        {actIndex = next inner tile in uppermost tile row        }
  5758.   MOV BX,actIndex     {and use it as starting value for next column   }
  5759.  
  5760.   MOV DI,oldDI        {ES:DI = ^inner tile in uppermost tile row     }
  5761.   ADD DI,4            {increment by one tile   }
  5762.   MOV oldDI,DI        {and use it as starting value for next column   }
  5763.  
  5764.   MOV AX,tempYtil
  5765.   MOV ytil,AX     {set Y-coordinate back to the uppermost inner tile row    }
  5766.   { MOV AX,oldY }
  5767.   { MOV y,AX }
  5768.  
  5769.   INC xtil      {increment X-coordinate by one tile column  }
  5770.   { MOV AX,16 }
  5771.   { ADD x,AX }
  5772.  
  5773.   DEC counter
  5774.   JNZ @m1xloop
  5775.  
  5776.   MOV DI,tempDI       {Thus: ES:DI, actIndex, xtil, ytil, x, y point to      }
  5777.   MOV AX,tempActIndex {the first inner tile again (N.B.: y, ytil have        }
  5778.   MOV actIndex,AX     {restored some lines above, already!   }
  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     {hold a copy in DX   }
  5786.   ADD ytil,AX   {ytil points to the bottommost tile row   }
  5787.  
  5788.   MOV CL,5
  5789.   SHL AX,CL     {dto. for 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. for y: inc(y,16 * innerTilesY) }
  5794.  
  5795.   MOV AX,XTiles
  5796.   MUL DX          {AX := XTiles * innerTilesY}
  5797.   ADD actIndex,AX {dto. for actIndex: inc(actIndex,XTiles * innerTilesY) }
  5798.  
  5799.  @m1SkipInnerTiles:
  5800.   {ES:DI, actIndex, xtil, ytil, x, y point to the first inner tile of the}
  5801.   {bottommost tile row  }
  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 {are we standing at the right bottom corner tile? }
  5817.   MOV counter,AX
  5818.  
  5819.   {compute addition factor needed to position from bottom to top:}
  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 with WriteMode1: }
  5847.     { in: ES:DI = ^destination address}
  5848.     {     SI = tile index     }
  5849.     {     BX = adjustment for row addresses       }
  5850.     {     bottomcut, Win*, SCROLLADR,...}
  5851.     {out: ES:DI = ^destination address of the next tile to the right of it}
  5852.     {     BX = adjustment for row addresses       }
  5853.     {rem: WriteMode1 has been set already and remains set  }
  5854.     {     DX is not used       }
  5855.     MOV AL,[OFFSET BackTile +SI]
  5856.     XOR AH,AH      {compute offset address of tile:    }
  5857.     MOV CL,6       {each tile is 64 bytes in size, thus}
  5858.     SHL AX,CL      {AX := tile * 64 = tile SHL 6       }
  5859.     MOV SI,AX
  5860.  
  5861.     MOV CX,16
  5862.     SUB CX,bottomcut
  5863.  
  5864.     MOV AX,DS   {save DS to AX    }
  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 of the next row below the tile, now position to the next    }
  5878.     {tile:         }
  5879.     {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  5880.     {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  5881.     {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  5882.     { ▀                      }
  5883.     ADD DI,BX
  5884.  
  5885.   {position to next tile to the right of it:     }
  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 point to the lower right corner tile}
  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 with WriteMode1: }
  5913.     { in: ES:DI = ^destination address}
  5914.     {     SI = tile index     }
  5915.     {     rightcut MOD 4 = 0  }
  5916.     {     rightcut, bottomcut, Win*, SCROLLADR,...}
  5917.     {out: ES = ^graphic segment }
  5918.     {rem: WriteMode1 has been set already and remains set  }
  5919.     MOV AL,[OFFSET BackTile +SI]
  5920.     XOR AH,AH      {compute offset address of tile:    }
  5921.     MOV CL,6       {each tile is 64 bytes in size, thus}
  5922.     SHL AX,CL      {AX := tile * 64 = tile 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=row counter }
  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. point to lower right corner tile  }
  5954.  
  5955.  @m1SkipLowerRightCorner:
  5956.   CMP leftcut,0
  5957.   JE @m1fertig
  5958.  
  5959.   {now position on lower left corner tile:        }
  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 {don't forget: corner tile may be cut at its left side}
  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 with WriteMode1: }
  5984.     { in: ES:DI = ^destination address}
  5985.     {     SI = tile index     }
  5986.     {     rightcut MOD 4 = 0  }
  5987.     {     leftcut, bottomcut, Win*, SCROLLADR,...}
  5988.     {out: (ES = ^graphic segment) }
  5989.     {rem: WriteMode1 has been set (and remains set)          }
  5990.     MOV AL,[OFFSET BackTile +SI]
  5991.     XOR AH,AH      {compute offset address of tile:    }
  5992.     MOV CL,6       {each tile is 64 bytes in size, thus}
  5993.     SHL AX,CL      {AX := tile * 64 = tile 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 = row counter }
  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.   {now switch back to write mode 0:           }
  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 = correct value, if we have to jump!    }
  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 with WriteMode0: }
  6071.     { in: WinXMIN,WinYMIN = screen coordinates   }
  6072.     {     ES = ^graphic segment}
  6073.     {     SI = tile index    }
  6074.     {     BX = leftcut       }
  6075.     {     CX = xtil          }
  6076.     {     topcut, Win*, SCROLLADR,...}
  6077.     {out: ES = ^graphic segment}
  6078.     {rem: WriteMode0 has been set already and remains set  }
  6079.     MOV AL,[OFFSET BackTile +SI]
  6080.     XOR AH,AH      {compute offset address of tile:    }
  6081.     MOV CL,6       {each tile is 64 bytes in size, thus}
  6082.     SHL AX,CL      {AX := tile * 64 = tile SHL 6       }
  6083.  
  6084.     MOV SI,topcut  {additionaly: the rows cut off at the upper edge:}
  6085.     MOV CX,16      {4 bytes for each row  }
  6086.     SUB CX,SI      {CX := 16 - topcut = rows to draw        }
  6087.     SHL SI,1
  6088.     SHL SI,1
  6089.     ADD SI,AX      {SI = pointer to first tile *row* to copy       }
  6090.  
  6091.     MOV AX,BX      {BX = leftcut}
  6092.     SHR AX,1       {increment SI by left cut-off = leftcut DIV 4    }
  6093.     SHR AX,1
  6094.     ADD SI,AX      {SI = pointer to first tile *byte* to copy       }
  6095.  
  6096.     MOV DI,WinYMINmLINESIZEaWinXMINdiv4 {ES:DI = destination address}
  6097.     MOV DS,SCROLLADR  {DS:SI = source address}
  6098.     {Now there is no further variable on stack, so BP can be used   }
  6099.     {for other purposes!}
  6100.     PUSH BP        {will be needed when leaving the procedure! }
  6101.     MOV BP,16+3    {BP:=(16 + 3 - leftcut) DIV 4 = bytes per tile row  }
  6102.     SUB BP,BX
  6103.     PUSH BP        {save BP for next plane     }
  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      {select first read plane}
  6112.     PUSH AX
  6113.     MOV DX,3C4h
  6114.     MOV AX,0102h
  6115.     OUT DX,AX      {choose write plane 0 }
  6116.  
  6117.     MOV AX,LINESIZE  {is a constant     }
  6118.     SUB AX,BP
  6119.     MOV DX,4
  6120.     SUB DX,BP
  6121.     MOV BX,CX      {number of rows   }
  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      {choose write plane 1 }
  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      {select next read plane  }
  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  {is a constant     }
  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      {choose write plane 2 }
  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      {select next read plane  }
  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  {is a constant     }
  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      {choose write plane 3 }
  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      {select next read plane  }
  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  {is a constant     }
  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.   {position to next tile to the right of it:     }
  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 = ^destination address}
  6259.  
  6260.   {now draw innerTilesX tiles, which are only cut at their top:}
  6261.   MOV AX,innerTilesX
  6262.   OR AX,AX
  6263.   JBE @m0UpperInnerTilesDone
  6264.   MOV counter,AX
  6265.  
  6266.   MOV BX,16        {correction factor to set DI one tile row higher and one}
  6267.   SUB BX,topcut    {one tile column further    }
  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 with WriteMode0: }
  6288.     { in: ES:DI = ^destination address}
  6289.     {     SI = tile index     }
  6290.     {     topcut, Win*, SCROLLADR,...}
  6291.     {out: ES:DI = ^destination address of the next tile to the right of it}
  6292.     {rem: WriteMode0 has been set already and remains set  }
  6293.     MOV AL,[OFFSET BackTile +SI]
  6294.     XOR AH,AH      {compute offset address of tile:    }
  6295.     MOV CL,6       {each tile is 64 bytes in size, thus}
  6296.     SHL AX,CL      {AX := tile * 64 = tile SHL 6       }
  6297.  
  6298.     MOV SI,topcut  {additionaly: the rows cut off at the upper edge:}
  6299.     MOV CX,16      {4 bytes for each row  }
  6300.     SUB CX,SI      {CX := 16 - topcut = rows to draw        }
  6301.     SHL SI,1
  6302.     SHL SI,1
  6303.     ADD SI,AX      {SI = pointer to first tile *row* to copy       }
  6304.  
  6305.     PUSH BP        {save BP  }
  6306.     MOV DX,3C4h
  6307.     MOV AX,StartWritePlane
  6308.     OUT DX,AX      {select first write plane }
  6309.     PUSH AX
  6310.     MOV DX,3CEh
  6311.     MOV AX,0004h
  6312.     OUT DX,AX      {choose read plane 0}
  6313.  
  6314.     MOV DS,SCROLLADR   {now unused: AX, BX, BP, DX}
  6315.  
  6316.     MOV BX,CX      {copy of row counter}
  6317.     MOV BP,SI      {BP = copy of SI  }
  6318.     MOV AX,DI      {AX = copy of DI  }
  6319.    @m0eineZeile4b1:
  6320.     MOVSW
  6321.     MOVSW
  6322.     ADD DI,LINESIZE-4
  6323.     LOOP @m0eineZeile4b1
  6324.     MOV SI,BP      {restore old values         }
  6325.     MOV DI,AX
  6326.     MOV CX,BX
  6327.  
  6328.     MOV AX,0104h
  6329.     OUT DX,AX      {DX = 3CEh -> choose read plane 1}
  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      {select next write plane    }
  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      {restore old values         }
  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 -> select next write plane    }
  6359.     PUSH AX
  6360.     MOV DX,3CEh
  6361.     MOV AX,0204h
  6362.     OUT DX,AX      {choose read plane 2}
  6363.     MOV AX,DI
  6364.  
  6365.    @m0eineZeile4b3:
  6366.     MOVSW
  6367.     MOVSW
  6368.     ADD DI,LINESIZE-4
  6369.     LOOP @m0eineZeile4b3
  6370.     MOV SI,BP      {restore old values         }
  6371.     MOV DI,AX
  6372.     MOV CX,BX
  6373.  
  6374.     MOV AX,0304h
  6375.     OUT DX,AX      {DX = 3CEh -> choose read plane 3}
  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      {select last write plane: no more PUSHs necessary}
  6385.  
  6386.    @m0eineZeile4b4:
  6387.     MOVSW
  6388.     MOVSW
  6389.     ADD DI,LINESIZE-4
  6390.     LOOP @m0eineZeile4b4
  6391.                    {don't restore old values         }
  6392.  
  6393.     POP BP         {make TP happy      }
  6394.     MOV AX,SEG @Data
  6395.     MOV DS,AX
  6396.  
  6397.     {DI = ^start of the next row below the tile, now position to the next    }
  6398.     {tile:         }
  6399.     {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  6400.     {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  6401.     {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  6402.     { ▀                      }
  6403.     ADD DI,Korrektur
  6404.     {-1, because "inc di" has been executed exactly 1x}
  6405.     DEC DI
  6406.  
  6407.   {position to next tile to the right of it:     }
  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 = ^first row of the upper right corner tile}
  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 with WriteMode0: }
  6433.     { in: ES:DI = ^destination address}
  6434.     {     SI = tile index     }
  6435.     {     StartWritePlane = first bitplane to write to     }
  6436.     {     topcut, rightcut, Win*, SCROLLADR,...}
  6437.     {out: ES = ^graphic segment }
  6438.     {rem: WriteMode0 has been set already and remains set  }
  6439.     MOV AL,[OFFSET BackTile +SI]
  6440.     XOR AH,AH      {compute offset address of tile:    }
  6441.     MOV CL,6       {each tile is 64 bytes in size, thus}
  6442.     SHL AX,CL      {AX := tile * 64 = tile SHL 6       }
  6443.  
  6444.     MOV SI,topcut  {additionaly: the rows cut off at the upper edge:}
  6445.     MOV CX,16      {4 bytes for each row  }
  6446.     SUB CX,SI      {CX := 16 - topcut = rows to draw        }
  6447.     SHL SI,1
  6448.     SHL SI,1
  6449.     ADD SI,AX      {SI = pointer to first tile *row* to copy       }
  6450.  
  6451.     MOV BX,rightcut
  6452.     PUSH BP        {will be needed when leaving the procedure! }
  6453.     MOV DS,SCROLLADR  {DS:SI = source address}
  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.     {Now there is no further variable on stack, so BP can be used   }
  6462.     {for other purposes!}
  6463.     MOV BP,16+3    {BP:=(16 + 3 - rightcut) DIV 4 = bytes per tile row  }
  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 := rows to draw        }
  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.   {position on first left tile, which is not cut at its top:                 }
  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 = ^destination address of the 1st tile of the 1st tile row not being cut at its top}
  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   {correction factor for AX}
  6636.   MOV LINESIZE_sub_BytesPerPlane,AX
  6637.   MOV BytesPerPlane,DX  {bytes to move }
  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  {is a constant for the tile column}
  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 with WriteMode0: }
  6672.     { in: ES:DI = ^destination address}
  6673.     {     SI = tile index     }
  6674.     {     StartLesePlane = first bitplane to read from         }
  6675.     {     LINESIZE_sub_BytesPerPlane, BytesPerPlane = correction factors}
  6676.     {     leftcut, leftcutDIV4, Win*, SCROLLADR,...}
  6677.     {out: ES:DI = ^destination address of the next tile below}
  6678.     {rem: WriteMode0 has been set already and remains set   }
  6679.     MOV AL,[OFFSET BackTile +SI]
  6680.     XOR AH,AH      {compute offset address of tile:    }
  6681.     MOV CL,6       {each tile is 64 bytes in size, thus}
  6682.     SHL AX,CL      {AX := tile * 64 = tile 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.row  }
  6711.     REP MOVSB
  6712.     ADD SI,DX
  6713.     ADD DI,AX   {ES:DI = ^next tile     }
  6714.  
  6715.     MOV CX,BP   {2.row  }
  6716.     REP MOVSB
  6717.     ADD SI,DX
  6718.     ADD DI,AX
  6719.  
  6720.     MOV CX,BP   {3.row  }
  6721.     REP MOVSB
  6722.     ADD SI,DX
  6723.     ADD DI,AX
  6724.  
  6725.     MOV CX,BP   {4.row  }
  6726.     REP MOVSB
  6727.     ADD SI,DX
  6728.     ADD DI,AX
  6729.  
  6730.     MOV CX,BP   {5.row  }
  6731.     REP MOVSB
  6732.     ADD SI,DX
  6733.     ADD DI,AX
  6734.  
  6735.     MOV CX,BP   {6.row  }
  6736.     REP MOVSB
  6737.     ADD SI,DX
  6738.     ADD DI,AX
  6739.  
  6740.     MOV CX,BP   {7.row  }
  6741.     REP MOVSB
  6742.     ADD SI,DX
  6743.     ADD DI,AX
  6744.  
  6745.     MOV CX,BP   {8.row  }
  6746.     REP MOVSB
  6747.     ADD SI,DX
  6748.     ADD DI,AX
  6749.  
  6750.     MOV CX,BP   {9.row  }
  6751.     REP MOVSB
  6752.     ADD SI,DX
  6753.     ADD DI,AX
  6754.  
  6755.     MOV CX,BP  {10.row  }
  6756.     REP MOVSB
  6757.     ADD SI,DX
  6758.     ADD DI,AX
  6759.  
  6760.     MOV CX,BP  {11.row  }
  6761.     REP MOVSB
  6762.     ADD SI,DX
  6763.     ADD DI,AX
  6764.  
  6765.     MOV CX,BP  {12.row  }
  6766.     REP MOVSB
  6767.     ADD SI,DX
  6768.     ADD DI,AX
  6769.  
  6770.     MOV CX,BP  {13.row  }
  6771.     REP MOVSB
  6772.     ADD SI,DX
  6773.     ADD DI,AX
  6774.  
  6775.     MOV CX,BP  {14.row  }
  6776.     REP MOVSB
  6777.     ADD SI,DX
  6778.     ADD DI,AX
  6779.  
  6780.     MOV CX,BP  {15.row  }
  6781.     REP MOVSB
  6782.     ADD SI,DX
  6783.     ADD DI,AX
  6784.  
  6785.     MOV CX,BP  {16.row  }
  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.row  }
  6816.     REP MOVSB
  6817.     ADD SI,DX
  6818.     ADD DI,AX   {ES:DI = ^next tile     }
  6819.  
  6820.     MOV CX,BP   {2.row  }
  6821.     REP MOVSB
  6822.     ADD SI,DX
  6823.     ADD DI,AX
  6824.  
  6825.     MOV CX,BP   {3.row  }
  6826.     REP MOVSB
  6827.     ADD SI,DX
  6828.     ADD DI,AX
  6829.  
  6830.     MOV CX,BP   {4.row  }
  6831.     REP MOVSB
  6832.     ADD SI,DX
  6833.     ADD DI,AX
  6834.  
  6835.     MOV CX,BP   {5.row  }
  6836.     REP MOVSB
  6837.     ADD SI,DX
  6838.     ADD DI,AX
  6839.  
  6840.     MOV CX,BP   {6.row  }
  6841.     REP MOVSB
  6842.     ADD SI,DX
  6843.     ADD DI,AX
  6844.  
  6845.     MOV CX,BP   {7.row  }
  6846.     REP MOVSB
  6847.     ADD SI,DX
  6848.     ADD DI,AX
  6849.  
  6850.     MOV CX,BP   {8.row  }
  6851.     REP MOVSB
  6852.     ADD SI,DX
  6853.     ADD DI,AX
  6854.  
  6855.     MOV CX,BP   {9.row  }
  6856.     REP MOVSB
  6857.     ADD SI,DX
  6858.     ADD DI,AX
  6859.  
  6860.     MOV CX,BP  {10.row  }
  6861.     REP MOVSB
  6862.     ADD SI,DX
  6863.     ADD DI,AX
  6864.  
  6865.     MOV CX,BP  {11.row  }
  6866.     REP MOVSB
  6867.     ADD SI,DX
  6868.     ADD DI,AX
  6869.  
  6870.     MOV CX,BP  {12.row  }
  6871.     REP MOVSB
  6872.     ADD SI,DX
  6873.     ADD DI,AX
  6874.  
  6875.     MOV CX,BP  {13.row  }
  6876.     REP MOVSB
  6877.     ADD SI,DX
  6878.     ADD DI,AX
  6879.  
  6880.     MOV CX,BP  {14.row  }
  6881.     REP MOVSB
  6882.     ADD SI,DX
  6883.     ADD DI,AX
  6884.  
  6885.     MOV CX,BP  {15.row  }
  6886.     REP MOVSB
  6887.     ADD SI,DX
  6888.     ADD DI,AX
  6889.  
  6890.     MOV CX,BP  {16.row  }
  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.row  }
  6921.     REP MOVSB
  6922.     ADD SI,DX
  6923.     ADD DI,AX   {ES:DI = ^next tile     }
  6924.  
  6925.     MOV CX,BP   {2.row  }
  6926.     REP MOVSB
  6927.     ADD SI,DX
  6928.     ADD DI,AX
  6929.  
  6930.     MOV CX,BP   {3.row  }
  6931.     REP MOVSB
  6932.     ADD SI,DX
  6933.     ADD DI,AX
  6934.  
  6935.     MOV CX,BP   {4.row  }
  6936.     REP MOVSB
  6937.     ADD SI,DX
  6938.     ADD DI,AX
  6939.  
  6940.     MOV CX,BP   {5.row  }
  6941.     REP MOVSB
  6942.     ADD SI,DX
  6943.     ADD DI,AX
  6944.  
  6945.     MOV CX,BP   {6.row  }
  6946.     REP MOVSB
  6947.     ADD SI,DX
  6948.     ADD DI,AX
  6949.  
  6950.     MOV CX,BP   {7.row  }
  6951.     REP MOVSB
  6952.     ADD SI,DX
  6953.     ADD DI,AX
  6954.  
  6955.     MOV CX,BP   {8.row  }
  6956.     REP MOVSB
  6957.     ADD SI,DX
  6958.     ADD DI,AX
  6959.  
  6960.     MOV CX,BP   {9.row  }
  6961.     REP MOVSB
  6962.     ADD SI,DX
  6963.     ADD DI,AX
  6964.  
  6965.     MOV CX,BP  {10.row  }
  6966.     REP MOVSB
  6967.     ADD SI,DX
  6968.     ADD DI,AX
  6969.  
  6970.     MOV CX,BP  {11.row  }
  6971.     REP MOVSB
  6972.     ADD SI,DX
  6973.     ADD DI,AX
  6974.  
  6975.     MOV CX,BP  {12.row  }
  6976.     REP MOVSB
  6977.     ADD SI,DX
  6978.     ADD DI,AX
  6979.  
  6980.     MOV CX,BP  {13.row  }
  6981.     REP MOVSB
  6982.     ADD SI,DX
  6983.     ADD DI,AX
  6984.  
  6985.     MOV CX,BP  {14.row  }
  6986.     REP MOVSB
  6987.     ADD SI,DX
  6988.     ADD DI,AX
  6989.  
  6990.     MOV CX,BP  {15.row  }
  6991.     REP MOVSB
  6992.     ADD SI,DX
  6993.     ADD DI,AX
  6994.  
  6995.     MOV CX,BP  {16.row  }
  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.row  }
  7026.     REP MOVSB
  7027.     ADD SI,DX
  7028.     ADD DI,AX   {ES:DI = ^next tile     }
  7029.  
  7030.     MOV CX,BP   {2.row  }
  7031.     REP MOVSB
  7032.     ADD SI,DX
  7033.     ADD DI,AX
  7034.  
  7035.     MOV CX,BP   {3.row  }
  7036.     REP MOVSB
  7037.     ADD SI,DX
  7038.     ADD DI,AX
  7039.  
  7040.     MOV CX,BP   {4.row  }
  7041.     REP MOVSB
  7042.     ADD SI,DX
  7043.     ADD DI,AX
  7044.  
  7045.     MOV CX,BP   {5.row  }
  7046.     REP MOVSB
  7047.     ADD SI,DX
  7048.     ADD DI,AX
  7049.  
  7050.     MOV CX,BP   {6.row  }
  7051.     REP MOVSB
  7052.     ADD SI,DX
  7053.     ADD DI,AX
  7054.  
  7055.     MOV CX,BP   {7.row  }
  7056.     REP MOVSB
  7057.     ADD SI,DX
  7058.     ADD DI,AX
  7059.  
  7060.     MOV CX,BP   {8.row  }
  7061.     REP MOVSB
  7062.     ADD SI,DX
  7063.     ADD DI,AX
  7064.  
  7065.     MOV CX,BP   {9.row  }
  7066.     REP MOVSB
  7067.     ADD SI,DX
  7068.     ADD DI,AX
  7069.  
  7070.     MOV CX,BP  {10.row  }
  7071.     REP MOVSB
  7072.     ADD SI,DX
  7073.     ADD DI,AX
  7074.  
  7075.     MOV CX,BP  {11.row  }
  7076.     REP MOVSB
  7077.     ADD SI,DX
  7078.     ADD DI,AX
  7079.  
  7080.     MOV CX,BP  {12.row  }
  7081.     REP MOVSB
  7082.     ADD SI,DX
  7083.     ADD DI,AX
  7084.  
  7085.     MOV CX,BP  {13.row  }
  7086.     REP MOVSB
  7087.     ADD SI,DX
  7088.     ADD DI,AX
  7089.  
  7090.     MOV CX,BP  {14.row  }
  7091.     REP MOVSB
  7092.     ADD SI,DX
  7093.     ADD DI,AX
  7094.  
  7095.     MOV CX,BP  {15.row  }
  7096.     REP MOVSB
  7097.     ADD SI,DX
  7098.     ADD DI,AX
  7099.  
  7100.     MOV CX,BP  {16.row  }
  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.   {next tile; DI already has the right value}
  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, because "inc di" has been executed exactly 1x}
  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 = ^destination address of the first inner tile (still)}
  7138.  
  7139.   MOV AX,innerTilesY    {are there any inner tiles at all?}
  7140.   OR AX,AX
  7141.   JBE @m0SkipInnerTiles {no  }
  7142.   CMP innerTilesX,0
  7143.   JB @m0SkipInnerTiles
  7144.  
  7145.   MOV counter,AX
  7146.   MOV AX,actIndex     {make copies of the actual values  }
  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 = ^first right corner tile being not cut at its top   }
  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 with WriteMode0: }
  7202.     { in: ES:DI = ^destination address}
  7203.     {     SI = tile index     }
  7204.     {     StartWritePlane = first bitplane to write to     }
  7205.     {     innerTilesY >= 1    }
  7206.     {     rightcut, Win*, SCROLLADR,...}
  7207.     {out: ES:DI = ^destination address of the next tile below}
  7208.     {rem: WriteMode0 has been set already and remains set   }
  7209.     MOV AL,[OFFSET BackTile +SI]
  7210.     XOR AH,AH      {compute offset address of tile:    }
  7211.     MOV CL,6       {each tile is 64 bytes in size, thus}
  7212.     SHL AX,CL      {AX := tile * 64 = tile 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.row  }
  7240.     REP MOVSB
  7241.     ADD SI,DX
  7242.     ADD DI,AX   {ES:DI = ^next tile     }
  7243.  
  7244.     MOV CX,BP   {2.row  }
  7245.     REP MOVSB
  7246.     ADD SI,DX
  7247.     ADD DI,AX
  7248.  
  7249.     MOV CX,BP   {3.row  }
  7250.     REP MOVSB
  7251.     ADD SI,DX
  7252.     ADD DI,AX
  7253.  
  7254.     MOV CX,BP   {4.row  }
  7255.     REP MOVSB
  7256.     ADD SI,DX
  7257.     ADD DI,AX
  7258.  
  7259.     MOV CX,BP   {5.row  }
  7260.     REP MOVSB
  7261.     ADD SI,DX
  7262.     ADD DI,AX
  7263.  
  7264.     MOV CX,BP   {6.row  }
  7265.     REP MOVSB
  7266.     ADD SI,DX
  7267.     ADD DI,AX
  7268.  
  7269.     MOV CX,BP   {7.row  }
  7270.     REP MOVSB
  7271.     ADD SI,DX
  7272.     ADD DI,AX
  7273.  
  7274.     MOV CX,BP   {8.row  }
  7275.     REP MOVSB
  7276.     ADD SI,DX
  7277.     ADD DI,AX
  7278.  
  7279.     MOV CX,BP   {9.row  }
  7280.     REP MOVSB
  7281.     ADD SI,DX
  7282.     ADD DI,AX
  7283.  
  7284.     MOV CX,BP  {10.row  }
  7285.     REP MOVSB
  7286.     ADD SI,DX
  7287.     ADD DI,AX
  7288.  
  7289.     MOV CX,BP  {11.row  }
  7290.     REP MOVSB
  7291.     ADD SI,DX
  7292.     ADD DI,AX
  7293.  
  7294.     MOV CX,BP  {12.row  }
  7295.     REP MOVSB
  7296.     ADD SI,DX
  7297.     ADD DI,AX
  7298.  
  7299.     MOV CX,BP  {13.row  }
  7300.     REP MOVSB
  7301.     ADD SI,DX
  7302.     ADD DI,AX
  7303.  
  7304.     MOV CX,BP  {14.row  }
  7305.     REP MOVSB
  7306.     ADD SI,DX
  7307.     ADD DI,AX
  7308.  
  7309.     MOV CX,BP  {15.row  }
  7310.     REP MOVSB
  7311.     ADD SI,DX
  7312.     ADD DI,AX
  7313.  
  7314.     MOV CX,BP  {16.row  }
  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.row  }
  7346.     REP MOVSB
  7347.     ADD SI,DX
  7348.     ADD DI,AX   {ES:DI = ^next tile     }
  7349.  
  7350.     MOV CX,BP   {2.row  }
  7351.     REP MOVSB
  7352.     ADD SI,DX
  7353.     ADD DI,AX
  7354.  
  7355.     MOV CX,BP   {3.row  }
  7356.     REP MOVSB
  7357.     ADD SI,DX
  7358.     ADD DI,AX
  7359.  
  7360.     MOV CX,BP   {4.row  }
  7361.     REP MOVSB
  7362.     ADD SI,DX
  7363.     ADD DI,AX
  7364.  
  7365.     MOV CX,BP   {5.row  }
  7366.     REP MOVSB
  7367.     ADD SI,DX
  7368.     ADD DI,AX
  7369.  
  7370.     MOV CX,BP   {6.row  }
  7371.     REP MOVSB
  7372.     ADD SI,DX
  7373.     ADD DI,AX
  7374.  
  7375.     MOV CX,BP   {7.row  }
  7376.     REP MOVSB
  7377.     ADD SI,DX
  7378.     ADD DI,AX
  7379.  
  7380.     MOV CX,BP   {8.row  }
  7381.     REP MOVSB
  7382.     ADD SI,DX
  7383.     ADD DI,AX
  7384.  
  7385.     MOV CX,BP   {9.row  }
  7386.     REP MOVSB
  7387.     ADD SI,DX
  7388.     ADD DI,AX
  7389.  
  7390.     MOV CX,BP  {10.row  }
  7391.     REP MOVSB
  7392.     ADD SI,DX
  7393.     ADD DI,AX
  7394.  
  7395.     MOV CX,BP  {11.row  }
  7396.     REP MOVSB
  7397.     ADD SI,DX
  7398.     ADD DI,AX
  7399.  
  7400.     MOV CX,BP  {12.row  }
  7401.     REP MOVSB
  7402.     ADD SI,DX
  7403.     ADD DI,AX
  7404.  
  7405.     MOV CX,BP  {13.row  }
  7406.     REP MOVSB
  7407.     ADD SI,DX
  7408.     ADD DI,AX
  7409.  
  7410.     MOV CX,BP  {14.row  }
  7411.     REP MOVSB
  7412.     ADD SI,DX
  7413.     ADD DI,AX
  7414.  
  7415.     MOV CX,BP  {15.row  }
  7416.     REP MOVSB
  7417.     ADD SI,DX
  7418.     ADD DI,AX
  7419.  
  7420.     MOV CX,BP  {16.row  }
  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.row  }
  7452.     REP MOVSB
  7453.     ADD SI,DX
  7454.     ADD DI,AX   {ES:DI = ^next tile     }
  7455.  
  7456.     MOV CX,BP   {2.row  }
  7457.     REP MOVSB
  7458.     ADD SI,DX
  7459.     ADD DI,AX
  7460.  
  7461.     MOV CX,BP   {3.row  }
  7462.     REP MOVSB
  7463.     ADD SI,DX
  7464.     ADD DI,AX
  7465.  
  7466.     MOV CX,BP   {4.row  }
  7467.     REP MOVSB
  7468.     ADD SI,DX
  7469.     ADD DI,AX
  7470.  
  7471.     MOV CX,BP   {5.row  }
  7472.     REP MOVSB
  7473.     ADD SI,DX
  7474.     ADD DI,AX
  7475.  
  7476.     MOV CX,BP   {6.row  }
  7477.     REP MOVSB
  7478.     ADD SI,DX
  7479.     ADD DI,AX
  7480.  
  7481.     MOV CX,BP   {7.row  }
  7482.     REP MOVSB
  7483.     ADD SI,DX
  7484.     ADD DI,AX
  7485.  
  7486.     MOV CX,BP   {8.row  }
  7487.     REP MOVSB
  7488.     ADD SI,DX
  7489.     ADD DI,AX
  7490.  
  7491.     MOV CX,BP   {9.row  }
  7492.     REP MOVSB
  7493.     ADD SI,DX
  7494.     ADD DI,AX
  7495.  
  7496.     MOV CX,BP  {10.row  }
  7497.     REP MOVSB
  7498.     ADD SI,DX
  7499.     ADD DI,AX
  7500.  
  7501.     MOV CX,BP  {11.row  }
  7502.     REP MOVSB
  7503.     ADD SI,DX
  7504.     ADD DI,AX
  7505.  
  7506.     MOV CX,BP  {12.row  }
  7507.     REP MOVSB
  7508.     ADD SI,DX
  7509.     ADD DI,AX
  7510.  
  7511.     MOV CX,BP  {13.row  }
  7512.     REP MOVSB
  7513.     ADD SI,DX
  7514.     ADD DI,AX
  7515.  
  7516.     MOV CX,BP  {14.row  }
  7517.     REP MOVSB
  7518.     ADD SI,DX
  7519.     ADD DI,AX
  7520.  
  7521.     MOV CX,BP  {15.row  }
  7522.     REP MOVSB
  7523.     ADD SI,DX
  7524.     ADD DI,AX
  7525.  
  7526.     MOV CX,BP  {16.row  }
  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.row  }
  7558.     REP MOVSB
  7559.     ADD SI,DX
  7560.     ADD DI,AX   {ES:DI = ^next tile     }
  7561.  
  7562.     MOV CX,BP   {2.row  }
  7563.     REP MOVSB
  7564.     ADD SI,DX
  7565.     ADD DI,AX
  7566.  
  7567.     MOV CX,BP   {3.row  }
  7568.     REP MOVSB
  7569.     ADD SI,DX
  7570.     ADD DI,AX
  7571.  
  7572.     MOV CX,BP   {4.row  }
  7573.     REP MOVSB
  7574.     ADD SI,DX
  7575.     ADD DI,AX
  7576.  
  7577.     MOV CX,BP   {5.row  }
  7578.     REP MOVSB
  7579.     ADD SI,DX
  7580.     ADD DI,AX
  7581.  
  7582.     MOV CX,BP   {6.row  }
  7583.     REP MOVSB
  7584.     ADD SI,DX
  7585.     ADD DI,AX
  7586.  
  7587.     MOV CX,BP   {7.row  }
  7588.     REP MOVSB
  7589.     ADD SI,DX
  7590.     ADD DI,AX
  7591.  
  7592.     MOV CX,BP   {8.row  }
  7593.     REP MOVSB
  7594.     ADD SI,DX
  7595.     ADD DI,AX
  7596.  
  7597.     MOV CX,BP   {9.row  }
  7598.     REP MOVSB
  7599.     ADD SI,DX
  7600.     ADD DI,AX
  7601.  
  7602.     MOV CX,BP  {10.row  }
  7603.     REP MOVSB
  7604.     ADD SI,DX
  7605.     ADD DI,AX
  7606.  
  7607.     MOV CX,BP  {11.row  }
  7608.     REP MOVSB
  7609.     ADD SI,DX
  7610.     ADD DI,AX
  7611.  
  7612.     MOV CX,BP  {12.row  }
  7613.     REP MOVSB
  7614.     ADD SI,DX
  7615.     ADD DI,AX
  7616.  
  7617.     MOV CX,BP  {13.row  }
  7618.     REP MOVSB
  7619.     ADD SI,DX
  7620.     ADD DI,AX
  7621.  
  7622.     MOV CX,BP  {14.row  }
  7623.     REP MOVSB
  7624.     ADD SI,DX
  7625.     ADD DI,AX
  7626.  
  7627.     MOV CX,BP  {15.row  }
  7628.     REP MOVSB
  7629.     ADD SI,DX
  7630.     ADD DI,AX
  7631.  
  7632.     MOV CX,BP  {16.row  }
  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.   {next tile; DI already has the right value}
  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 = ^destination address of the first inner tile (still)}
  7669.   {innerTilesX >= 0, innerTilesX >= 1 -> it would suffice to check innerTilesX=0:}
  7670.  
  7671.   CMP innerTilesX,0   {IF (innerTilesX <= 0) OR (innerTilesY <= 0) THEN skip}
  7672.   JBE @m0SkipInnerTiles  {If there are no inner tiles, then the            }
  7673.                          {actual position is located at the 1st tile which    }
  7674.                          {is not cut at its left and is in the lowest tile row }
  7675.  
  7676.  
  7677.   {now "FOR x:=1 TO innerTilesX DO FOR y:=1 TO innerTilesY DO .."            }
  7678.   MOV oldDI,DI        {make temporary copies of DI and actIndex    }
  7679.   MOV AX,actIndex
  7680.   MOV oldActIndex,AX
  7681.  
  7682.   MOV AX,innerTilesX
  7683.   MOV counter,AX  {counter for X-direction}
  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  {is a constant for the tile column}
  7695.  
  7696.   MOV AX,innerTilesY
  7697.   MOV CH,AL  {CH is used as a counter for the Y-direction}
  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 with WriteMode0: }
  7714.     { in: ES:DI = ^destination address}
  7715.     {     SI = tile index     }
  7716.     {     CL = 6              }
  7717.     {     SCROLLADR}
  7718.     {out: ES:DI = ^destination address of the next tile below}
  7719.     {     CL = 6}
  7720.     {rem: WriteMode0 has been set already and remains set   }
  7721.     {     CH will not be changed! }
  7722.     MOV AL,[OFFSET BackTile +SI]
  7723.     XOR AH,AH      {compute offset address of tile:    }
  7724.                    {each tile is 64 bytes in size, thus}
  7725.     SHL AX,CL      {AX := tile * 64 = tile 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.row  }
  7739.     MOVSW
  7740.     ADD DI,LINESIZE-4   {ES:DI = ^next tile     }
  7741.  
  7742.     MOVSW       {2.row  }
  7743.     MOVSW
  7744.     ADD DI,LINESIZE-4
  7745.  
  7746.     MOVSW       {3.row  }
  7747.     MOVSW
  7748.     ADD DI,LINESIZE-4
  7749.  
  7750.     MOVSW       {4.row  }
  7751.     MOVSW
  7752.     ADD DI,LINESIZE-4
  7753.  
  7754.     MOVSW       {5.row  }
  7755.     MOVSW
  7756.     ADD DI,LINESIZE-4
  7757.  
  7758.     MOVSW       {6.row  }
  7759.     MOVSW
  7760.     ADD DI,LINESIZE-4
  7761.  
  7762.     MOVSW       {7.row  }
  7763.     MOVSW
  7764.     ADD DI,LINESIZE-4
  7765.  
  7766.     MOVSW       {8.row  }
  7767.     MOVSW
  7768.     ADD DI,LINESIZE-4
  7769.  
  7770.     MOVSW       {9.row  }
  7771.     MOVSW
  7772.     ADD DI,LINESIZE-4
  7773.  
  7774.     MOVSW      {10.row  }
  7775.     MOVSW
  7776.     ADD DI,LINESIZE-4
  7777.  
  7778.     MOVSW      {11.row  }
  7779.     MOVSW
  7780.     ADD DI,LINESIZE-4
  7781.  
  7782.     MOVSW      {12.row  }
  7783.     MOVSW
  7784.     ADD DI,LINESIZE-4
  7785.  
  7786.     MOVSW      {13.row  }
  7787.     MOVSW
  7788.     ADD DI,LINESIZE-4
  7789.  
  7790.     MOVSW      {14.row  }
  7791.     MOVSW
  7792.     ADD DI,LINESIZE-4
  7793.  
  7794.     MOVSW      {15.row  }
  7795.     MOVSW
  7796.     ADD DI,LINESIZE-4
  7797.  
  7798.     MOVSW      {16.row  }
  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.row  }
  7817.     MOVSW
  7818.     ADD DI,LINESIZE-4   {ES:DI = ^next tile     }
  7819.  
  7820.     MOVSW       {2.row  }
  7821.     MOVSW
  7822.     ADD DI,LINESIZE-4
  7823.  
  7824.     MOVSW       {3.row  }
  7825.     MOVSW
  7826.     ADD DI,LINESIZE-4
  7827.  
  7828.     MOVSW       {4.row  }
  7829.     MOVSW
  7830.     ADD DI,LINESIZE-4
  7831.  
  7832.     MOVSW       {5.row  }
  7833.     MOVSW
  7834.     ADD DI,LINESIZE-4
  7835.  
  7836.     MOVSW       {6.row  }
  7837.     MOVSW
  7838.     ADD DI,LINESIZE-4
  7839.  
  7840.     MOVSW       {7.row  }
  7841.     MOVSW
  7842.     ADD DI,LINESIZE-4
  7843.  
  7844.     MOVSW       {8.row  }
  7845.     MOVSW
  7846.     ADD DI,LINESIZE-4
  7847.  
  7848.     MOVSW       {9.row  }
  7849.     MOVSW
  7850.     ADD DI,LINESIZE-4
  7851.  
  7852.     MOVSW      {10.row  }
  7853.     MOVSW
  7854.     ADD DI,LINESIZE-4
  7855.  
  7856.     MOVSW      {11.row  }
  7857.     MOVSW
  7858.     ADD DI,LINESIZE-4
  7859.  
  7860.     MOVSW      {12.row  }
  7861.     MOVSW
  7862.     ADD DI,LINESIZE-4
  7863.  
  7864.     MOVSW      {13.row  }
  7865.     MOVSW
  7866.     ADD DI,LINESIZE-4
  7867.  
  7868.     MOVSW      {14.row  }
  7869.     MOVSW
  7870.     ADD DI,LINESIZE-4
  7871.  
  7872.     MOVSW      {15.row  }
  7873.     MOVSW
  7874.     ADD DI,LINESIZE-4
  7875.  
  7876.     MOVSW      {16.row  }
  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.row  }
  7895.     MOVSW
  7896.     ADD DI,LINESIZE-4   {ES:DI = ^next tile     }
  7897.  
  7898.     MOVSW       {2.row  }
  7899.     MOVSW
  7900.     ADD DI,LINESIZE-4
  7901.  
  7902.     MOVSW       {3.row  }
  7903.     MOVSW
  7904.     ADD DI,LINESIZE-4
  7905.  
  7906.     MOVSW       {4.row  }
  7907.     MOVSW
  7908.     ADD DI,LINESIZE-4
  7909.  
  7910.     MOVSW       {5.row  }
  7911.     MOVSW
  7912.     ADD DI,LINESIZE-4
  7913.  
  7914.     MOVSW       {6.row  }
  7915.     MOVSW
  7916.     ADD DI,LINESIZE-4
  7917.  
  7918.     MOVSW       {7.row  }
  7919.     MOVSW
  7920.     ADD DI,LINESIZE-4
  7921.  
  7922.     MOVSW       {8.row  }
  7923.     MOVSW
  7924.     ADD DI,LINESIZE-4
  7925.  
  7926.     MOVSW       {9.row  }
  7927.     MOVSW
  7928.     ADD DI,LINESIZE-4
  7929.  
  7930.     MOVSW      {10.row  }
  7931.     MOVSW
  7932.     ADD DI,LINESIZE-4
  7933.  
  7934.     MOVSW      {11.row  }
  7935.     MOVSW
  7936.     ADD DI,LINESIZE-4
  7937.  
  7938.     MOVSW      {12.row  }
  7939.     MOVSW
  7940.     ADD DI,LINESIZE-4
  7941.  
  7942.     MOVSW      {13.row  }
  7943.     MOVSW
  7944.     ADD DI,LINESIZE-4
  7945.  
  7946.     MOVSW      {14.row  }
  7947.     MOVSW
  7948.     ADD DI,LINESIZE-4
  7949.  
  7950.     MOVSW      {15.row  }
  7951.     MOVSW
  7952.     ADD DI,LINESIZE-4
  7953.  
  7954.     MOVSW      {16.row  }
  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.row  }
  7973.     MOVSW
  7974.     ADD DI,LINESIZE-4   {ES:DI = ^next tile     }
  7975.  
  7976.     MOVSW       {2.row  }
  7977.     MOVSW
  7978.     ADD DI,LINESIZE-4
  7979.  
  7980.     MOVSW       {3.row  }
  7981.     MOVSW
  7982.     ADD DI,LINESIZE-4
  7983.  
  7984.     MOVSW       {4.row  }
  7985.     MOVSW
  7986.     ADD DI,LINESIZE-4
  7987.  
  7988.     MOVSW       {5.row  }
  7989.     MOVSW
  7990.     ADD DI,LINESIZE-4
  7991.  
  7992.     MOVSW       {6.row  }
  7993.     MOVSW
  7994.     ADD DI,LINESIZE-4
  7995.  
  7996.     MOVSW       {7.row  }
  7997.     MOVSW
  7998.     ADD DI,LINESIZE-4
  7999.  
  8000.     MOVSW       {8.row  }
  8001.     MOVSW
  8002.     ADD DI,LINESIZE-4
  8003.  
  8004.     MOVSW       {9.row  }
  8005.     MOVSW
  8006.     ADD DI,LINESIZE-4
  8007.  
  8008.     MOVSW      {10.row  }
  8009.     MOVSW
  8010.     ADD DI,LINESIZE-4
  8011.  
  8012.     MOVSW      {11.row  }
  8013.     MOVSW
  8014.     ADD DI,LINESIZE-4
  8015.  
  8016.     MOVSW      {12.row  }
  8017.     MOVSW
  8018.     ADD DI,LINESIZE-4
  8019.  
  8020.     MOVSW      {13.row  }
  8021.     MOVSW
  8022.     ADD DI,LINESIZE-4
  8023.  
  8024.     MOVSW      {14.row  }
  8025.     MOVSW
  8026.     ADD DI,LINESIZE-4
  8027.  
  8028.     MOVSW      {15.row  }
  8029.     MOVSW
  8030.     ADD DI,LINESIZE-4
  8031.  
  8032.     MOVSW      {16.row  }
  8033.     MOVSW
  8034.  
  8035.  
  8036.     MOV AX,SEG @Data
  8037.     MOV DS,AX
  8038.  
  8039.     ADD DI,LINESIZE-4-1
  8040.  
  8041.   {next tile; DI already has the right value}
  8042.   MOV AX,XTiles     {set temp. actIndex to next row        }
  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 still has its old value, as only oldActIndex has been changed. }
  8052.   INC actIndex        {actIndex = next inner tile in uppermost tile row        }
  8053.   MOV AX,actIndex     {and use it as starting value for next column   }
  8054.   MOV oldActIndex,AX
  8055.  
  8056.   MOV DI,oldDI        {ES:DI = ^inner tile in uppermost tile row     }
  8057.   ADD DI,4            {increment by one tile   }
  8058.   MOV oldDI,DI        {and use it as starting value for next column   }
  8059.  
  8060.   MOV AX,tempYtil
  8061.   MOV ytil,AX     {set Y-coordinate back to the uppermost inner tile row    }
  8062.   { MOV AX,oldY }
  8063.   { MOV y,AX }
  8064.  
  8065.   INC xtil      {increment X-coordinate by one tile column  }
  8066.   { MOV AX,16 }
  8067.   { ADD x,AX }
  8068.  
  8069.   DEC counter
  8070.   JNZ @m0xloop
  8071.  
  8072.   MOV DI,tempDI       {Thus: ES:DI, actIndex, xtil, ytil, x, y point to      }
  8073.   MOV AX,tempActIndex {the first inner tile again (N.B.: y, ytil have        }
  8074.   MOV actIndex,AX     {restored some lines above, already!   }
  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     {hold a copy in DX   }
  8082.   ADD ytil,AX   {ytil points to the bottommost tile row   }
  8083.  
  8084.   MOV CL,5
  8085.   SHL AX,CL     {dto. for 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. for y: inc(y,16 * innerTilesY) }
  8090.  
  8091.   MOV AX,XTiles
  8092.   MUL DX          {AX := XTiles * innerTilesY}
  8093.   ADD actIndex,AX {dto. for actIndex: inc(actIndex,XTiles * innerTilesY) }
  8094.  
  8095.  @m0SkipInnerTiles:
  8096.   {ES:DI, actIndex, xtil, ytil, x, y point to the first inner tile of the}
  8097.   {bottommost tile row  }
  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 {are we standing at the right bottom corner tile? }
  8113.   MOV counter,AX
  8114.  
  8115.   {compute addition factor needed to position from bottom to top:}
  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, because DI has been inremented 1x (due to planing)}
  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 with WriteMode0: }
  8144.     { in: ES:DI = ^destination address}
  8145.     {     SI = tile index     }
  8146.     {     bottomcut, Win*, SCROLLADR,...}
  8147.     {out: ES:DI = ^destination address of the next tile to the right of it}
  8148.     {rem: WriteMode0 has been set already and remains set  }
  8149.     MOV AL,[OFFSET BackTile +SI]
  8150.     XOR AH,AH      {compute offset address of tile:    }
  8151.     MOV CL,6       {each tile is 64 bytes in size, thus}
  8152.     SHL AX,CL      {AX := tile * 64 = tile 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 of the next row below the tile, now position to the next    }
  8253.     {tile:         }
  8254.     {┌─┬─┬─┬─┐    ┌─┬─┬─┬─┐▄ }
  8255.     {├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤  }
  8256.     {└─┴─┴─┴─┘    └─┴─┴─┴─┘  }
  8257.     { ▀                      }
  8258.     ADD DI,Korrektur
  8259.  
  8260.   {position to next tile to the right of it:     }
  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 point to the lower right corner tile}
  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 with WriteMode0: }
  8288.     { in: ES:DI = ^destination address}
  8289.     {     SI = tile index     }
  8290.     {     StartWritePlane = first bitplane to write to     }
  8291.     {     rightcut MOD 4 = 0  }
  8292.     {     rightcut, bottomcut, Win*, SCROLLADR,...}
  8293.     {out: ES = ^graphic segment }
  8294.     {rem: WriteMode0 has been set already and remains set  }
  8295.     PUSH BP
  8296.     MOV AL,[OFFSET BackTile +SI]
  8297.     XOR AH,AH      {compute offset address of tile:    }
  8298.     MOV CL,6       {each tile is 64 bytes in size, thus}
  8299.     SHL AX,CL      {AX := tile * 64 = tile 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 per row }
  8325.     MOV BL,CL  {BL = row counter  }
  8326.     MOV BH,CL  {copy to 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. point to lower right corner tile  }
  8451.  
  8452.  @m0SkipLowerRightCorner:
  8453.   CMP leftcut,0
  8454.   JE @m0fertig
  8455.  
  8456.   {now position on lower left corner tile:        }
  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 {don't forget: corner tile may be cut at its left side}
  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 with WriteMode0: }
  8482.     { in: ES:DI = ^destination address}
  8483.     {     SI = tile index     }
  8484.     {     StartLesePlane = first bitplane to read from         }
  8485.     {     rightcut MOD 4 = 0  }
  8486.     {     leftcut, bottomcut, Win*, SCROLLADR,...}
  8487.     {out: (ES = ^graphic segment) }
  8488.     {rem: WriteMode0 has been set (and remains set)          }
  8489.     PUSH BP
  8490.     MOV AL,[OFFSET BackTile +SI]
  8491.     XOR AH,AH      {compute offset address of tile:    }
  8492.     MOV CL,6       {each tile is 64 bytes in size, thus}
  8493.     SHL AX,CL      {AX := tile * 64 = tile 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=row counter }
  8527.     MOV BH,CL   {copy of it }
  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.   {------- starting here: put sprites on actual graphic page}
  8649.  
  8650.   @Sprites_zeichnen:
  8651.     MOV SI,NMAX*2
  8652.     PUSH BP      {pop BP in the end!       }
  8653.  
  8654.  
  8655.     @zeichne:
  8656.      CMP SI,SplitIndex_mal2  {split index?}
  8657.      JNE @SZeich
  8658.  
  8659.      MOV AX,WinXMIN
  8660.      {yes: set Win* variables to the whole area (0,0)..(XMAX,YMAX):}
  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 = normal data segment, ES = graphic page segment,  }
  8686.     {SI = sprite position number * 2                        }
  8687.     @SZeich:
  8688.       MOV BX,[SI + OFFSET SpriteN]   {BX = SpriteN[?] = sprite load number}
  8689.       SHL BX,1                       {BX = sprite load number * 2}
  8690.  
  8691.     {now: compute "SpriteN[?] := SpriteN[NextSprite[?]]"     }
  8692.       MOV BX,[BX + OFFSET NextSprite] {AX = NextSprite[SpriteN[?]]}
  8693.       MOV [SI + OFFSET SpriteN],BX    {use it as new SpriteN[?] value}
  8694.       SHL BX,1
  8695.  
  8696.  
  8697.       JNZ @aktiv
  8698.       JMP @noSprite
  8699.  
  8700.  
  8701.     @aktiv:
  8702.       PUSH SI           {save sprite position number * 2 }
  8703.  
  8704.       MOV DX,[SI + OFFSET SpriteX]   {if SpriteX > xmax then skip_sprite}
  8705.       SUB DX,StartVirtualX           {virtual -> absolute coordinates   }
  8706.       MOV AX,WinXMAX
  8707.       CMP DX,AX
  8708.       JLE @L0
  8709.     @ToSprite_fertig:                {jump-rail to @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_virtual }
  8714.       SUB DI,StartVirtualY           {DI = SpriteY (absolute!)}
  8715.  
  8716.       PUSH AX                        {WinXMAX}
  8717.       MOV AX,WinYMIN
  8718.       MOV SI,WinXMIN
  8719.       MOV CX,WinYMAX                 {save old Win* values...  }
  8720.  
  8721.       MOV DS,[BX + OFFSET SPRITEAD]  {!!!DS = ^sprite data !!!}
  8722.  
  8723.       MOV [WinYMIN_],AX              {...and store them in new DS segment}
  8724.       MOV [WinXMIN_],SI
  8725.       POP AX
  8726.       MOV [WinXMAX_],AX              {hold WinYMAX in CX  }
  8727.  
  8728.       MOV AX,[Breite]                {AX = width in groups of 4 }
  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_width_in_points  }
  8733.       ADD AX,DX                      {AX = max_width_in_points+SpriteX  }
  8734.       CMP AX,[WinXMIN_]   {is the right end to the left of the window area?}
  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 = height (in rows)   }
  8744.       CMP DI,AX                      {if starty >= height then skip_sprite}
  8745.       JGE @ToSprite_fertig
  8746.       ADD BP,CX                      {BP = -(SpriteY - WinYMIN) + WinYMAX}
  8747.       SUB BP,[WinYMIN_]
  8748.       JL @ToSprite_fertig            {(a bit lax:)  }
  8749.       CMP AX,BP                      {if height + 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 := height - 1 }
  8753.  
  8754.     {BP = endy, SI=[@max_Breite+1] = max_width_in_groups of 4,  }
  8755.     {DI = starty, BX = SpriteY, DX=[@akt_SpriteX+1] = SpriteX,  }
  8756.     {DS = ^sprite data, ES = ^graphic page}
  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 =Yactual * 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             {store it in [zeilenadr], too }
  8767.       MOV BP,DX
  8768.       MUL SI                         {AX = endy * max_width_in_groups_of_4 = yoffset}
  8769.       MOV [yoffset_],AX              {store it in [yoffset_], too}
  8770.       SHL DI,1                       {DI = starty * 2}
  8771.       MOV CS:WORD PTR @Starty_2+1,DI {store in [@Starty_2 + 1], too }
  8772.  
  8773.       {Now: look at sprite's mode byte and determine if an other than the }
  8774.       {momentary active sprite display method is needed; if so, then copy }
  8775.       {in the right one!                                                  }
  8776.       {used registers: AX and SI                                          }
  8777.        MOV AL,[Modus]                {get mode byte of the sprite}
  8778.        XOR AH,AH
  8779.        SHL AX,1
  8780.        MOV SI,AX
  8781.        MOV SI,CS:[OFFSET Adressen +SI] {get pointer to according routine    }
  8782.        MOV AX,CS:[SI]
  8783.        CMP AX,CS:[WORD PTR @Patch1]    {is this routine already active? }
  8784.        JE @no_newcode                  {yes, nothing to do}
  8785.        PUSH DS                         {no, copy routine to all places  }
  8786.        PUSH CS                         {where it will be needed}
  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                          {restore DS         }
  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_width_in_groups_of_4) }
  8839.     { BP = [@akt_SpriteX + 1]= SpriteX}
  8840.     { DS                     = ^sprite data}
  8841.     { ES                     = ^graphic page}
  8842.     { [end_min_start]        = (endy - starty) * 2 = Yactual * 2 }
  8843.     { [@max_Breite + 1]      = max_width_in_groups_of_4   }
  8844.     @eine_Zeile:
  8845.       MOV SI,[end_min_start]         {SI = Yactual * 2  }
  8846.       ADD SI,DI                      {startx := sprite[WORD PTR sprite[L] +  }
  8847.       MOV DI,SI                      {              (Yactual + starty) * 2]  }
  8848.       ADD SI,[Left]
  8849.       MOV SI,[SI]                    {SI = startx, DI = (Yactual + 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 = ScreenStartX := SpriteX + startx     }
  8854.       CMP AX,[WinXMAX_]       {if ScreenStartX > WinXMAX then skip_zeile    }
  8855.       JG @ToZeile_fertig
  8856.       MOV CX,SI                      {CX = startx}
  8857.       SUB AX,[WinXMIN_]              {lecutoff_in_points := startx }
  8858.       JGE @L1                        {if ScreenStartX < WinXMIN then     }
  8859.       SUB SI,AX                      { [dec(startx,ScreenStartX - WinXMIN)     }
  8860.       XOR AX,AX                      {  ScreenStartX := WinXMIN          }
  8861.       MOV CX,BP                      {  lecutoff_in_points := -SpriteX]  }
  8862.       NEG CX
  8863.     @L1:
  8864.       ADD AX,[WinXMIN_]              {CX = [licutoff_] = lecutoff_in_points,  }
  8865.       MOV [licutoff_],CX             {SI = startx, AX = ScreenStartX       }
  8866.       ADD DI,[Right]
  8867.       MOV DI,[DI]                    {DI = endx := sprite[WORD PTR sprite[R] +  }
  8868.                                      {                (Yactual + 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 = overhang := WinXMAX - (SpriteX + endx) }
  8874.       JNS @kein_Ueberhang_rechts
  8875.       ADD BP,DX
  8876.     @kein_Ueberhang_rechts:          {BP = visible width of this row -1    }
  8877.       OR BP,BP
  8878.       JNS @L6
  8879.     @ToZeile_fertig:
  8880.       JMP @Zeile_fertig              {if width <= 0 then skip_zeile  }
  8881.     @L6:
  8882.       ADD BP,4
  8883.  
  8884.       { AX                 = ScreenStartX    }
  8885.       { BX = [zeilenadr]   = (endy + SpriteY) * LINESIZE }
  8886.       { CX = [licutoff_]   = lecutoff_in_points }
  8887.       {(DX                 = (negative) overhang (if value < 0) )    }
  8888.       {(SI                 = startx) }
  8889.       {(DI                 = endx) }
  8890.       { BP                 = width of this row in pixels + 3       }
  8891.       { DS                 = ^sprite data}
  8892.       { ES                 = ^graphic page}
  8893.       { [@max_Breite + 1]  = max_width_in_groups_of_4) }
  8894.       { [end_min_start]    = (endy - starty) * 2 =Yactual * 2 }
  8895.       { [@Starty_2 + 1]    = starty * 2}
  8896.       { [@max_Breite + 1]  = max_width_in_groups_of_4,  }
  8897.       { [@akt_SpriteX + 1] = SpriteX}
  8898.       MOV [bildx],AX                 {save ScreenStartX      }
  8899.       MOV DX,CX                      {DX = lecutoff_in_points }
  8900.       MOV CX,BP
  8901.       SHR CX,1
  8902.       SHR CX,1                       {CX = width DIV 4 }
  8903.       JCXZ @Plane1
  8904.  
  8905.       {SI = source pointer := sprite[WORD PTR (lecutoff_in_points + 0 AND 3) * 2}
  8906.       {                       + (lecutoff_in_points + 0) DIV 4 + yoffset   }
  8907.       MOV SI,DX
  8908.       AND SI,3                       
  8909.       SHL SI,1                  {SI = ((lecutoff_in_points + 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.                                      {     + (lecutoff_in_points + i) DIV 4  }
  8917.                                      {     + yoffset                         }
  8918.  
  8919.       {DI = destination pointer := (ScreenStartX + 0) DIV 4 + zeilenadr}
  8920.       MOV DI,AX                      {DI = ScreenStartX     }
  8921.       SHR DI,1
  8922.       SHR DI,1
  8923.       ADD DI,BX
  8924.       MOV BL,AL
  8925.       AND BX,3                       {BX = (ScreenStartX + i) AND 3     }
  8926.       MOV AH,Translate[BX]           {AH = 1,2,4,8 for BX = 0,1,2,3     }
  8927.       MOV AL,2
  8928.       MOV DX,3C4h
  8929.       OUT DX,AX                      {choose plane   }
  8930.  
  8931.       XCHG BX,DI
  8932.       {copy CX bytes from DS:SI to ES:BX        }
  8933.       {this is the address where the data transfer routine will be patched!}
  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 = ScreenStartX+1    }
  8940.       MOV BX,DX
  8941.       SHR BX,1
  8942.       SHR BX,1                    {BX = destination pointer := (ScreenStartX + 1) }
  8943.       ADD BX,[zeilenadr]          {                DIV 4 + zeilenadr    }
  8944.       MOV CX,BP
  8945.       DEC CX                      {CX = width of this line + 3 - 1  }
  8946.       SHR CX,1
  8947.       SHR CX,1                    {CX = bytes_to_move for i = 1  }
  8948.       JCXZ @Plane2
  8949.       MOV DI,[licutoff_]
  8950.       INC DI                      {DI = (lecutoff_in_points + 1)  }
  8951.       MOV SI,DI
  8952.       AND SI,3
  8953.       SHL SI,1                    {SI = ((lecutoff_in_points + 1) AND 3) * 2 }
  8954.       MOV SI,[SI]                 {SI = sprite[WORD PTR licutoff_...]     }
  8955.       SHR DI,1                    {     + (lecutoff_in_points + 1) DIV 4  }
  8956.       SHR DI,1                    {     + yoffset                         }
  8957.       ADD SI,DI
  8958.       ADD SI,[yoffset_]           {SI = source pointer, }
  8959.                                   {DI = (lecutoff_in_points + 1) DIV 4  }
  8960.  
  8961.       MOV DI,DX                   {DI = ScreenStartX + 1    }
  8962.       AND DI,3                    {DI = (ScreenStartX + 1) AND 3     }
  8963.       MOV AH,Translate[DI]        {load mask for port-access  }
  8964.       MOV AL,2
  8965.       MOV DX,3C4h                 {select plane  }
  8966.       OUT DX,AX
  8967.  
  8968.       {this is the address where the data transfer routine will be patched!}
  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 = ScreenStartX + 2    }
  8996.       AND DI,3                       {DI = (ScreenStartX + 1) AND 3     }
  8997.       MOV AH,Translate[DI]
  8998.       MOV AL,2
  8999.       MOV DX,3C4h
  9000.       OUT DX,AX
  9001.  
  9002.       {this is the address where the data transfer routine will be patched!}
  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 = ScreenStartX + 3    }
  9030.       AND DI,3                       {DI = (ScreenStartX + 1) AND 3     }
  9031.       MOV AH,Translate[DI]
  9032.       MOV AL,2
  9033.       MOV DX,3C4h
  9034.       OUT DX,AX
  9035.  
  9036.       {this is the address where the data transfer routine will be patched!}
  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.       {reset Win* variables to their old values --if necessary:   }
  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.                                  {prepare "CX := Offset_Adr[Page]":    }
  9106.       MOV SI,PAGE                {page value *2 (word-sized entries!)}
  9107.       MOV BX,SI                  {save page value in BX!}
  9108.       SHL SI,1                   {add start address of array to that   }
  9109.       ADD SI,OFFSET Offset_Adr-StartIndex*2  {evtl. correct displacement    }
  9110.       LODSW                      {realize "AX := Offset_Adr[Page]"   }
  9111.       MOV CX,AX                  {bring value to CX}
  9112.       MOV DI,CRTAddress          {DI := CRTAddress }
  9113.  
  9114.     {The graphic page is now ready to be displayed:                       }
  9115.       cli
  9116.       mov  dx,StatusReg
  9117.  
  9118.     {Wait for "display enable"=0 (that is: active), so that page flipping }
  9119.     {for HB/LB takes place while displaying the same page:}
  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-startaddress-register}
  9133.       OUT DX,AL
  9134.       INC DX
  9135.                                  
  9136.       MOV AX,CX                  {AX := Offset_Adr[Page]}
  9137.       OUT DX,AL                  {set LB of new starting address  }
  9138.       DEC DX
  9139.       MOV AL,$0C
  9140.       OUT DX,AL
  9141.       INC DX
  9142.       MOV AL,AH                  {set HB of new starting address  }
  9143.       OUT DX,AL
  9144.       STI
  9145.  
  9146.       NEG BX       {new PAGE-value := 1-old PAGE-value, that is:  }
  9147.       ADD BX,1     {IF PAGE = 0 THEN PAGE := 1 ELSE (PAGE = 1) PAGE := 0 }
  9148.       MOV PAGE,BX
  9149.  
  9150.       SHL BX,1     {new PAGEADR-value := Segment_Adr[PAGE]  }
  9151.       ADD BX,OFFSET Segment_Adr-StartIndex*2
  9152.       MOV AX,[BX]
  9153.       MOV PAGEADR,AX
  9154.  
  9155.       {Check whether the preset (min.) cycle time has passed:           }
  9156.     @L10:
  9157.       MOV AL,TimeFlag   {bit 7 = 0/1 for delay completed/not yet completed }
  9158.       AND AL,$80
  9159.       JE @L10
  9160.  
  9161.       {start time control mechanism for next cycle:}
  9162.       MOV AL,IsAT                 {is this an AT/386? ($0/$80 = yes/no)    }
  9163.       OR AL,AL                    {timing mechanism only works on AT/386     }
  9164.       JNE @L11                    {otherwise: no timing mechanism!         }
  9165.       MOV TimeFlag,AL             {AL = 0 use this as initial value, too   }
  9166.       MOV DX,WORD PTR CycleTime   {store min. time for 1 cycle (micro-     }
  9167.       MOV CX,WORD PTR CycleTime+2 {seconds): CX = HIGH-word, DX = LOW-word }
  9168.       MOV BX,OFFSET TimeFlag      {ES:BX = pointer to TimeFlag, bit 7 = 0/1}
  9169.       MOV AX,DS                   {for: delay still lasts/has ended             }
  9170.       MOV ES,AX
  9171.       MOV AX,8300h                {start time control mechanism}
  9172.       INT 15h
  9173.     @L11:
  9174.  END; {of ASM}
  9175. END; {of ANIMATE}
  9176.  
  9177. PROCEDURE FreeSpriteMem(number:WORD);
  9178. { in: number = sprite load number of the sprite to deallocate     }
  9179. {out: allocated memory has been released, SPRITEAD[number] has been set to 0 and}
  9180. {     SPRITEPTR[number] has been set to NIL}
  9181. {rem: sprite numbers referencing to the released sprite contents   }
  9182. {     are not allowed after this routine, of course!}
  9183. {     If no sprite "number" had been loaded, nothing at all will happen}
  9184. {     evtl. sprite cycles must be freed by calling this routine for each}
  9185. {     of its members once.     }
  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 of the sprite file to load (type: "*.COD" / "*.LIB")  }
  9200. {     number = number for the first sprite of this file                   }
  9201. {out: number of the sprites read in from the file (0 = error occured)     }
  9202. {The routine detects automatically, if the file holfds a single           }
  9203. {     tains a single sprite or a complete sprite library and loads all     }
  9204. {     sprite data onto the heap, in a way such that the address always     }
  9205. {     lies on a segment (=paragraph) boundary. These starting addresses    }
  9206. {     are then stored in table SPRITEAD[number]; if more than one sprite  }
  9207. {     has been loaded, they will be stored with consecutive numbers, that   }
  9208. {     is, 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;  {number of sprites already read in   }
  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  {File doesn't exist or at least not with that path }
  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.   {first, read in the sprite header:   }
  9237.   {$I-}     {load sprite header vià BLOCKREAD into the heap         }
  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.   {enough space left?  }
  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. release old memory      }
  9260.  
  9261.   {Now read in the real sprite data:       }
  9262.   getmem(p1,Header.SpriteLength+15);       {get enough space       }
  9263.   SPRITESIZE[number+count]:=Header.SpriteLength+15;
  9264.   SPRITEPTR [number+count]:=p1;
  9265.   IF (LONGINT(p1) mod 16)=0
  9266.    THEN p2:=p1                             {make p2 fall on segment boundary}
  9267.    ELSE LONGINT(p2):=LONGINT(p1) + (16-LONGINT(p1) mod 16);
  9268.  
  9269.   MOVE(Header,p2^,Kopf);  {store sprite header to heap  }
  9270.   LONGINT(p1):=LONGINT(p2)+Kopf;   {points exactly behind the header}
  9271.  
  9272.   {$I-}     {load the "rest" of the sprite     }
  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.   {assign it to the sprite number:}
  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 of the sprite file to load (type: "*.COD" / "*.LIB")  }
  9299. {     number = 0..255 = tile number for the file's first sprite           }
  9300. {out: number of the tiles read in from the file (0 = error occured)       }
  9301. {The routine detects automatically, if the file holfds a single           }
  9302. {     sprite or a complete sprite library. Always all sprites in the file  }
  9303. {     sprites, splits them into tiles and stores them into the 4th graphic  }
  9304. {     page, starting with the given number "number"                       }
  9305. {     Because a tile consists of 16x16 points, the sprites must be a mul-  }
  9306. {     tiple of 16 points in each direction (x _and_ y)                     }
  9307. {     If the file contains more than one tile, they will be loaded by row, }
  9308. {     each row from left to right                                         }
  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;  {number of sprites already read in   }
  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  {File doesn't exist or at least not with that path }
  9327.         Error:=Err_FileIO;
  9328.         CompressError:=CompressErr_NoError;
  9329.         LoadTile:=0; exit
  9330.        END;
  9331.  WHILE NOT _physicalEOF(f) DO
  9332.  BEGIN
  9333.   {first, read in the sprite header:   }
  9334.   {$I-}     {load sprite header vià BLOCKREAD into the heap         }
  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;  {or 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)   {size a multiple of 16?  }
  9351.    THEN BEGIN
  9352.          Error:=Err_NoTile;
  9353.          goto quit_loop
  9354.         END
  9355.    ELSE BEGIN {yes, get number of tiles in that sprite file   }
  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; {"step" needed for correct addressing}
  9359.         END;
  9360.   IF (Header.SpriteLength>MaxAvail)    {enough space left?  }
  9361.    THEN BEGIN
  9362.          Error:=Err_NotEnoughMemory;
  9363.          goto quit_loop;
  9364.         END;
  9365.  
  9366.   {Now read in the real sprite data:       }
  9367.   getmem(p1,Header.SpriteLength);      {get enough space       }
  9368.  
  9369.   {$I-}     {load the "rest" of the sprite     }
  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);      {release memory    }
  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) = upper left corner of the area (in virtual coord.)}
  9423. {     (x2,y2) = dto., lower right corner}
  9424. {out: (BackX1,BackY1), (BackX2,BackY2) = coord., rounded to 16'grid        }
  9425. {     XTiles, YTiles = width and height of the chosen range in tiles       }
  9426. {rem: The upper left corner will be torn to the upper left, the lower      }
  9427. {     right corner to the lower right!}
  9428. {     Obviously, calling the routine only makes sense when using SCROLLING  }
  9429. {     as background mode!     }
  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 = wanted background mode, STATIC or SCROLLING       }
  9443. {out: Backgroundmode = set mode, 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 = starting index of first tile  }
  9452. {     TileWidth = width (in tiles) of the rectangular pattern to be repeated}
  9453. {     TileHeight= dto., height}
  9454. {     BackX1,BackY1,BackX2,BackY2= background area set by calling       }
  9455. {      SetScrollRange()  }
  9456. {out: Starting with tile no. FirstTile, the next TileWidth*TileHeight }
  9457. {     tiles have been used as a repeating rectangular pattern of     }
  9458. {     width TileWidth and height TileHeight into the background.     }
  9459. {     This way, the complete defined background area has been built  }
  9460. {     For example, MakeTileArea(7,3,2) will result in the following    }
  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: Calling this routine only makes sense, when SCROLLING has been}
  9474. {     set as background mode and SetScrollRange() has been called    }
  9475. {     previously!}
  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-coordinate for this row   }
  9484.            {compute index of 1st tile of the actual row:   }
  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   = virtual coordinates where the tile shall be placed          }
  9496. {     TileNr= number of the tile to be placed  }
  9497. {out: - }
  9498. {rem: The point (x,y) first gets rounded to a grid with mesh 16  }
  9499. {     Calling this routine only makes sense when using SCROLLING}
  9500. {     as background mode!     }
  9501. VAR index:WORD;
  9502. BEGIN
  9503.  ASM
  9504.     MOV AX,x        {compute relative X-distance from left edge of   }
  9505.     SUB AX,BackX1   {the defined area and store it to "x", formula:  }
  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 last hex-digit is $0!                    }
  9509.     SAR AX,1
  9510.     MOV x,AX
  9511.  
  9512.     MOV AX,y        {dto. for distance between the y-coordinate and }
  9513.     SUB AX,BackY1   {the upper edge: 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 {a tile row has width XTiles, each tile consists of 16x16 points}
  9524.         index:=y*XTiles+x;  {to be exact: (x MOD XTiles)}
  9525.         BackTile[Succ(index)]:=TileNr;  {"succ", to hold free BackTile[0]   }
  9526.        END;
  9527. END;
  9528.  
  9529. FUNCTION GetTile(x,y:INTEGER):BYTE;
  9530. { in: x,y   = virtual coordinate for which the corresp. tile should be computed}
  9531. {out: no. of the tile at this position  }
  9532. {rem: The point (x,y) first gets rounded to a grid with mesh 16}
  9533. {     If the point lies outside the defined scroll area, then the     }
  9534. {     routine returns the value BackTile[0] of the offscreen tile}
  9535. BEGIN
  9536.  ASM
  9537.     MOV AX,x        {compute relative X-distance from left edge of   }
  9538.     SUB AX,BackX1   {the defined area and store it to "x", formula:  }
  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 last hex-digit is $0!                    }
  9542.     SAR AX,1
  9543.     MOV x,AX
  9544.  
  9545.     MOV AX,y        {dto. for distance between the y-coordinate and }
  9546.     SUB AX,BackY1   {the upper edge: 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= number of the tile to be placed  }
  9561. {out: - }
  9562. {rem: all screen parts, which lie outside the window specified by }
  9563. {     SetBackgroundScrollRange will become the tile TileNr as      }
  9564. {     pattern           }
  9565. {     Calling this routine only makes sense when using SCROLLING    }
  9566. {     as background mode!     }
  9567. BEGIN
  9568.  BackTile[0]:=TileNr
  9569. END;
  9570.  
  9571. PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
  9572. { in: Sp = spriteLOADnumber, which mode byte shall be changed      }
  9573. {out: M  = method, which shall be used to display Sp from now on:   }
  9574. {          Display_NORMAL, Display_FAST, Display_SHADOW or          }
  9575. {          Display_SHADOWEXACT                                      }
  9576. {rem: If the sprite doesn't exist yet (or the mode isn't allowed),  }
  9577. {     nothing will happen at all                                    }
  9578. VAR ad:WORD;
  9579. BEGIN
  9580.  ad:=SPRITEAD[Sp];
  9581.  IF ad=0 THEN Error:=Err_InvalidSpriteNumber {sprite must already be loaded}
  9582.  ELSE IF (M<Display_NORMAL) OR (M>Display_SHADOWEXACT)
  9583.   THEN Error:=Err_InvalidMode  {only these 4 modes are allowed}
  9584.  ELSE MEM[ad:Modus]:=M
  9585. END;
  9586.  
  9587. FUNCTION GetModeByte(Sp:WORD):BYTE;
  9588. { in: Sp = spriteLOADnumber, which mode byte shall be determined   }
  9589. {out: actually set display method for sprite Sp: Display_NORMAL,    }
  9590. {     Display_FAST, Display_SHADOW, Display_SHADOWEXACT or         }
  9591. {     Display_UNKNOW, if the sprite hasn't been loaded, yet!       }
  9592. VAR ad:WORD;
  9593. BEGIN
  9594.  ad:=SPRITEAD[Sp];
  9595.  IF (ad=0)
  9596.   THEN GetModeByte:=Display_UNKNOWN     {sprite not yet loaded    }
  9597.   ELSE GetModeByte:=MEM[SPRITEAD[Sp]:Modus]
  9598. END;
  9599.  
  9600. PROCEDURE FillBackground(color:BYTE);
  9601. { in: color = color for filling the background page BACKGNDPAGE         }
  9602. {     BACKGNDADR = pointer to background memory  }
  9603. {out: The graphic page BACKGNDPAGE has been filled with color "Color"}
  9604. {rem: Using the routine only makes sense when using background mode STATIC   }
  9605. BEGIN
  9606.  IF EMSused
  9607.   THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access         }
  9608.  FillChar(MEM[BACKGNDADR:0],4*PAGESIZE,color);
  9609. END;
  9610.  
  9611. PROCEDURE FillPage(pa:WORD; color:BYTE);
  9612. { in: pa    = the page which shall be filled (0..3)    }
  9613. {     color = color with which to fill the page}
  9614. {out: graphic page "pa" has been filled with color "Color"}
  9615. {rem: It only makes sense to use pages 0,1 and BACKGNDPAGE,   }
  9616. {     but SCROLLPAGE is allowed, too         }
  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; {use MapMask register to select all 4 planes   }
  9624.         fillchar(MEM[Segment_Adr[pa]:0],PAGESIZE,Color)
  9625.        END;
  9626. END;
  9627.  
  9628. PROCEDURE GetBackgroundFromPage(pa:WORD);
  9629. {in : pa = 0 or 1   }
  9630. {out: -             }
  9631. {The background memory BACKGNDPAGE becomes filles with the contents of the }
  9632. {     specified graphic page.         }
  9633. {     Using the routine only makes sense when using background mode STATIC}
  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); {prepare EMS access         }
  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  {plane to read from 0}
  9653.         MOV BX,PAGESIZE / 2
  9654.  
  9655.         {DS:SI = source pointer, ES:DI = destination pointer, BX = no. of words }
  9656.         {AX = access mask for read plane 0, DX = port address for it}
  9657.  
  9658.         CLI
  9659.         OUT DX,AX {select plane 0  }
  9660.         MOV CX,BX
  9661.         REP MOVSW {store plane data    }
  9662.         XOR SI,SI {reset SI     }
  9663.  
  9664.         INC AH    {select plane 1  }
  9665.         OUT DX,AX
  9666.         MOV CX,BX
  9667.         REP MOVSW
  9668.         XOR SI,SI
  9669.  
  9670.         INC AH    {select plane 2  }
  9671.         OUT DX,AX
  9672.         MOV CX,BX
  9673.         REP MOVSW
  9674.         XOR SI,SI
  9675.  
  9676.         INC AH    {select plane 3  }
  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     = file name for the picture to store   }
  9690. {     pa       = page to be saved (0..3)      }
  9691. {     PAGESIZE = size of one bitplane       }
  9692. {     PICHeader= tag for picture file                   }
  9693. {out: - }
  9694. {rem: The picture at page "pa" has been stored (as bitmap) to file "name"    }
  9695. {     This file has size 4*PAGESIZE+3 = 64003 bytes: 320x200 points ,        }
  9696. {     1 byte plus length(PICHeader) as tag     }
  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 to disk  }
  9719.         port[$3ce]:=5;       {save old read-/write mode      }
  9720.         oldMode:=port[$3cf];
  9721.         port[$3cf]:=$40;     {set read mode 0   }
  9722.         FOR i:=0 TO 3 DO
  9723.          BEGIN
  9724.           portw[$3CE]:=4+(i shl 8); {select read plane  }
  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;       {restore old read-/write mode   }
  9731.          port[$3cf]:=oldMode;
  9732.        END
  9733.   ELSE BEGIN {RAM to disk  }
  9734.         IF EMSused
  9735.          THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access         }
  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     = file name for the picture to load}
  9753. {     pa       = destination page to which the image shall be loaded (0..3) }
  9754. {     PAGESIZE = size of one bitplane    }
  9755. {     PICHeader= tag for picture files     }
  9756. {out: - }
  9757. {rem: The bitmap-picture in file "name" has been loaded into page "pa"     }
  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 to VRAM  }
  9797.         ASM cli END;
  9798.         port[$3ce]:=5;       {save old read-/write mode      }
  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); {which write plane   }
  9809.           p1:=@temp^[1];
  9810.           p2:=ptr(Segment_Adr[pa],0);
  9811.           ASM
  9812.            cli
  9813.            mov dx,$3CE      {select write mode 0  }
  9814.            mov ax,$4005
  9815.            out dx,ax
  9816.  
  9817.            mov ax,splane    {select write plane   }
  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; {restore old read-/write mode   }
  9833.         Dispose(temp);
  9834.        END
  9835.   ELSE BEGIN {disk to RAM  }
  9836.         IF EMSused
  9837.          THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access         }
  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       = file name for the background picture to store   }
  9855. {     BACKGNDPAGE= page to be saved (=2)      }
  9856. {     PAGESIZE   = size of one bitplane     }
  9857. {     PICHeader  = tag for picture file                   }
  9858. {out: - }
  9859. {rem: The picture of the background page has been stored as file "name" }
  9860. {     This file has size 4*PAGESIZE+3 = 64003 bytes: 320x200 points     }
  9861. {     at 1 byte each, plus length(PICHeader) as tag  }
  9862. {     Using the routine only makes sense when using background mode STATIC      }
  9863. BEGIN
  9864.  WritePage(name,BACKGNDPAGE)
  9865. END;
  9866.  
  9867. PROCEDURE LoadBackgroundPage(name:STRING);
  9868. { in: name       = file name for the background picture to load}
  9869. {     BACKGNDPAGE= destination page, in which to load the picture (=2) }
  9870. {     PAGESIZE = size of one bitplane    }
  9871. {     PICHeader= tag for picture files     }
  9872. {out: - }
  9873. {rem: The bitmap-picture contained in file "name" has been loaded to the }
  9874. {     background page BACKGNDPAGE}
  9875. {     Using the routine only makes sense when using background mode STATIC}
  9876. BEGIN
  9877.  LoadPage(name,BACKGNDPAGE)
  9878. END;
  9879.  
  9880. PROCEDURE FadeIn(pa,ti,style:WORD);
  9881. { in: pa = page which shall be faded onto the actually visible page  }
  9882. {     ti = time in milliseconds for this action             }
  9883. {     style = algorithm which shall be used            }
  9884. {     1-PAGE = actually visible page  }
  9885. {out: Error = Err_InvalidFade, if illegal "style" value has been used   }
  9886. {rem: graphic mode must have been initialized already            }
  9887. {     most often, you will use pa=BACKGNDPAGE   }
  9888.  
  9889.   PROCEDURE WipeIn(pa,time:WORD);
  9890.   { in: pa    = page, which contents will be made visible       }
  9891.   {     time  = time (in millisceconds) for this action (approx.)           }
  9892.   {     1-PAGE= (visible) graphic page on which to draw         }
  9893.   {out: - }
  9894.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  9895.   CONST hoehe =40; {divisor of Succ(YMAX)}
  9896.         breite=40; {divisor of Succ(XMAX)}
  9897.         br_x  =Succ(XMAX) DIV breite; {blocks in X-direction}
  9898.         br_y  =Succ(YMAX) DIV hoehe;  {blocks in Y-direction}
  9899.         n=hoehe*br_x; {number of executions of the delay loop }
  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    = page, which contents will be made visible       }
  9930.   {     time  = time (in millisceconds) for this action (approx.)           }
  9931.   {     m     = the way how this shall be done (1..14)}
  9932.   {     1-PAGE= (visible) graphic page on which to draw         }
  9933.   {out: - }
  9934.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  9935.   CONST n=Succ(XMAX)*Succ(YMAX);  {number of screen pixels}
  9936.         {e.g., good values are sums of powers of 2 +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 {compute: "x := rand MOD 320" and "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 {compute: 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    = page, which contents will be made visible       }
  9978.   {     time  = time (in millisceconds) for this action (approx.)           }
  9979.   {     m     = the way how this shall be done (1..1)}
  9980.   {     1-PAGE= (visible) graphic page on which to draw         }
  9981.   {out: - }
  9982.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  9983.   CONST n=Succ(XMAX)*Succ(YMAX);  {number of screen pixels}
  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 {compute: "x:=rand MOD 320" and "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 {compute: 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, thus correct it }
  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    = page, which contents will be made visible       }
  10031.   {     time  = time (in millisceconds) for this action (approx.)           }
  10032.   {     down  = TRUE/FALSE for: from bottom to top/vice versa   }
  10033.   {     1-PAGE= (visible) graphic page on which to draw         }
  10034.   {out: - }
  10035.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  10036.   CONST n=Succ(YMAX);      {number of executions of the delay loop  }
  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    = page, which contents will be made visible       }
  10073.   {     time  = time (in millisceconds) for this action (approx.)           }
  10074.   {     left_to_right=TRUE/FALSE for: from left to right/vice versa  }
  10075.   {     1-PAGE= (visible) graphic page on which to draw         }
  10076.   {out: - }
  10077.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  10078.   CONST n=Succ(XMAX);      {number of executions of the delay loop  }
  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    = page, which contents will be made visible       }
  10115.   {     time  = time (in millisceconds) for this action (approx.)           }
  10116.   {     up    = TRUE/FALSE for: from bottom to top/vice versa   }
  10117.   {     1-PAGE= (visible) graphic page on which to draw         }
  10118.   {out: - }
  10119.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  10120.   LABEL oneLine1,oneLine2,oneLine3,oneLine4;
  10121.   CONST n=Succ(YMAX);      {number of executions of the delay loop  }
  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 {scroll upwards    }
  10134.                  ASM
  10135.                    MOV DX,3C4h
  10136.                    MOV AX,0F02h  {access all 4 planes at once          }
  10137.                    OUT DX,AX
  10138.                    MOV DX,3CEh
  10139.                    MOV AX,4105h  {set write mode 1  }
  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] = ^visible page   }
  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] = ^source address}
  10155.  
  10156.                    PUSH DS
  10157.                    MOV DX,AX
  10158.                    MOV BX,YMAX*LINESIZE+(LINESIZE-1)  {DX:BX = ^source data}
  10159.  
  10160.                    MOV AX,YMAX   {AX = row counter }
  10161.  
  10162.                  oneLine2:
  10163.                    STD           {move backwards! }
  10164.                    MOV SI,ES     {first scroll old contents upwards:   }
  10165.                    MOV DS,SI     {DS = ES = visible graphic page }
  10166.                    MOV SI,(YMAX-1)*LINESIZE+(LINESIZE-1)  {from last but one graphic row}
  10167.                    MOV DI,YMAX*LINESIZE+(LINESIZE-1)      {to last graphic row     }
  10168.                    MOV CX,YMAX*LINESIZE  {199 rows  }
  10169.                    REP MOVSB
  10170.  
  10171.                    MOV DS,DX     {now make new row visible:        }
  10172.                    MOV SI,BX     {DS:SI = ^row to move     }
  10173.                    MOV CX,LINESIZE  {1 row  }
  10174.                    REP MOVSB
  10175.  
  10176.                    SUB BX,LINESIZE  {increase source pointer }
  10177.  
  10178.                    {--- insertion to realize timing:}
  10179.                    PUSH AX       {save all registers needed later     }
  10180.                    PUSH BX
  10181.                    PUSH DX
  10182.                    PUSH ES
  10183.                    MOV AX,SEG @DATA {restore TP's data segment         }
  10184.                    MOV DS,AX
  10185.                    CLD           {just to be sure!  }
  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.                    {--- end of insertion}
  10195.  
  10196.                    DEC AX        {all rows done?    }
  10197.                    JNS oneLine2  {no, next row       }
  10198.  
  10199.                    MOV DX,3CEh   {restore write mode 0        }
  10200.                    MOV AX,4005h
  10201.                    OUT DX,AX
  10202.                    POP DS
  10203.                  END;
  10204.                 END
  10205.            ELSE BEGIN {scroll downwards   }
  10206.                  ASM
  10207.                    MOV DX,3C4h
  10208.                    MOV AX,0F02h  {access all 4 planes at once          }
  10209.                    OUT DX,AX
  10210.                    MOV DX,3CEh
  10211.                    MOV AX,4105h  {set write mode 1  }
  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] = ^visible page   }
  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] = ^source address}
  10227.  
  10228.                    PUSH DS
  10229.                    MOV DX,AX
  10230.                    MOV BX,0*LINESIZE  {DX:BX = ^source data}
  10231.  
  10232.                    MOV AX,YMAX   {AX = row counter }
  10233.  
  10234.                  oneLine1:
  10235.                    MOV SI,ES     {first scroll old contents upwards:   }
  10236.                    MOV DS,SI     {DS = ES = visible graphic page }
  10237.                    MOV SI,1*LINESIZE  {from graphic row 1}
  10238.                    MOV DI,0*LINESIZE  {to graphic row 0  }
  10239.                    MOV CX,YMAX*LINESIZE  {199 rows  }
  10240.                    REP MOVSB
  10241.  
  10242.                    MOV DS,DX     {now make new row visible:        }
  10243.                    MOV SI,BX     {DS:SI = ^row to move     }
  10244.                    MOV CX,LINESIZE  {1 row  }
  10245.                    REP MOVSB
  10246.  
  10247.                    ADD BX,LINESIZE  {increase source pointer }
  10248.  
  10249.                    {--- insertion to realize timing:}
  10250.                    PUSH AX       {save all registers needed later     }
  10251.                    PUSH BX
  10252.                    PUSH DX
  10253.                    PUSH ES
  10254.                    MOV AX,SEG @DATA {restore TP's data segment         }
  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.                    {--- end of insertion}
  10265.  
  10266.                    DEC AX        {all rows done?    }
  10267.                    JNS oneLine1  {no, next row       }
  10268.  
  10269.                    MOV DX,3CEh   {restore write mode 0        }
  10270.                    MOV AX,4005h
  10271.                    OUT DX,AX
  10272.                    POP DS
  10273.                  END;
  10274.                 END;
  10275.          END
  10276.  
  10277.     ELSE BEGIN {pa = BACKGNDPAGE, thus copy from RAM to VRAM}
  10278.           IF EMSused
  10279.            THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access         }
  10280.           IF up
  10281.            THEN BEGIN {scroll upwards    }
  10282.                  ASM
  10283.                    MOV DX,3C4h
  10284.                    MOV AX,0F02h  {access all 4 planes at once          }
  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] = ^visible page   }
  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] = ^source address }
  10302.                                  {(for pa=BACKGNDPAGE id. to BACKGNADR)}
  10303.                    PUSH BP
  10304.  
  10305.                    MOV BP,AX
  10306.                    MOV BX,YMAX*LINESIZE+(LINESIZE-1)-1  {BP:BX = ^source data}
  10307.  
  10308.                    MOV AX,YMAX   {AX = row counter }
  10309.  
  10310.                  oneLine4:
  10311.                    MOV SI,AX
  10312.                    MOV DX,3CEh
  10313.                    MOV AX,4105h  {set write mode 1  }
  10314.                    OUT DX,AX
  10315.                    MOV AX,SI
  10316.  
  10317.                    STD           {move backwards! }
  10318.                    MOV SI,ES     {first scroll old contents upwards:   }
  10319.                    MOV DS,SI     {DS = ES = visible graphic page }
  10320.                    MOV SI,(YMAX-1)*LINESIZE+(LINESIZE-1)  {from last but one graphic row}
  10321.                    MOV DI,YMAX*LINESIZE+(LINESIZE-1)      {to last graphic row     }
  10322.                    MOV CX,YMAX*LINESIZE  {199 rows  }
  10323.                    REP MOVSB
  10324.  
  10325.                    PUSH AX
  10326.                    MOV DX,3CEh   {select write mode 0}
  10327.                    MOV AX,4005h
  10328.                    OUT DX,AX
  10329.  
  10330.                    MOV DX,3C4h
  10331.                    MOV AX,0102h  {select write plane 0    }
  10332.                    OUT DX,AX
  10333.                    MOV DS,BP     {now make new row visible:        }
  10334.                    MOV SI,BX     {DS:SI = ^row to move     }
  10335.                    DEC DI        {decrement DI by 1 (word accesses!)   }
  10336.                    MOV CX,LINESIZE / 2  {1 row  }
  10337.                    REP MOVSW
  10338.  
  10339.                    SHL AH,1      {select write plane 1    }
  10340.                    OUT DX,AX
  10341.                    ADD SI,PAGESIZE+LINESIZE
  10342.                    MOV CX,LINESIZE / 2 {1 row  }
  10343.                    ADD DI,LINESIZE
  10344.                    REP MOVSW
  10345.  
  10346.                    SHL AH,1      {select write plane 2    }
  10347.                    OUT DX,AX
  10348.                    ADD SI,PAGESIZE+LINESIZE
  10349.                    MOV CX,LINESIZE / 2 {1 row  }
  10350.                    ADD DI,LINESIZE
  10351.                    REP MOVSW
  10352.  
  10353.                    SHL AH,1      {select write plane 3    }
  10354.                    OUT DX,AX
  10355.                    ADD SI,PAGESIZE+LINESIZE
  10356.                    MOV CX,LINESIZE / 2 {1 row  }
  10357.                    ADD DI,LINESIZE
  10358.                    REP MOVSW
  10359.  
  10360.                    MOV AH,$F     {select all 4 planes      }
  10361.                    OUT DX,AX
  10362.  
  10363.                    SUB BX,LINESIZE  {increase source pointer }
  10364.                    POP AX
  10365.  
  10366.                    {--- insertion to realize timing:}
  10367.                    MOV SI,BP     {temporary BP }
  10368.                    POP BP        {TP's old BP    }
  10369.                    PUSH AX       {save all registers needed later     }
  10370.                    PUSH BX
  10371.                    PUSH SI
  10372.                    PUSH ES
  10373.                    MOV AX,SEG @DATA {restore TP's data segment         }
  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.                    {--- end of insertion}
  10386.  
  10387.                    DEC AX        {all rows done?    }
  10388.                    JNS oneLine4  {no, next row       }
  10389.  
  10390.                    POP BP
  10391.                    POP DS
  10392.                  END;
  10393.                 END
  10394.            ELSE BEGIN {scroll downwards   }
  10395.                  ASM
  10396.                    MOV DX,3C4h
  10397.                    MOV AX,0F02h  {access all 4 planes at once          }
  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] = ^visible page   }
  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] = ^source address }
  10415.                                  {(for pa=BACKGNDPAGE id. to BACKGNADR)}
  10416.                    PUSH BP
  10417.  
  10418.                    MOV BP,AX
  10419.  
  10420.                    MOV DX,AX
  10421.                    MOV BX,0*LINESIZE  {DX:BX = ^source data}
  10422.  
  10423.                    MOV AX,YMAX   {AX = row counter }
  10424.  
  10425.                  oneLine3:
  10426.                    MOV SI,AX
  10427.                    MOV DX,3CEh
  10428.                    MOV AX,4105h  {set write mode 1  }
  10429.                    OUT DX,AX
  10430.                    MOV AX,SI
  10431.  
  10432.                    MOV SI,ES     {first scroll old contents upwards:   }
  10433.                    MOV DS,SI     {DS = ES = visible graphic page }
  10434.                    MOV SI,1*LINESIZE  {from graphic row 1}
  10435.                    MOV DI,0*LINESIZE  {to graphic row 0  }
  10436.                    MOV CX,YMAX*LINESIZE  {199 rows  }
  10437.                    REP MOVSB
  10438.  
  10439.                    PUSH AX
  10440.                    MOV DX,3CEh   {select write mode 0}
  10441.                    MOV AX,4005h
  10442.                    OUT DX,AX
  10443.  
  10444.                    MOV DX,3C4h
  10445.                    MOV AX,0102h  {select write plane 0    }
  10446.                    OUT DX,AX
  10447.                    MOV DS,BP     {now make new row visible:        }
  10448.                    MOV SI,BX     {DS:SI = ^row to move     }
  10449.                    MOV CX,LINESIZE / 2 {1 row  }
  10450.                    REP MOVSW
  10451.  
  10452.                    SHL AH,1      {select write plane 1    }
  10453.                    OUT DX,AX
  10454.                    ADD SI,PAGESIZE-LINESIZE
  10455.                    MOV CX,LINESIZE / 2 {1 row  }
  10456.                    SUB DI,LINESIZE
  10457.                    REP MOVSW
  10458.  
  10459.                    SHL AH,1      {select write plane 2    }
  10460.                    OUT DX,AX
  10461.                    ADD SI,PAGESIZE-LINESIZE
  10462.                    MOV CX,LINESIZE / 2  {1 row  }
  10463.                    SUB DI,LINESIZE
  10464.                    REP MOVSW
  10465.  
  10466.                    SHL AH,1      {select write plane 3    }
  10467.                    OUT DX,AX
  10468.                    ADD SI,PAGESIZE-LINESIZE
  10469.                    MOV CX,LINESIZE / 2  {1 row  }
  10470.                    SUB DI,LINESIZE
  10471.                    REP MOVSW
  10472.  
  10473.                    MOV AH,$F     {select all 4 planes      }
  10474.                    OUT DX,AX
  10475.  
  10476.                    ADD BX,LINESIZE  {increase source pointer }
  10477.                    POP AX
  10478.  
  10479.                    {--- insertion to realize timing:}
  10480.                    MOV SI,BP     {temporary BP }
  10481.                    POP BP        {TP's old BP    }
  10482.                    PUSH AX       {save all registers needed later     }
  10483.                    PUSH BX
  10484.                    PUSH SI
  10485.                    PUSH ES
  10486.                    MOV AX,SEG @DATA {restore TP's data segment         }
  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.                    {--- end of insertion}
  10499.  
  10500.                    DEC AX        {all rows done?    }
  10501.                    JNS oneLine3  {no, next row       }
  10502.  
  10503.                    MOV DX,3CEh   {restore write mode 0        }
  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    = page, which contents will be made visible       }
  10516.   {     time  = time (in millisceconds) for this action (approx.)           }
  10517.   {     left  = TRUE/FALSE for: from left to right/vice versa   }
  10518.   {     1-PAGE= (visible) graphic page on which to draw         }
  10519.   {out: - }
  10520.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  10521.   LABEL oneColumn1,oneColumn2,oneColumn3,oneColumn4;
  10522.   CONST n=Pred(LINESIZE);      {number of executions of the delay loop }
  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 {scroll to the left }
  10535.                  ASM
  10536.                    MOV DX,3C4h
  10537.                    MOV AX,0F02h  {access all 4 planes at once          }
  10538.                    OUT DX,AX
  10539.                    MOV DX,3CEh
  10540.                    MOV AX,4105h  {set write mode 1  }
  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] = ^visible page   }
  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] = ^source address}
  10556.  
  10557.                    PUSH DS
  10558.                    MOV DX,AX
  10559.                    MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
  10560.  
  10561.                    MOV AX,LINESIZE-1   {AX = column counter}
  10562.  
  10563.                  oneColumn2:     {scroll old screen contents to the right:}
  10564.                    MOV SI,ES
  10565.                    MOV DS,SI     {DS = ES = visible graphic page }
  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's data}
  10575.                    MOV CX,YMAX+1 {CX = row counter }
  10576.  
  10577.                    MOV SI,AX
  10578.                    XOR DI,DI
  10579.                    MOV BX,LINESIZE-1
  10580.                    MOV DS,DX     {DS = ^source data}
  10581.  
  10582.                  @oneRow:        {update first column:  }
  10583.                    MOVSB
  10584.                    ADD SI,BX     {position at next row:           }
  10585.                    ADD DI,BX     {works, because BX + 1 = LINESIZE}
  10586.                    LOOP @oneRow
  10587.  
  10588.                    {--- insertion to realize timing:}
  10589.                    PUSH AX       {save all registers needed later     }
  10590.                    PUSH DX
  10591.                    PUSH ES
  10592.                    MOV AX,SEG @DATA {restore TP's data segment         }
  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.                    {--- end of insertion}
  10602.  
  10603.                    DEC AX        {all columns done?  }
  10604.                    JNS oneColumn2  {no, next column     }
  10605.  
  10606.                    MOV DX,3CEh   {restore write mode 0        }
  10607.                    MOV AX,4005h
  10608.                    OUT DX,AX
  10609.                    POP DS
  10610.                  END;
  10611.                 END
  10612.            ELSE BEGIN {scroll to the right }
  10613.                  ASM
  10614.                    MOV DX,3C4h
  10615.                    MOV AX,0F02h  {access all 4 planes at once          }
  10616.                    OUT DX,AX
  10617.                    MOV DX,3CEh
  10618.                    MOV AX,4105h  {set write mode 1  }
  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] = ^visible page   }
  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] = ^source address}
  10634.  
  10635.                    PUSH DS
  10636.                    MOV DX,AX
  10637.                    MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
  10638.  
  10639.                    MOV AX,0      {AX = column counter}
  10640.  
  10641.                  oneColumn1:     {scroll old screen contents to the left:}
  10642.                    MOV SI,ES
  10643.                    MOV DS,SI     {DS = ES = visible graphic page }
  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's data}
  10651.                    MOV CX,YMAX+1 {CX = row counter }
  10652.  
  10653.                    MOV SI,AX
  10654.                    MOV DI,LINESIZE-1
  10655.                    MOV BX,LINESIZE-1
  10656.                    MOV DS,DX     {DS = ^source data}
  10657.  
  10658.                  @oneRow:        {update last column:    }
  10659.                    MOVSB
  10660.                    ADD SI,BX     {position at next row:           }
  10661.                    ADD DI,BX     {works, because BX + 1 = LINESIZE}
  10662.                    LOOP @oneRow
  10663.  
  10664.                    {--- insertion to realize timing:}
  10665.                    PUSH AX       {save all registers needed later     }
  10666.                    PUSH DX
  10667.                    PUSH ES
  10668.                    MOV AX,SEG @DATA {restore TP's data segment         }
  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.                    {--- end of insertion}
  10678.  
  10679.                    INC AX        {all columns done?  }
  10680.                    CMP AX,LINESIZE
  10681.                    JB oneColumn1  {no, next column     }
  10682.  
  10683.                    MOV DX,3CEh   {restore write mode 0        }
  10684.                    MOV AX,4005h
  10685.                    OUT DX,AX
  10686.                    POP DS
  10687.                  END;
  10688.                 END;
  10689.          END
  10690.     ELSE BEGIN {pa = BACKGNDPAGE, thus copy from RAM to VRAM}
  10691.           IF EMSused
  10692.            THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access         }
  10693.           IF left
  10694.            THEN BEGIN {scroll to the left }
  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] = ^visible page   }
  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] = ^source address}
  10709.  
  10710.                    PUSH DS
  10711.                    MOV DX,AX
  10712.                    MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
  10713.                    MOV AX,LINESIZE-1 {column counter}
  10714.  
  10715.                  oneColumn4:     {scroll old screen contents to the right:}
  10716.                    MOV SI,DX
  10717.                    MOV DI,AX
  10718.                    MOV DX,3C4h
  10719.                    MOV AX,0F02h  {access all 4 planes at once          }
  10720.                    OUT DX,AX
  10721.                    MOV DX,3CEh
  10722.                    MOV AX,4105h  {set write mode 1  }
  10723.                    OUT DX,AX
  10724.                    MOV DX,SI
  10725.                    MOV AX,DI
  10726.  
  10727.                    MOV SI,ES
  10728.                    MOV DS,SI     {DS = ES = visible graphic page }
  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's data}
  10738.                    MOV CX,YMAX+1 {CX = row counter }
  10739.  
  10740.                    MOV SI,AX     {column counter}
  10741.                    XOR DI,DI
  10742.                    MOV BX,LINESIZE-1
  10743.                    MOV DS,DX     {DS = ^source data}
  10744.  
  10745.                    PUSH AX
  10746.                    PUSH DX
  10747.                    MOV DX,3CEh   {select write mode 0}
  10748.                    MOV AX,4005h
  10749.                    OUT DX,AX
  10750.  
  10751.                    MOV DX,3C4h
  10752.  
  10753.                  @oneRow0:       {update first column:  }
  10754.                    MOV AX,0802h  {select write plane 3    }
  10755.                    OUT DX,AX
  10756.                    MOV AL,[SI +3*PAGESIZE]
  10757.                    MOV ES:[DI],AL
  10758.                    MOV AX,0402h  {select write plane 2    }
  10759.                    OUT DX,AX
  10760.                    MOV AL,[SI +2*PAGESIZE]
  10761.                    MOV ES:[DI],AL
  10762.                    MOV AX,0202h  {select write plane 1    }
  10763.                    OUT DX,AX
  10764.                    MOV AL,[SI +1*PAGESIZE]
  10765.                    MOV ES:[DI],AL
  10766.                    MOV AX,0102h  {select write plane 0    }
  10767.                    OUT DX,AX
  10768.                    MOVSB
  10769.                    ADD SI,BX     {position at next row:           }
  10770.                    ADD DI,BX     {works, because BX + 1 = LINESIZE}
  10771.                    LOOP @oneRow0
  10772.  
  10773.  
  10774.                    {--- insertion to realize timing:}
  10775.                    PUSH ES       {save all registers needed later     }
  10776.                    MOV AX,SEG @DATA {restore TP's data segment         }
  10777.                    MOV DS,AX
  10778.                  END;
  10779.                  INC(counter);
  10780.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10781.                  ASM;
  10782.                    POP ES
  10783.                    {--- end of insertion}
  10784.  
  10785.                    POP DX
  10786.                    POP AX
  10787.                    DEC AX        {all columns done?  }
  10788.                    JNS oneColumn4  {no, next column     }
  10789.  
  10790.                    MOV DX,3CEh   {restore write mode 0        }
  10791.                    MOV AX,4005h
  10792.                    OUT DX,AX
  10793.                    POP DS
  10794.                  END;
  10795.                 END
  10796.            ELSE BEGIN {scroll to the right }
  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] = ^visible page   }
  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] = ^source address}
  10811.  
  10812.                    PUSH DS
  10813.                    MOV DX,AX
  10814.                    MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
  10815.  
  10816.                    MOV AX,0      {AX = column counter}
  10817.  
  10818.                  oneColumn3:     {scroll old screen contents to the left:}
  10819.                    MOV SI,DX
  10820.                    MOV DI,AX
  10821.                    MOV DX,3C4h
  10822.                    MOV AX,0F02h  {access all 4 planes at once          }
  10823.                    OUT DX,AX
  10824.                    MOV DX,3CEh
  10825.                    MOV AX,4105h  {set write mode 1  }
  10826.                    OUT DX,AX
  10827.                    MOV DX,SI
  10828.                    MOV AX,DI
  10829.  
  10830.                    MOV SI,ES
  10831.                    MOV DS,SI     {DS = ES = visible graphic page }
  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's data}
  10839.                    MOV CX,YMAX+1 {CX = row counter }
  10840.  
  10841.                    MOV SI,AX
  10842.                    MOV DI,LINESIZE-1
  10843.                    MOV BX,LINESIZE-1
  10844.                    MOV DS,DX     {DS = ^source data}
  10845.  
  10846.                    PUSH AX
  10847.                    PUSH DX
  10848.                    MOV DX,3CEh   {select write mode 0}
  10849.                    MOV AX,4005h
  10850.                    OUT DX,AX
  10851.  
  10852.                    MOV DX,3C4h
  10853.  
  10854.                  @oneRow:        {update last column:    }
  10855.                    MOV AX,0802h  {select write plane 3    }
  10856.                    OUT DX,AX
  10857.                    MOV AL,[SI +3*PAGESIZE]
  10858.                    MOV ES:[DI],AL
  10859.                    MOV AX,0402h  {select write plane 2    }
  10860.                    OUT DX,AX
  10861.                    MOV AL,[SI +2*PAGESIZE]
  10862.                    MOV ES:[DI],AL
  10863.                    MOV AX,0202h  {select write plane 1    }
  10864.                    OUT DX,AX
  10865.                    MOV AL,[SI +1*PAGESIZE]
  10866.                    MOV ES:[DI],AL
  10867.                    MOV AX,0102h  {select write plane 0    }
  10868.                    OUT DX,AX
  10869.                    MOVSB
  10870.                    ADD SI,BX     {position at next row:           }
  10871.                    ADD DI,BX     {works, because BX + 1 = LINESIZE}
  10872.                    LOOP @oneRow
  10873.  
  10874.                    {--- insertion to realize timing:}
  10875.                    PUSH ES       {save all registers needed later     }
  10876.                    MOV AX,SEG @DATA {restore TP's data segment         }
  10877.                    MOV DS,AX
  10878.                  END;
  10879.                  INC(counter);
  10880.                  WHILE ClockTicks<t+counter*temp DO BEGIN END;
  10881.                  ASM;
  10882.                    POP ES
  10883.                    {--- end of insertion}
  10884.  
  10885.                    POP DX
  10886.                    POP AX
  10887.  
  10888.                    INC AX        {all columns done?  }
  10889.                    CMP AX,LINESIZE
  10890.                    JB oneColumn3  {no, next column     }
  10891.  
  10892.                    MOV DX,3CEh   {restore write mode 0        }
  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    = page, which contents will be made visible       }
  10903.   {     time  = time (in millisceconds) for this action (approx.)           }
  10904.   {out: - }
  10905.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  10906.   CONST centerX=XMAX DIV 2; {middle of screen}
  10907.         centerY=YMAX DIV 2;
  10908.         k=189;     {number of circles := sqrt(centerX² + centerY²), rounded up}
  10909.         adjust=0.707106781; {compensation in diagonal direction = 1/sqrt(2) }
  10910.         n=TRUNC(k/adjust);  {number of executions of the delay loop }
  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.    {unfortunately, FOR true_radius:=1 TO k STEP 1/adjust isn't possible in TP}
  10923.    radius:=0.0;
  10924.    REPEAT
  10925.     radqu:=TRUNC(sqr(radius));
  10926.     FOR x:=0 TO TRUNC(radius/sqrt(2)) DO {compute octant    }
  10927.      BEGIN
  10928.       y:=TRUNC(sqrt(radqu-sqr(x))); {Pythagorean proposition}
  10929.       u1:=x0-x; v1:=y0-y;           {use axial- and point symmetrie      }
  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    = number of lines to be scrolled up             }
  10971. {     wait = time (in ms) the program will wait after each line     }
  10972. {rem: scrolling always starts at page 0 (=$A000:0000)     }
  10973. {     After the scrolling, the command "Screen(1-page)" must be issued!}
  10974. BEGIN
  10975.  Screen(0); {position at $A000:0000      }
  10976.  ASM
  10977.     XOR SI,SI                {compute address of page 0 = $A000:0000    }
  10978.     AND SI,3                 {page value *2 (word-sized entries!)}
  10979.     SHL SI,1                 {add start address of array to that   }
  10980.     ADD SI,OFFSET Offset_Adr-StartIndex*2  {evtl. correct displacement    }
  10981.     LODSW                    {and fetch value}
  10982.     MOV BX,AX
  10983.     MOV CX,n
  10984.     MOV SI,wait
  10985.  
  10986.   @oneline:
  10987.     ADD BX,LINESIZE
  10988.  
  10989.     CLI                      {May not be interrupted!              }
  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-startaddress-register}
  11002.     OUT DX,AL
  11003.     INC DX
  11004.  
  11005.     MOV AL,BL
  11006.     OUT DX,AL                {set LB of new starting address  }
  11007.     DEC DX
  11008.     MOV AL,$0C
  11009.     OUT DX,AL
  11010.     INC DX
  11011.     MOV AL,BH                {set HB of new starting address  }
  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 = starting address}
  11030. {     dest   = destination address }
  11031. {     len    = length of the area to copy       }
  11032. {out: - }
  11033. {rem: The both areas may not overlap each other       }
  11034. {     WriteMode1 will be used; therefore, the length "len" counts  }
  11035. {     for 4 bytes each: for example, a call like       }
  11036. {     CopyVRAMtoVRAM(Ptr($A000,0),Ptr($A000,PAGESIZE),PAGESIZE) }
  11037. {     would copy a complete page (4*PAGESIZE = 64000 bytes)}
  11038. ASM
  11039.   MOV AX,4105h   {enable write mode 1   }
  11040.   MOV DX,3CEh
  11041.   OUT DX,AX
  11042.   MOV AX,0F02h   {access all 4 planes at once          }
  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 for: use EMS-memory for BACKGNDPAGE contents}
  11064. {out: SpriteN[],SPRITEAD[],SPRITEPTR[],SPRITESIZE[],BackTile[] have been }
  11065. {     initialised to "completely empty"}
  11066. {     NextSprite[] has been set so that each sprite is its own    }
  11067. {     successor     }
  11068. {     PAGE, PAGEADR have been set to graphic page 0         }
  11069. {     BACKGNDADR has been set to the background page        }
  11070. {     SCROLLADR has been set to the scrollable background page   }
  11071. {     BACKGROUNDMODE has been set to STATIC  }
  11072. {     The default tile for the scolling background has been set to #0    }
  11073. {     StartVirtualX,StartVirtualY =0 (that is: virtual = absolute coord.)}
  11074. {     oldMode = old graphicmode  }
  11075. {     Error has been set, if no VGA-card could be found in the system    }
  11076. {     CycleTime = 0, that is: no min. time for animation cycle           }
  11077. {     Color = 15, that is: white }
  11078. {     CurrentFont = pointer to internal font}
  11079. {     FontHeight, FontWidth = sizes of internal font        }
  11080. {     FontType = its type  }
  11081. {     ActualColors = default color palette of mode $13 }
  11082. {     CRTAddress = port address of CRT-address register}
  11083. {     StatusReg  = port address of state register     }
  11084. {     EMSused = TRUE, if EMS-memory has been allocated for BACKGNDPAGE   }
  11085. {     BackgroundEMSHandle = handle to allocated EMS-block (if EMSused=TRUE)     }
  11086. {     buf = pointer to allocated heap-block (if EMSused=FALSE)           }
  11087. {     Win* coordinates have been set to the complete animation window    }
  11088. {     SplitIndex has been set so that all sprites will be clipped to     }
  11089. {     the animation window         }
  11090. {rem: This procedure should be called only once - namely in the very     }
  11091. {     beginning - for initializing the whole package properly                            }
  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's identify-adapter-function supported?}
  11102.               ( (BL=7) OR (BL=8) )  {monochrome or color VGA - 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];  {segment address of background page }
  11127.  
  11128.  PAGE:=0;   {page to be drawn upon                }
  11129.  PAGEADR:=Segment_Adr[PAGE];
  11130.  SCROLLADR:=Segment_Adr[SCROLLPAGE];
  11131.  SetBackgroundMode(STATIC);
  11132.  SetOffscreenTile(0);
  11133.  
  11134.  StartVirtualX:=0; StartVirtualY:=0;    {virtual = absolute coordinates  }
  11135.  Color:=white;            {set actual drawing color to white }
  11136.  regs.ah:=$f; intr($10,regs); oldMode:=regs.al;
  11137.  
  11138.  ActualColors:=DefaultColors;
  11139.  {SetShadowTab(Schatten) isn't needed, as the default values are set already}
  11140.  
  11141.  ASM  {see if we are using color-/monochorme display}
  11142.    MOV DX,3CCh  {use output register:     }
  11143.    IN AL,DX
  11144.    TEST AL,1    {is it a color display?    }
  11145.    MOV DX,3D4h
  11146.    JNZ @L1      {yes  }
  11147.    MOV DX,3B4h  {no  }
  11148.   @L1:          {DX = 3B4h / 3D4h = CRTAddress-register for monochrome/color}
  11149.    MOV CRTAddress,DX
  11150.    ADD DX,6     {DX = 3BAh / 3DAh = state register for monochrome/color}
  11151.    MOV StatusReg,DX
  11152.  END; {of ASM}
  11153.  
  11154.  LoadFont('');  {load internal font }
  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 {use EMS      }
  11164.         BackgroundEMSHandle:=EMSAllocate(4); {allocate 64K  }
  11165.         If EmsError<>0
  11166.          THEN BEGIN {don't do it}
  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 {no, not enough or "wrong" EMS-memory, use heap instead:        }
  11178.         New(buf)
  11179.        END
  11180.   ELSE EMSFillFrame(BackgroundEMSHandle); {prepare EMS access         }
  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); {round towards increasing addresses  }
  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); {=clip all    }
  11201. END;
  11202.  
  11203. PROCEDURE CloseRoutines;
  11204. { in: oldMode = old graphicmode, to which we'll switch back            }
  11205. {     EMSused = did we use EMS memory for BACKGNDPAGE?       }
  11206. {     BackgroundEMSHandle = if yes, then this is the handle to it  }
  11207. {     buf       = if no, then this is the pointer to the normal heap area}
  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 = number of the occurred error    }
  11218. {out: the error described in words}
  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 = file to search, incl. starting path}
  11247. {out: complete pathname to file   }
  11248. {rem: If the file is not found in the specified directory, then all   }
  11249. {     subdirectories will be searched through recursively.}
  11250. {     If the search still doesn't find the file, '' will be returned }
  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 = file to be searched}
  11259.  {     Pfad = path to start the search from  }
  11260.  {out: complete pathname to file or '', if the file is not found       }
  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.   {here: file has not be found in current directory      }
  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 {search through next directory}
  11280.            temp:=SearchFile(Pfad+Dir.Name+'\');
  11281.            IF temp<>''
  11282.         THEN BEGIN {found recursively!}
  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.