home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Stars of Shareware: Programmierung
/
SOURCE.mdf
/
programm
/
msdos
/
pascal
/
anivga12
/
anivga.eng
< prev
next >
Wrap
Text File
|
1993-07-11
|
329KB
|
11,305 lines
{$A+,B-,D+,L+,N-,E-,O-,R-,S-,V-,G-,F-,I-,X-}
{$M 16384,0,655360}
{$UNDEF IOcheck}
{Programtool for the realization of fast animations on the VGA-graphic- }
{card by: Kai Rohrbacher, 1988-1993, Turbo-Pascal 6.0 }
{ Features: }
{ - flickerfree animation by using page-flipping, watching retrace }
{ signals and usage of a special 256 color mode of the VGA }
{ - sprite movement in any pixel-increments you want }
{ - arbitrary background image for the animation }
{ - animations may be restricted to a screen window }
{ - full use of the VGA's 256-color mode }
{ - several sprite display methods available: }
{ - sprites can be declared to be transparent with regard to the background }
{ or other sprites pixel by pixel }
{ - sprites can change their color depending on the underlying background }
{ image (-> shadow function) }
{ - pixel precise hit-detection routine for sprite collisions built in }
{ - correct clipping of sprites at the screen boundaries when moving on- or }
{ offscreen }
{ - up to 32767 different sprites (default: 1000) }
{ - up to 32767 sprites may be simultaneously active (default: 500) }
{ - maximal size of each sprite = 64k }
{ - maximal size of all loaded sprites only restricted by available memory }
{ - works with virtual coordinates in the range -16000..+16000, thus }
{ simple creation of horizontal/vertical "scrolling" applications }
{ - scrolling background image, too }
{ - restricting the animations to a window is possible }
{ - several supporting routines to: draw lines (with built in clipping- }
{ algorithm), points and graphic-text (incl. clipping), automatic heap }
{ management for storing/loading sprites, handling background images, }
{ changing sprite display modes at runtime, adjustment for different }
{ CPU-clocks, ... }
UNIT ANIVGA;
INTERFACE
USES CRT,DOS,Compression;
CONST ANIVGAVersion=12; {version number}
NMAX=499;
XMAX=319;
YMAX=199;
LoadMAX=1000; {max. number of simultaneously active sprites }
LINESIZE=(XMAX+1) DIV 4; {size of one row = 80 bytes }
PAGESIZE=(YMAX+1)*LINESIZE; {200 rows at 320/4 bytes each}
BACKGNDPAGE=2;
SCROLLPAGE=3;
STATIC=0; {constants for background mode}
SCROLLING=1;
MaxTiles=10000; {max. number of background tiles }
StartVirtualX:INTEGER=0; {upper left image corner }
StartVirtualY:INTEGER=0;
{supported display modes for sprites: }
Display_NORMAL=0; {normal : transparent for color 0 }
Display_FAST =1; {fast : don't take background into account}
Display_SHADOW=2; {shadow : color lookup driven by background data}
Display_SHADOWEXACT=3; {color 0 is transparent for shadows, too }
Display_UNKNOWN=255;{error value}
{error codes of the animation package: }
Err_None=0;
Err_NotEnoughMemory=1;
Err_FileIO=2;
Err_InvalidSpriteNumber=3;
Err_NoSprite=4;
Err_InvalidPageNumber=5;
Err_NoVGA=6;
Err_NoPicture=7;
Err_InvalidPercentage=8;
Err_NoTile=9;
Err_InvalidTileNumber=10;
Err_InvalidCoordinates=11;
Err_BackgroundToBig=12;
Err_InvalidMode=13;
Err_InvalidSpriteLoadNumber=14;
Err_NoPalette=15;
Err_PaletteWontFit=16;
Err_InvalidFade=17;
Err_NoFont=18;
Err_EMSError=19;
CONST MaxFontHeight=16;
MaxFontWidth=15;
TagMonoFont=0;
TagColorFont=1;
TagProportional=$80;
TYPE MonoFontChar=ARRAY[0..MaxFontHeight-1] OF WORD;
MonoFont=ARRAY[0..255] OF MonoFontchar;
ColorFontChar=ARRAY[0..MaxFontHeight-1] OF ARRAY[0..MaxFontWidth-1] OF BYTE;
ColorFont=ARRAY[0..255] OF ColorFontchar;
FontOrient=(horizontal,vertical); {possible text directions }
CONST GraphTextOrientation:FontOrient=horizontal; {actual output direction }
GraphTextColor:BYTE=white; {actual text color }
GraphTextBackground:BYTE=white;
CurrentFont:POINTER=NIL; {pointer to actual font }
UpdateOuterArea:BYTE=2; {update outer background area}
WinClip:BOOLEAN=FALSE; {clip pixel to window? }
VAR FontHeight,
FontWidth,
FontType,
FontProportion:BYTE;
FontWidthTable:ARRAY[0..255] OF BYTE;
TYPE Table=ARRAY[0..NMAX] OF INTEGER;
ColorTable=ARRAY[0..255] OF BYTE;
TYPE PaletteEntry=RECORD red,green,blue:BYTE END;
Palette=ARRAY[0..255] OF PaletteEntry;
PalettePtr=^Palette;
CONST DefaultColors:Palette= {default palette colors of 256color mode}
( {read out by using the VGA-BIOS-calls: }
(red: 0; green: 0; blue: 0), { MOV AX,1017h ;read palette registers}
(red: 0; green: 0; blue: 42), { XOR BX,BX ;start with color 0 }
(red: 0; green: 42; blue: 0), { MOV CX,100h ;all 256 colors }
(red: 0; green: 42; blue: 42), { LES DX,Ziel ;to ES:DX }
(red: 42; green: 0; blue: 0), { INT 10h }
(red: 42; green: 0; blue: 42), {Attention! These values can/could be }
(red: 42; green: 21; blue: 0), {read out directly from the VGA card not}
(red: 42; green: 42; blue: 42), {until a graphic mode has been activated; }
(red: 21; green: 21; blue: 21), {thus, here they were defined "static"}
(red: 21; green: 21; blue: 63),
(red: 21; green: 63; blue: 21),
(red: 21; green: 63; blue: 63),
(red: 63; green: 21; blue: 21),
(red: 63; green: 21; blue: 63),
(red: 63; green: 63; blue: 21),
(red: 63; green: 63; blue: 63),
(red: 0; green: 0; blue: 0),
(red: 5; green: 5; blue: 5),
(red: 8; green: 8; blue: 8),
(red: 11; green: 11; blue: 11),
(red: 14; green: 14; blue: 14),
(red: 17; green: 17; blue: 17),
(red: 20; green: 20; blue: 20),
(red: 24; green: 24; blue: 24),
(red: 28; green: 28; blue: 28),
(red: 32; green: 32; blue: 32),
(red: 36; green: 36; blue: 36),
(red: 40; green: 40; blue: 40),
(red: 45; green: 45; blue: 45),
(red: 50; green: 50; blue: 50),
(red: 56; green: 56; blue: 56),
(red: 63; green: 63; blue: 63),
(red: 0; green: 0; blue: 63),
(red: 16; green: 0; blue: 63),
(red: 31; green: 0; blue: 63),
(red: 47; green: 0; blue: 63),
(red: 63; green: 0; blue: 63),
(red: 63; green: 0; blue: 47),
(red: 63; green: 0; blue: 31),
(red: 63; green: 0; blue: 16),
(red: 63; green: 0; blue: 0),
(red: 63; green: 16; blue: 0),
(red: 63; green: 31; blue: 0),
(red: 63; green: 47; blue: 0),
(red: 63; green: 63; blue: 0),
(red: 47; green: 63; blue: 0),
(red: 31; green: 63; blue: 0),
(red: 16; green: 63; blue: 0),
(red: 0; green: 63; blue: 0),
(red: 0; green: 63; blue: 16),
(red: 0; green: 63; blue: 31),
(red: 0; green: 63; blue: 47),
(red: 0; green: 63; blue: 63),
(red: 0; green: 47; blue: 63),
(red: 0; green: 31; blue: 63),
(red: 0; green: 16; blue: 63),
(red: 31; green: 31; blue: 63),
(red: 39; green: 31; blue: 63),
(red: 47; green: 31; blue: 63),
(red: 55; green: 31; blue: 63),
(red: 63; green: 31; blue: 63),
(red: 63; green: 31; blue: 55),
(red: 63; green: 31; blue: 47),
(red: 63; green: 31; blue: 39),
(red: 63; green: 31; blue: 31),
(red: 63; green: 39; blue: 31),
(red: 63; green: 47; blue: 31),
(red: 63; green: 55; blue: 31),
(red: 63; green: 63; blue: 31),
(red: 55; green: 63; blue: 31),
(red: 47; green: 63; blue: 31),
(red: 39; green: 63; blue: 31),
(red: 31; green: 63; blue: 31),
(red: 31; green: 63; blue: 39),
(red: 31; green: 63; blue: 47),
(red: 31; green: 63; blue: 55),
(red: 31; green: 63; blue: 63),
(red: 31; green: 55; blue: 63),
(red: 31; green: 47; blue: 63),
(red: 31; green: 39; blue: 63),
(red: 45; green: 45; blue: 63),
(red: 49; green: 45; blue: 63),
(red: 54; green: 45; blue: 63),
(red: 58; green: 45; blue: 63),
(red: 63; green: 45; blue: 63),
(red: 63; green: 45; blue: 58),
(red: 63; green: 45; blue: 54),
(red: 63; green: 45; blue: 49),
(red: 63; green: 45; blue: 45),
(red: 63; green: 49; blue: 45),
(red: 63; green: 54; blue: 45),
(red: 63; green: 58; blue: 45),
(red: 63; green: 63; blue: 45),
(red: 58; green: 63; blue: 45),
(red: 54; green: 63; blue: 45),
(red: 49; green: 63; blue: 45),
(red: 45; green: 63; blue: 45),
(red: 45; green: 63; blue: 49),
(red: 45; green: 63; blue: 54),
(red: 45; green: 63; blue: 58),
(red: 45; green: 63; blue: 63),
(red: 45; green: 58; blue: 63),
(red: 45; green: 54; blue: 63),
(red: 45; green: 49; blue: 63),
(red: 0; green: 0; blue: 28),
(red: 7; green: 0; blue: 28),
(red: 14; green: 0; blue: 28),
(red: 21; green: 0; blue: 28),
(red: 28; green: 0; blue: 28),
(red: 28; green: 0; blue: 21),
(red: 28; green: 0; blue: 14),
(red: 28; green: 0; blue: 7),
(red: 28; green: 0; blue: 0),
(red: 28; green: 7; blue: 0),
(red: 28; green: 14; blue: 0),
(red: 28; green: 21; blue: 0),
(red: 28; green: 28; blue: 0),
(red: 21; green: 28; blue: 0),
(red: 14; green: 28; blue: 0),
(red: 7; green: 28; blue: 0),
(red: 0; green: 28; blue: 0),
(red: 0; green: 28; blue: 7),
(red: 0; green: 28; blue: 14),
(red: 0; green: 28; blue: 21),
(red: 0; green: 28; blue: 28),
(red: 0; green: 21; blue: 28),
(red: 0; green: 14; blue: 28),
(red: 0; green: 7; blue: 28),
(red: 14; green: 14; blue: 28),
(red: 17; green: 14; blue: 28),
(red: 21; green: 14; blue: 28),
(red: 24; green: 14; blue: 28),
(red: 28; green: 14; blue: 28),
(red: 28; green: 14; blue: 24),
(red: 28; green: 14; blue: 21),
(red: 28; green: 14; blue: 17),
(red: 28; green: 14; blue: 14),
(red: 28; green: 17; blue: 14),
(red: 28; green: 21; blue: 14),
(red: 28; green: 24; blue: 14),
(red: 28; green: 28; blue: 14),
(red: 24; green: 28; blue: 14),
(red: 21; green: 28; blue: 14),
(red: 17; green: 28; blue: 14),
(red: 14; green: 28; blue: 14),
(red: 14; green: 28; blue: 17),
(red: 14; green: 28; blue: 21),
(red: 14; green: 28; blue: 24),
(red: 14; green: 28; blue: 28),
(red: 14; green: 24; blue: 28),
(red: 14; green: 21; blue: 28),
(red: 14; green: 17; blue: 28),
(red: 20; green: 20; blue: 28),
(red: 22; green: 20; blue: 28),
(red: 24; green: 20; blue: 28),
(red: 26; green: 20; blue: 28),
(red: 28; green: 20; blue: 28),
(red: 28; green: 20; blue: 26),
(red: 28; green: 20; blue: 24),
(red: 28; green: 20; blue: 22),
(red: 28; green: 20; blue: 20),
(red: 28; green: 22; blue: 20),
(red: 28; green: 24; blue: 20),
(red: 28; green: 26; blue: 20),
(red: 28; green: 28; blue: 20),
(red: 26; green: 28; blue: 20),
(red: 24; green: 28; blue: 20),
(red: 22; green: 28; blue: 20),
(red: 20; green: 28; blue: 20),
(red: 20; green: 28; blue: 22),
(red: 20; green: 28; blue: 24),
(red: 20; green: 28; blue: 26),
(red: 20; green: 28; blue: 28),
(red: 20; green: 26; blue: 28),
(red: 20; green: 24; blue: 28),
(red: 20; green: 22; blue: 28),
(red: 0; green: 0; blue: 16),
(red: 4; green: 0; blue: 16),
(red: 8; green: 0; blue: 16),
(red: 12; green: 0; blue: 16),
(red: 16; green: 0; blue: 16),
(red: 16; green: 0; blue: 12),
(red: 16; green: 0; blue: 8),
(red: 16; green: 0; blue: 4),
(red: 16; green: 0; blue: 0),
(red: 16; green: 4; blue: 0),
(red: 16; green: 8; blue: 0),
(red: 16; green: 12; blue: 0),
(red: 16; green: 16; blue: 0),
(red: 12; green: 16; blue: 0),
(red: 8; green: 16; blue: 0),
(red: 4; green: 16; blue: 0),
(red: 0; green: 16; blue: 0),
(red: 0; green: 16; blue: 4),
(red: 0; green: 16; blue: 8),
(red: 0; green: 16; blue: 12),
(red: 0; green: 16; blue: 16),
(red: 0; green: 12; blue: 16),
(red: 0; green: 8; blue: 16),
(red: 0; green: 4; blue: 16),
(red: 8; green: 8; blue: 16),
(red: 10; green: 8; blue: 16),
(red: 12; green: 8; blue: 16),
(red: 14; green: 8; blue: 16),
(red: 16; green: 8; blue: 16),
(red: 16; green: 8; blue: 14),
(red: 16; green: 8; blue: 12),
(red: 16; green: 8; blue: 10),
(red: 16; green: 8; blue: 8),
(red: 16; green: 10; blue: 8),
(red: 16; green: 12; blue: 8),
(red: 16; green: 14; blue: 8),
(red: 16; green: 16; blue: 8),
(red: 14; green: 16; blue: 8),
(red: 12; green: 16; blue: 8),
(red: 10; green: 16; blue: 8),
(red: 8; green: 16; blue: 8),
(red: 8; green: 16; blue: 10),
(red: 8; green: 16; blue: 12),
(red: 8; green: 16; blue: 14),
(red: 8; green: 16; blue: 16),
(red: 8; green: 14; blue: 16),
(red: 8; green: 12; blue: 16),
(red: 8; green: 10; blue: 16),
(red: 11; green: 11; blue: 16),
(red: 12; green: 11; blue: 16),
(red: 13; green: 11; blue: 16),
(red: 15; green: 11; blue: 16),
(red: 16; green: 11; blue: 16),
(red: 16; green: 11; blue: 15),
(red: 16; green: 11; blue: 13),
(red: 16; green: 11; blue: 12),
(red: 16; green: 11; blue: 11),
(red: 16; green: 12; blue: 11),
(red: 16; green: 13; blue: 11),
(red: 16; green: 15; blue: 11),
(red: 16; green: 16; blue: 11),
(red: 15; green: 16; blue: 11),
(red: 13; green: 16; blue: 11),
(red: 12; green: 16; blue: 11),
(red: 11; green: 16; blue: 11),
(red: 11; green: 16; blue: 12),
(red: 11; green: 16; blue: 13),
(red: 11; green: 16; blue: 15),
(red: 11; green: 16; blue: 16),
(red: 11; green: 15; blue: 16),
(red: 11; green: 13; blue: 16),
(red: 11; green: 12; blue: 16),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0),
(red: 0; green: 0; blue: 0)
);
VAR Error:BYTE; {global error variable }
SpriteN:Table;
SpriteX:Table;
SpriteY:Table;
NextSprite:ARRAY[0..LoadMAX] OF WORD;
PAGE,PAGEADR,SCROLLADR,BACKGNDADR:WORD;
Color:BYTE; {drawing color for lines}
ActualColors:Palette; {actual color palette}
was_cut:BOOLEAN; {TRUE/FALSE, if "GetImage" had to clip image }
left_cut, {variables set by "GetImage"; if "was_cut" has}
right_cut, {been set to TRUE, they will report, where and }
top_cut, {how much of the image had to been clipped }
bottom_cut:WORD; {away }
WinXMIN,WinYMIN,WinXMAX,WinYMAX,WinWidth,WinHeight:WORD;
BackgroundMode:BYTE;
BackTile:ARRAY[0..MaxTiles] OF BYTE; {tile memory }
XTiles,YTiles:INTEGER; {width, height of defined area }
BackX1,BackY1,BackX2,BackY2:INTEGER; {coordinates of the defined area }
CONST Fade_Squares =0;
Fade_Moiree1 =1;
Fade_Moiree2 =2;
Fade_Moiree3 =3;
Fade_Moiree4 =4;
Fade_Moiree5 =5;
Fade_Moiree6 =6;
Fade_Moiree7 =7;
Fade_Moiree8 =8;
Fade_Moiree9 =9;
Fade_Moiree10=10;
Fade_Moiree11=11;
Fade_Moiree12=12;
Fade_Moiree13=13;
Fade_Moiree14=14;
Fade_SweepInFromTop=15;
Fade_SweepInFromBottom=16;
Fade_SweepInFromLeft=17;
Fade_SweepInFromRight=18;
Fade_ScrollInFromTop=19;
Fade_ScrollInFromBottom=20;
Fade_ScrollInFromLeft=21;
Fade_ScrollInFromRight=22;
Fade_Circles =23;
Fade_Moiree15=24;
{---- for EMS-routines ----}
CONST BACKTAB:ARRAY[0..3] OF WORD=(0,PAGESIZE,2*PAGESIZE,3*PAGESIZE);
TYPE Puffer=ARRAY[0..4*PAGESIZE-1 +15] OF BYTE; {buffer for a page }
VAR buf:^Puffer; {pointer to it}
Const EMSInt = $67; {interrupt used for EMS }
USEEMS = TRUE; {if FALSE: no usage of existing EMS, }
{if TRUE : use EMS, if it exists }
Var EMSError:BYTE; {<>0 means: there was an error }
BackgroundEMSHandle:WORD; {access handle for allocated EMS-block }
EMSused:BOOLEAN; {tells, whether EMS is really used or not }
PROCEDURE ShadowTab;
PROCEDURE SetShadowTab(brightness:BYTE);
PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
PROCEDURE GetPalette(VAR pal:Palette);
PROCEDURE FadeToPalette(destPal:Palette; AnzSteps:WORD);
FUNCTION LoadPalette(name:String; number:BYTE; VAR pal:Palette):WORD;
PROCEDURE EnsureEMSConsistency(EMSHandle:WORD);
PROCEDURE SetCycleTime(milliseconds:WORD);
PROCEDURE SetSpriteCycle(nr,len:WORD);
FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:WORD):POINTER;
PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:WORD);
PROCEDURE FreeImageMem(p:POINTER);
PROCEDURE InitGraph;
PROCEDURE Screen(pa:WORD);
PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:WORD);
PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
FUNCTION GetPixel(x,y:INTEGER):BYTE;
FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE;
FUNCTION PageGetPixel(x,y:INTEGER; pa:WORD):BYTE;
PROCEDURE PutPixel(x,y:INTEGER; color:Byte);
PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte);
PROCEDURE PagePutPixel(x,y:INTEGER; color:BYTE; pa:WORD);
PROCEDURE LoadFont(s:STRING);
FUNCTION OutTextLength(s:STRING):WORD;
PROCEDURE OutTextXY(x,y:INTEGER; pa:WORD; s:STRING);
PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
PROCEDURE MakeTextSprite(s:STRING; nr:WORD);
FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN;
PROCEDURE SetSplitIndex(number:INTEGER);
FUNCTION GetSplitIndex:INTEGER;
PROCEDURE SetAnimateWindow(x1,y1,x2,y2:INTEGER);
PROCEDURE Animate;
PROCEDURE FreeSpriteMem(number:WORD);
FUNCTION LoadSprite(name:String; number:WORD):WORD;
FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
PROCEDURE SetBackgroundMode(mode:BYTE);
PROCEDURE MakeTileArea(FirstTile:BYTE; TileWidth,TileHeight:INTEGER);
PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
FUNCTION GetTile(x,y:INTEGER):BYTE;
PROCEDURE SetOffscreenTile(TileNr:BYTE);
PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
FUNCTION GetModeByte(Sp:WORD):BYTE;
PROCEDURE FillPage(pa:WORD; color:Byte);
PROCEDURE FillBackground(color:BYTE);
PROCEDURE GetBackgroundFromPage(pa:WORD);
PROCEDURE WritePage(name:STRING; pa:WORD);
PROCEDURE LoadPage(name:STRING; pa:WORD);
PROCEDURE WriteBackgroundPage(name:STRING);
PROCEDURE LoadBackgroundPage(name:STRING);
PROCEDURE FadeIn(pa,ti,style:WORD);
PROCEDURE CopyVRAMtoVRAM(source,dest:POINTER; len:WORD);
PROCEDURE IntroScroll(n,wait:WORD);
PROCEDURE InitRoutines;
PROCEDURE CloseRoutines;
FUNCTION GetErrorMessage:STRING;
FUNCTION FindFile(P:PathStr):PathStr;
{--------------------------------------------------------------------------}
IMPLEMENTATION
CONST ANIVGAVersionS:STRING[11]='AniVGA V1.2'; {version number}
StartIndex=0;
EndIndex=StartIndex+3;
{offset addresses of the graphic pages (in segment $A000):}
Offset_Adr:Array[StartIndex..EndIndex] OF WORD=($0000,$3E80,$7D00,$BB80);
{segment addresses of graphic pages (with offset = 0):}
Segment_Adr:ARRAY[StartIndex..EndIndex] OF WORD=($A000,$A3E8,$A7D0,$ABB8);
{sprite header format: }
{0..1 DW offset_ptr_to_plane0_data}
{2..3 DW offset_ptr_to_plane1_data}
{4..5 DW offset_ptr_to_plane2_data}
{6..7 DW offset_ptr_to_plane3_data}
{8..9 DW width (in groups of 4) (in German: "Breite")}
{10..11 DW heigth in rows (in German: "Höhe")}
{12..15 DB 1,2,4,8 ; translate-table used for port-accesses}
{16..17 DW SpriteLength ; length of this sprite in bytes}
{ ; now follows the scratch area reserved for temporary}
{ ; variables: ("a|b" = used for a and b)}
{18..19 DW ? ; licutoff_ | hit1xfirst}
{20..21 DW ? ; zeilenadr | hit1yfirst}
{22..23 DW ? ; bildx | hit2xfirst}
{24..25 DW ? ; yoffset_ | hit2yfirst}
{26..27 DW ? ; end_min_start | ueberlappx_1}
{28..29 DW ? ; WinXMIN_ | ueberlappy_1}
{30..31 DW ? ; WinXMAX_ | x1}
{32..33 DW ? ; WinYMIN_ | x2}
{34..35 DW ? ; | y1}
{36..37 DW ? ; | y2}
{38..39 DB 'K','R' ; tag used for sprites}
{40 DB 1 ; version number}
{41 DB 0 ; display mode used for sprite}
{42..43 DW offset_ptr_to_left_boundary_data }
{44..45 DW offset_ptr_to_right_boundary_data}
{46..47 DW offset_ptr_to_top_boundary_data }
{48..49 DW offset_ptr_to_bottom_boundary_data}
{50..?? DB data }
{for ex.: xxrxxxxx, with: r=red=40, g=green=45, b=blue=35, x=white=30}
{ xrgrxxxx}
{ rbgbrxxx}
{addresses of important values inside the sprite header: }
Left=42;
Right=44;
Top=46;
Bottom=48;
Breite=8;
Hoehe=10;
Translate=12;
SpriteLength=16;
Kennung=38;
Version=40;
Modus=41;
{addresses of temporary variables for the drawing routines:}
licutoff_=18;
zeilenadr=20;
bildx=22;
yoffset_=24;
end_min_start=26;
WinXMIN_=28;
WinXMAX_=30;
WinYMIN_=32;
{addresses of temporary variables for the collision detection routine:}
hit1xfirst=18;
hit1yfirst=20;
hit2xfirst=22;
hit2yfirst=24;
ueberlappx_1=26;
ueberlappy_1=28;
x1=30;
x2=32;
y1=34;
y2=36;
TranslateTab:ARRAY[0..3] OF BYTE=(1,2,4,8); {For mask addressing }
PICHeader:STRING[3]='PIC'; {tag in picture files }
Schatten :BYTE=70; {default brightness of shadows }
TYPE SpriteHeader= RECORD
Zeiger_auf_Plane:Array[0..3] OF Word;
Breite_in_4er_Gruppen:WORD;
Hoehe_in_Zeilen:WORD;
Translate:Array[1..4] OF Byte;
SpriteLength:WORD;
Dummy:Array[1..10] OF Word;
Kennung:ARRAY[1..2] OF CHAR;
Version:BYTE;
Modus:BYTE;
ZeigerL,ZeigerR,ZeigerO,ZeigerU:Word;
END;
CONST Kopf=SizeOf(SpriteHeader);
FontMask:ARRAY[0..MaxFontWidth-1] OF WORD=
($4000,$2000,$1000,$800,$400,$200,$100,$80,$40,$20,$10,8,4,2,1);
internalFont:ARRAY[0..255,0..5] OF BYTE= {used font: }
(( 0, 0, 0, 0, 0, 0), {#0} {selfmade 6x6 font (ugly..)}
( 0, 54, 0, 62, 28, 0), {#1}
( 62, 42, 62, 34, 54, 62), {#2}
( 0, 20, 62, 62, 28, 8), {#3}
( 0, 8, 28, 62, 28, 8), {#4}
( 8, 28, 54, 62, 8, 28), {#5}
( 8, 28, 62, 62, 8, 28), {#6}
( 0, 8, 54, 54, 8, 0), {#7}
( 62, 54, 42, 42, 54, 62), {#8}
( 0, 28, 50, 38, 28, 0), {#9}
( 62, 34, 42, 42, 34, 62), {#10}
( 14, 6, 8, 28, 34, 28), {#11}
( 28, 34, 28, 8, 62, 8), {#12}
( 14, 10, 8, 8, 56, 56), {#13}
( 0, 30, 18, 30, 18, 54), {#14}
( 0, 8, 42, 20, 42, 8), {#15}
( 0, 32, 56, 62, 56, 32), {#16}
( 0, 2, 14, 62, 14, 2), {#17}
( 8, 28, 42, 42, 28, 8), {#18}
( 0, 54, 54, 54, 0, 54), {#19}
( 0, 30, 42, 26, 10, 10), {#20}
( 6, 8, 4, 10, 36, 24), {#21}
( 0, 0, 0, 0, 62, 62), {#22}
( 8, 28, 8, 28, 8, 62), {#23}
( 0, 8, 28, 62, 8, 8), {#24}
( 0, 8, 8, 62, 28, 8), {#25}
( 0, 8, 4, 62, 4, 8), {#26}
( 0, 8, 16, 62, 16, 8), {#27}
( 0, 0, 48, 62, 0, 0), {#28}
( 0, 0, 20, 62, 20, 0), {#29}
( 0, 0, 0, 8, 28, 62), {#30}
( 0, 0, 62, 28, 8, 0), {#31}
( 0, 0, 0, 0, 0, 0), { }
( 0, 12, 12, 12, 0, 12), {!}
( 0, 20, 20, 0, 0, 0), {"}
( 0, 20, 62, 20, 62, 20), {#}
( 8, 30, 40, 28, 10, 60), { $}
( 0, 50, 4, 8, 16, 38), {%}
( 0, 28, 54, 28, 38, 26), {&}
( 0, 4, 8, 0, 0, 0), {'}
( 0, 28, 48, 48, 48, 28), {(}
( 0, 56, 12, 12, 12, 56), {)}
( 0, 20, 8, 62, 8, 20), {*}
( 0, 0, 8, 62, 8, 0), {+}
( 0, 0, 0, 8, 8, 16), {,}
( 0, 0, 0, 62, 0, 0), {-}
( 0, 0, 0, 0, 12, 0), {.}
( 1, 2, 4, 8, 16, 32), {/}
( 0, 28, 38, 42, 50, 28), {0}
( 0, 12, 28, 12, 12, 30), {1}
( 0, 60, 6, 28, 48, 62), {2}
( 0, 60, 6, 28, 6, 60), {3}
( 0, 48, 52, 62, 12, 12), {4}
( 0, 62, 48, 60, 6, 60), {5}
( 0, 62, 32, 62, 34, 62), {6}
( 0, 62, 6, 12, 24, 24), {7}
( 0, 28, 54, 28, 54, 28), {8}
( 0, 28, 34, 30, 2, 28), {9}
( 0, 0, 8, 0, 8, 0), {:}
( 0, 0, 8, 0, 8, 16), {;}
( 0, 6, 12, 24, 12, 6), {<}
( 0, 0, 62, 0, 62, 0), {=}
( 0, 24, 12, 6, 12, 24), {>}
( 28, 34, 4, 8, 0, 8), {?}
( 0, 28, 34, 46, 32, 30), {@}
( 0, 28, 50, 62, 50, 50), {A}
( 0, 60, 50, 60, 50, 60), {B}
( 0, 30, 48, 48, 48, 30), {C}
( 0, 60, 54, 50, 54, 60), {D}
( 0, 62, 48, 60, 48, 62), {E}
( 0, 62, 48, 60, 48, 48), {F}
( 0, 30, 48, 54, 50, 30), {G}
( 0, 50, 50, 62, 50, 50), {H}
( 0, 30, 12, 12, 12, 30), {I}
( 0, 62, 2, 2, 50, 28), {J}
( 0, 50, 52, 56, 52, 50), {K}
( 0, 48, 48, 48, 48, 62), {L}
( 0, 34, 54, 42, 34, 34), {M}
( 0, 34, 50, 42, 38, 34), {N}
( 0, 28, 50, 50, 50, 28), {O}
( 0, 60, 50, 60, 48, 48), {P}
( 0, 24, 52, 52, 52, 26), {Q}
( 0, 60, 34, 60, 52, 50), {R}
( 0, 62, 48, 62, 6, 62), {S}
( 0, 62, 24, 24, 24, 24), {T}
( 0, 50, 50, 50, 50, 62), {U}
( 0, 50, 50, 50, 50, 12), {V}
( 0, 34, 34, 42, 62, 20), {W}
( 0, 34, 54, 28, 54, 34), {X}
( 0, 50, 50, 28, 12, 12), {Y}
( 0, 62, 6, 28, 48, 62), {Z}
( 0, 30, 24, 24, 24, 30), {[}
( 32, 16, 8, 4, 2, 1), {\}
( 0, 30, 6, 6, 6, 30), {]}
( 8, 20, 34, 0, 0, 0), {^}
( 0, 0, 0, 0, 0, 62), {_}
( 16, 8, 0, 0, 0, 0), {`}
( 0, 0, 28, 50, 50, 30), {a}
( 0, 32, 60, 34, 34, 60), {b}
( 0, 0, 30, 48, 48, 30), {c}
( 0, 2, 30, 34, 34, 30), {d}
( 0, 0, 28, 62, 32, 28), {e}
( 0, 6, 8, 30, 8, 8), {f}
( 0, 28, 34, 30, 2, 28), {g}
( 0, 48, 60, 50, 50, 50), {h}
( 12, 0, 12, 12, 12, 12), {i}
( 6, 0, 6, 6, 54, 28), {j}
( 0, 48, 52, 56, 54, 54), {k}
( 0, 24, 24, 24, 24, 14), {l}
( 0, 0, 52, 62, 42, 34), {m}
( 0, 0, 60, 50, 50, 50), {n}
( 0, 0, 28, 50, 50, 28), {o}
( 0, 0, 60, 50, 60, 48), {p}
( 0, 0, 28, 38, 30, 6), {q}
( 0, 0, 44, 26, 24, 24), {r}
( 0, 14, 16, 12, 34, 28), {s}
( 0, 24, 62, 24, 26, 12), {t}
( 0, 0, 50, 50, 50, 30), {u}
( 0, 0, 50, 50, 50, 28), {v}
( 0, 0, 34, 42, 42, 28), {w}
( 0, 0, 54, 24, 12, 54), {x}
( 0, 0, 50, 62, 2, 28), {y}
( 0, 0, 60, 12, 48, 62), {z}
( 0, 14, 24, 48, 24, 14),(*{*)
( 0, 4, 4, 0, 4, 4), {|}
( 0, 56, 12, 6, 12, 56),(*}*)
( 0, 26, 36, 0, 0, 0), {~}
( 0, 8, 20, 34, 62, 0), {#127}
( 28, 50, 32, 50, 28, 48), {#128}
( 0, 50, 0, 50, 50, 30), {#129}
( 6, 8, 28, 62, 32, 28), {#130}
( 4, 10, 0, 30, 49, 31), {#131}
( 26, 0, 28, 50, 50, 30), {#132}
( 12, 2, 28, 34, 34, 30), {#133}
( 4, 10, 4, 28, 50, 30), {#134}
( 0, 30, 48, 30, 4, 24), {#135}
( 28, 0, 28, 62, 48, 28), {#136}
( 20, 0, 28, 62, 32, 28), {#137}
( 12, 2, 28, 62, 48, 28), {#138}
( 26, 0, 12, 12, 12, 12), {#139}
( 12, 18, 0, 12, 12, 12), {#140}
( 24, 4, 0, 12, 12, 12), {#141}
( 50, 0, 28, 50, 62, 50), {#142}
( 12, 0, 28, 50, 62, 50), {#143}
( 28, 62, 48, 60, 48, 62), {#144}
( 0, 52, 10, 28, 40, 22), {#145}
( 0, 14, 20, 62, 36, 38), {#146}
( 8, 20, 0, 28, 50, 28), {#147}
( 20, 0, 28, 50, 50, 28), {#148}
( 24, 4, 0, 28, 50, 28), {#149}
( 8, 20, 0, 50, 50, 30), {#150}
( 24, 4, 0, 50, 50, 30), {#151}
( 20, 0, 50, 62, 2, 28), {#152}
( 20, 0, 28, 50, 50, 28), {#153}
( 50, 0, 50, 50, 50, 62), {#154}
( 4, 30, 32, 32, 30, 4), {#155}
( 12, 18, 56, 16, 34, 62), {#156}
( 54, 8, 62, 8, 62, 8), {#157}
( 48, 40, 52, 46, 36, 38), {#158}
( 12, 10, 24, 12, 40, 24), {#159}
( 12, 16, 0, 28, 34, 30), {#160}
( 12, 16, 0, 8, 8, 8), {#161}
( 12, 16, 0, 28, 50, 28), {#162}
( 12, 16, 0, 50, 50, 30), {#163}
( 26, 36, 0, 44, 18, 18), {#164}
( 26, 36, 0, 50, 42, 38), {#165}
( 28, 36, 26, 0, 62, 0), {#166}
( 28, 34, 28, 0, 62, 0), {#167}
( 8, 0, 8, 16, 34, 28), {#168}
( 0, 0, 63, 48, 0, 0), {#169}
( 0, 0, 63, 3, 0, 0), {#170}
( 18, 20, 8, 16, 42, 10), {#171}
( 18, 20, 8, 20, 38, 2), {#172}
( 12, 0, 12, 12, 12, 0), {#173}
( 10, 20, 40, 20, 10, 0), {#174}
( 40, 20, 10, 20, 40, 0), {#175}
( 21, 42, 21, 42, 21, 42), {#176}
( 63, 63, 63, 63, 63, 63), {#177}
( 42, 21, 42, 21, 42, 21), {#178}
( 4, 4, 4, 4, 4, 4), {#179}
( 4, 4, 4, 60, 4, 4), {#180}
( 4, 4, 60, 4, 60, 4), {#181}
( 10, 10, 10, 58, 10, 10), {#182}
( 0, 0, 0, 62, 10, 10), {#183}
( 0, 0, 60, 4, 60, 4), {#184}
( 10, 10, 58, 2, 58, 10), {#185}
( 10, 10, 10, 10, 10, 10), {#186}
( 0, 0, 62, 2, 58, 10), {#187}
( 10, 10, 58, 2, 62, 0), {#188}
( 10, 10, 10, 62, 0, 0), {#189}
( 4, 4, 60, 4, 60, 0), {#190}
( 0, 0, 0, 60, 4, 4), {#191}
( 4, 4, 4, 7, 0, 0), {#192}
( 4, 4, 4, 63, 0, 0), {#193}
( 0, 0, 0, 63, 4, 4), {#194}
( 4, 4, 4, 7, 4, 4), {#195}
( 0, 0, 0, 63, 0, 0), {#196}
( 4, 4, 4, 63, 4, 4), {#197}
( 4, 4, 7, 4, 7, 4), {#198}
( 10, 10, 10, 11, 10, 10), {#199}
( 10, 10, 11, 8, 15, 0), {#200}
( 0, 0, 15, 8, 11, 10), {#201}
( 10, 10, 59, 0, 63, 0), {#202}
( 0, 0, 63, 0, 59, 10), {#203}
( 10, 10, 11, 8, 11, 10), {#204}
( 0, 0, 63, 0, 63, 0), {#205}
( 10, 10, 59, 0, 59, 10), {#206}
( 4, 4, 63, 0, 63, 0), {#207}
( 10, 10, 10, 63, 0, 0), {#208}
( 0, 0, 63, 0, 63, 4), {#209}
( 0, 0, 0, 63, 10, 10), {#210}
( 10, 10, 10, 15, 0, 0), {#211}
( 4, 4, 7, 4, 7, 0), {#212}
( 0, 0, 7, 4, 7, 4), {#213}
( 0, 0, 0, 15, 10, 10), {#214}
( 10, 10, 10, 63, 10, 10), {#215}
( 4, 4, 63, 4, 63, 4), {#216}
( 4, 4, 4, 60, 0, 0), {#217}
( 0, 0, 7, 4, 4, 4), {#218}
( 63, 63, 63, 63, 63, 63), {#219}
( 0, 0, 0, 63, 63, 63), {#220}
( 48, 48, 48, 48, 48, 48), {#221}
( 3, 3, 3, 3, 3, 3), {#222}
( 63, 63, 63, 0, 0, 0), {#223}
( 0, 0, 26, 36, 36, 26), {#224}
( 0, 28, 38, 44, 34, 44), {#225}
( 0, 62, 34, 32, 32, 32), {#226}
( 0, 0, 62, 20, 20, 20), {#227}
( 62, 18, 8, 16, 34, 62), {#228}
( 0, 0, 30, 36, 36, 24), {#229}
( 0, 18, 18, 30, 16, 48), {#230}
( 0, 0, 26, 44, 8, 8), {#231}
( 0, 62, 8, 20, 8, 62), {#232}
( 0, 28, 34, 62, 34, 28), {#233}
( 0, 28, 34, 34, 20, 54), {#234}
( 14, 16, 8, 28, 34, 28), {#235}
( 0, 0, 20, 42, 20, 0), {#236}
( 0, 2, 20, 42, 20, 32), {#237}
( 0, 30, 32, 62, 32, 30), {#238}
( 0, 0, 28, 34, 34, 34), {#239}
( 0, 62, 0, 62, 0, 62), {#240}
( 0, 8, 28, 8, 0, 62), {#241}
( 16, 8, 4, 8, 16, 62), {#242}
( 4, 8, 16, 8, 4, 62), {#243}
( 4, 10, 8, 8, 8, 8), {#244}
( 8, 8, 8, 8, 40, 16), {#245}
( 0, 8, 0, 62, 0, 8), {#246}
( 26, 36, 0, 26, 36, 0), {#247}
( 24, 36, 24, 0, 0, 0), {#248}
( 0, 0, 0, 12, 0, 0), {#249}
( 0, 0, 0, 4, 0, 0), {#250}
( 15, 8, 8, 40, 24, 8), {#251}
( 44, 18, 18, 0, 0, 0), {#252}
( 56, 4, 24, 32, 60, 0), {#253}
( 0, 0, 28, 28, 0, 0), {#254}
( 0, 0, 0, 0, 0, 0));{#255}
VAR Steigung:BYTE; {determines, which algorithm will be used }
DY_mal2,DY_m_DX_mal2:INTEGER;
oldMode:byte;
regs:registers;
IsAT:BYTE;
TimeFlag:BYTE;
CycleTime:LONGINT;
SPRITEAD :ARRAY[0..LoadMAX] OF WORD; {normalized segment addresses }
SPRITEPTR :ARRAY[0..LoadMAX] OF POINTER; {full 32-bit-pointers }
SPRITESIZE:ARRAY[0..LoadMAX] OF WORD; {allocated sprite size }
CRTAddress, StatusReg : WORD;
WinLowerRight,WinXMINdiv4,WinYMIN_mul_LINESIZE,
WinYMINmLINESIZEaWinXMINdiv4:WORD;
WinWidthDiv4:BYTE;
BWinXMIN,BWinYMIN,BWinXMAX,BWinYMAX,BWinLowerRight,
BWinYMIN_mul_LINESIZE:WORD; {backups of all Win* variables}
SplitIndex,SplitIndex_mal2:INTEGER; {split index for sprites & clipping}
{-----------------------------------------------------}
PROCEDURE ShadowTab; ASSEMBLER;
{Pseudo-procedure to store the color lookup table into the code segment }
{DO NOT TRY TO CALL THIS "PROCEDURE"!!! }
{default values correspond to a darkening to 70% of the original brightness}
ASM
DB 254,104,120,124,112,108,114, 24, 20,128,144, 3,136, 5,140, 7
DB 254,254, 17, 17, 18, 19, 20, 20, 21, 8, 23, 24, 24, 25, 26, 7
DB 1, 1,107,108, 5,108,109, 4, 4, 4, 6, 6,116,116,117, 2
DB 2, 2,123,124, 3,124,125, 1,152,155,156,156, 5,156,156,157
DB 160,163,164,164,164,164,164,165,168,171,172,172, 3,172,172,173
DB 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
DB 24, 24, 24, 24, 24, 24, 24, 24,176,177,178,179,180,181,182,183
DB 184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199
DB 200,201,203,204,204,204,205,207,208,209,211,212,212,212,213,215
DB 216,217,219,220,220,220,221,223,246,227,228,228,228,228,228,229
DB 234,235,236,236,236,236,236,237,242,243,244,244,244,244,244,245
DB 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254
DB 254,254,254,254,254,254,254,254, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
DB 17, 17, 17, 17, 17, 17, 17, 17,254,254,254,254,254,254,254, 7
END;
PROCEDURE CS_TranslateTab; ASSEMBLER;
{Small pseudo-procedure to store lookup table for the bitmasks into }
{the code segment, too}
ASM
DB 1,2,4,8
END;
PROCEDURE SetShadowTab(brightness:BYTE);
{ in: brightness = wanted brightness for the colors in the shadow area, }
{ in percentage of the brightness of the original colors }
{out: ShadowTab = (best approximation) color table for wanted dimming }
{ Schatten = new brightness (Schatten is a global variable!) }
{rem: default value of ShadowTab is 70% of the original color brightness! }
{ This routine takes some time (about 4 sec on a 8MHz-AT!) }
VAR neue_Tabelle:ColorTable;
p1:POINTER;
BEGIN
IF (brightness<0) OR (brightness>100)
THEN BEGIN
Error:=Err_InvalidPercentage;
exit
END;
p1:=@neue_Tabelle; {trick, as the assembler messes up accesses to the SS-segment}
ASM
MOV CX,256 {outer loop-counter }
LES DI,p1 {ES:DI = ^neue_Tabelle[i] }
MOV SI,OFFSET ActualColors {DS:SI = ^ActualColors[]}
@outerloop:
LODSB {AL = tempColors[i].red}
MUL brightness {will be addressed via SS! }
MOV DL,100
DIV DL {AL = tempColors[i].red * brightness DIV 100}
MOV BL,AL {BL := AL = new red part }
LODSB {dto., for green}
MUL brightness
MOV DL,100
DIV DL
MOV BH,AL {...into BH}
LODSB {dto., for blue}
MUL brightness
MOV DL,100
DIV DL
MOV DH,AL {...into DH}
{BL / BH / DH = RGB-parts of the color, for which we are seeking an approximation}
PUSH CX
PUSH SI
PUSH DI
PUSH BP
MOV DI,65535 {min. square of error (up to now) }
MOV CX,256 {walk through all 256 default colors}
MOV SI,OFFSET ActualColors {DS:SI = ^ActualColors[]}
@searchloop:
MOV AL,BL {compute difference in red part }
SUB AL,[SI]
JL @noNewMin {new color may not be brighter! }
MUL AL {compute square of error}
MOV BP,AX
MOV AL,BH {dto., for green part}
SUB AL,[SI+1]
JL @noNewMin
MUL AL
ADD BP,AX
JC @noNewMin {don't tolerate huge color differences }
MOV AL,DH {dto., for blue part }
SUB AL,[SI+2]
JL @noNewMin
MUL AL
ADD AX,BP
JC @noNewMin
CMP AX,DI {did we find a better approximation?}
JAE @noNewMin {no }
MOV DI,AX {yes, store square of error and color}
MOV DL,CL
OR DI,DI {square of error = 0?}
JZ @ColorDone {yes, we can't find any better solution than that! }
@noNewMin:
ADD SI,3
LOOP @searchloop
CMP DI,65535 {no color found? }
JNE @ColorDone {but yes, nothing to do!}
MOV CX,256 {no, thus search again }
MOV SI,OFFSET ActualColors {DS:SI = ^ActualColors[]}
@searchloop2:
LODSB
SUB AL,BL {Diff ≈±2^6 -> square ≈2^12 -> 3 * square < MaxInt }
IMUL AL {so no overflow is possible}
MOV BP,AX
LODSB {dto., for green part}
SUB AL,BH
IMUL AL
ADD BP,AX
LODSB {dto., for blue part }
SUB AL,DH
IMUL AL
ADD AX,BP
CMP AX,DI {did we find a better approximation?}
JAE @noNewMin2 {no }
MOV DI,AX {yes, store square of error and color}
MOV DL,CL
@noNewMin2:
LOOP @searchloop2
@ColorDone: {100h - DL = optimal color found }
POP BP
POP DI {ES:DI = ^neue_Tabelle[i] }
POP SI {DS:SI = ^ActualColors[i] }
POP CX
MOV AL,DL {store into neue_Tabelle[i] }
NEG AL {AL = 100h - DL = best approximation}
STOSB
DEC CX {replacement for "LOOP @outerloop"; next color!}
JCXZ @fertig
JMP @outerloop
@fertig:
END; {of ASM}
MOVE(neue_Tabelle,@ShadowTab^,256); {activate new color table}
Schatten:=brightness
END;
PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
{ in: pal = pointer to palette to be set }
{ update = TRUE/FALSE for: recompute/don't recompute ShadowTab}
{out: ActualColors = actual color palette }
{rem: palette has been set and evtl., ShadowTab has been recomputed }
BEGIN
IF @pal<>@ActualColors
THEN ActualColors:=pal; {copy palette into ActualColors }
ASM
MOV SI,OFFSET ActualColors {DS:SI = ^ActualColors[]}
CLI
mov dx,StatusReg
@WaitNotVSyncLoop:
in al,dx
and al,8
jnz @WaitNotVSyncLoop
@WaitVSyncLoop:
in al,dx
and al,8
jz @WaitVSyncLoop
MOV DX,3C8h
XOR AL,AL
OUT DX,AL
INC DX
MOV CX,256/2
@L1:
LODSW
OUT DX,AL
MOV AL,AH
OUT DX,AL
LODSW
OUT DX,AL
MOV AL,AH
OUT DX,AL
LODSW
OUT DX,AL
MOV AL,AH
OUT DX,AL
LOOP @L1
STI
END; {of ASM}
IF update THEN SetShadowTab(Schatten)
END;
PROCEDURE GetPalette(VAR pal:Palette); ASSEMBLER;
{ in: pal = pointer to palette memory }
{out: pal = actually set palette }
ASM
CLI
XOR AL,AL
MOV DX,3C7h
OUT DX,AL
LES DI,pal
MOV CX,768
MOV DX,3C9h
@L1:
IN AL,DX
STOSB
LOOP @L1
STI
END;
PROCEDURE FadeToPalette(destPal:Palette; AnzSteps:WORD);
{ in: ActualColors = actually set color palette }
{ destPal = destination palette}
{ AnzSteps = number of interleaving steps}
{out: ActualColors = destPal}
{rem: The procedure fades from ActualColors to destPal, and it uses }
{ AnzSteps steps to do so}
{ setting of the palette is synchronised to the vertical retrace, }
{ thus each interleaving step lasts 1/70 sec }
VAR oldColors,pal:Palette;
i,steps:INTEGER;
s,d:POINTER;
BEGIN
dec(anzsteps);
IF anzsteps<1
THEN steps:=1
ELSE steps:=anzsteps; {bring steps into the same segment as pal}
oldColors:=ActualColors;
pal:=destpal; {bring pal and oldColors into the same segment}
s:=@pal; d:=@ActualColors;
FOR i:=0 TO steps-1 DO
BEGIN
{now use assembler to compute the following sequence:}
{ FOR c:=0 TO 255 DO }
{ BEGIN }
{ ActualColors[c].red:=LONGINT(pal[c].red-oldColors[c].red)*i }
{ DIV steps+ oldColors[c].red; }
{ ActualColors[c].green:=LONGINT(pal[c].green-oldColors[c].green)*i }
{ DIV steps+ oldColors[c].green; }
{ ActualColors[c].blue:=LONGINT(pal[c].blue-oldColors[c].blue)*i }
{ DIV steps+ oldColors[c].blue; }
{ END; }
ASM
LES DI,d {ES:DI = pointer to ActualColors-table }
LDS SI,s {DS:SI = pointer to pal-table }
MOV BX,OFFSET oldColors-OFFSET pal -1 {DS:SI+BX+1 = pointer to oldColors}
MOV CX,256
@docolor:
XOR AH,AH
LODSB {AX := pal[c].red}
SUB AL,[SI+BX]
SBB AH,0 {AX := pal[c].red - oldColors[c].red = delta}
IMUL i {DX:AX := delta * i}
IDIV steps {AX := delta * i/steps}
ADD AL,[SI+BX] {AX := delta * i/steps + oldColors[c].red}
STOSB
{dto. for green}
XOR AH,AH
LODSB
SUB AL,[SI+BX]
SBB AH,0
IMUL i
IDIV steps
ADD AL,[SI+BX]
STOSB
{dto. for blue}
XOR AH,AH
LODSB
SUB AL,[SI+BX]
SBB AH,0
IMUL i
IDIV steps
ADD AL,[SI+BX]
STOSB
LOOP @docolor
MOV AX,SEG @DATA
MOV DS,AX
END;
SetPalette(ActualColors,FALSE)
END;
SetPalette(pal,TRUE)
END;
FUNCTION LoadPalette(name:String; number:BYTE; VAR pal:Palette):WORD;
{ in: name = name of the palette file (type: "*.PAL") to load }
{ number = number for the first color being read in from the file }
{ ActualColors = actually set color palette }
{out: number of colors read from the file (0 = an error occured) }
{ pal = color palette read from the file, evtl. filled up}
{rem: All entries in "pal" which get not overwritten by the file's contents }
{ will become set to the actually set colors of "ActualColors"; the }
{ palette will only become loaded, not actually set!}
LABEL quitloop;
VAR len:LONGINT;
f:FileOfByte;
i,count:WORD;
TempPal:Palette;
flag:BOOLEAN;
tempName:STRING;
BEGIN
count:=0; {number of palette entries read in til now }
tempName:=FindFile(name);
IF tempName<>'' THEN name:=tempName;
_assign(f,name);
{$I-} _reset(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
if (ioresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN {File doesn't exist or at least not with that path }
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
LoadPalette:=0; exit
END;
len:=_filesize(f); {determine file length}
if (len mod 3<>0) OR (len>3*256) OR (len<3)
THEN BEGIN
Error:=Err_NoPalette;
goto quitloop;
END;
IF len+number*3>3*256
THEN BEGIN
Error:=Err_PaletteWontFit;
goto quitloop;
END;
TempPal:=ActualColors; {preset temporary palette with actual colors }
{$I-}
_blockread(f,TempPal[number],len);
{$IFDEF IOcheck} {$I+} {$ENDIF}
IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
goto quitloop;
END;
flag:=FALSE;
FOR i:=number TO Pred(number+(len DIV 3))
DO flag:=flag OR (TempPal[i].red>63)
OR (TempPal[i].green>63)
OR (TempPal[i].blue>63);
IF flag
THEN BEGIN
Error:=Err_NoPalette;
goto quitloop;
END;
{everything went alright: return palette}
pal:=TempPal;
count:=len DIV 3;
quitloop: ;
_close(f);
LoadPalette:=count
END;
{Now, all the code pieces follow, which can be used to display a sprite; }
{the interface used is the same for all of them: }
{ in: CX = number of bytes, which have to be copied from... }
{ DS:SI = (pointer to source address) to... }
{ ES:BX = (pointer to destination address) }
{ DI = bitplane (0..3) (=X-coordinate AND 3) }
{ The proper bitmask for selecting the correct write plane has al- }
{ ready been set, but not that for the evtl. needed read plane! }
{ The routines can rely upon CX being <>0 }
{rem: Every routine MUST CONSIST OF EXACTLY 16 bytes and BE FULLY RELO- }
{ CATIBLE and MAY NOT CHANGE registers BP,DS and ES!!!!!!!!!!!!!!!!! }
{ Besides that, for distinguishtability, the routines must be pair- }
{ wise disjoint in their first two bytes! }
PROCEDURE Modus0; ASSEMBLER;
{mode 0 considers color 0 being transparent for background data }
ASM
INC CX
STC {change BX such that (together with SI) it can be }
SBB BX,SI {used for accessing the target address}
@L1:
LODSB {fetch sprite byte }
OR AL,AL {is it zero? }
LOOPZ @L1 {yes, ignore it }
JCXZ @L2 {all bytes done? }
MOV ES:[BX+SI],AL {no, store to screen }
JMP @L1 {short} {work on next byte }
@L2:
END;
PROCEDURE Modus1; ASSEMBLER;
{mode 1 writes the data directly to the screen, without further processing }
ASM
MOV DI,BX {set DI so that the string instructions can be used }
XOR AX,AX {set AX := 0 }
SHR CX,1 {number of words to move }
REP MOVSW {move data as one block at once }
ADC CX,AX {does a single byte remain? }
REP MOVSB
MOV AX,AX {4 filling bytes; faster than 4 NOPs}
MOV AX,AX
END;
PROCEDURE Modus2Work; ASSEMBLER;
{Continuation of mode2 - everything, which didn't fit in the reserved 16 }
{bytes is placed here}
ASM
OUT DX,AX {enable read access for correct plane }
PUSH DS {DS still points to sprite data, but must }
{point to background! }
MOV AX,ES {DS:SI := ES:DI (source ptr:=dest. ptr) }
MOV DS,AX
MOV SI,DI
MOV BX,OFFSET ShadowTab {set pointer to color lookup table }
@L4:
LODSB {get background color... }
SEGCS XLAT {...use color lookup table to transform}
STOSB {...and display on actual graphic page}
LOOP @L4
POP DS
END;
PROCEDURE Modus2; ASSEMBLER;
{mode 2 is thought for "shadows" and the like: the sprite's data itself }
{will be ignored; instead, the background data underneath the sprite's }
{position is read in and these color values will be exchanged against }
{those of the color lookup table "ShadowTab" (e.g.: to realize shadows, }
{this table should hold a darker color for each of the original colors) }
ASM
MOV AX,DI {bring bitplane for read access to AX }
MOV DI,BX {put dest. addr. into DI for 8086's string instructions}
MOV AH,AL {bring bitplane to highbyte }
MOV AL,4
MOV DX,3CEh
MOV SI,OFFSET Modus2Work {sort of hack: "CALL Modus2Work" would be coded }
CALL SI {RELATIVE - and thus, jump to ever-neverland! }
END;
PROCEDURE Modus3Work; ASSEMBLER;
{continuation of Modus3 - everything, which didn't fit into 16 bytes }
{bytes is placed here}
ASM
STC
SBB BX,SI {address source and destination with only 1 index register}
MOV DX,BP {save BP-register }
MOV BP,BX
MOV BX,OFFSET ShadowTab {set pointer to color lookup table }
@L1:
LODSB {get sprite data... }
OR AL,AL { (ignore color 0 as "transparent") }
LOOPZ @L1
JCXZ @L2
MOV AL,ES:[BP+SI] {get background color... }
SEGCS XLAT {...use color lookup table to transform}
MOV ES:[BP+SI],AL {...and display on actual graphic page}
JMP @L1
@L2:
MOV BP,DX {restore old contents of BP register }
END;
PROCEDURE Modus3; ASSEMBLER;
{Modus3 is thought for "shadows", too: in this mode, all sprite }
{pixels with color <>0 will be processed: the background color, which is }
{underneath these pixels will be replaced by the corresponding color entry }
{from the table "ShadowTab" }
{in other words: this mode is the same as mode 2, with the difference, that }
{sprite color 0 is treated as being transparent for shadows! }
ASM
MOV DX,3CEh {prepare access to read plane: }
MOV AX,DI
MOV AH,AL {load read plane into AH }
MOV AL,4
OUT DX,AX {enable read access for correct plane }
INC CX {inc. number of bytes by 1 (-> LODSB!) }
MOV AX,OFFSET Modus3Work {trick to make code relocatible! }
CALL AX
END;
PROCEDURE Adressen; ASSEMBLER;
{table with the starting addresses of the 3 routines in the code segment}
ASM
DW OFFSET Modus0
DW OFFSET Modus1
DW OFFSET Modus2
DW OFFSET Modus3
END;
PROCEDURE GADR; ASSEMBLER;
{table with graphic rows starting addresses (offset part)}
ASM
DW $0000,$0050,$00A0,$00F0,$0140,$0190,$01E0,$0230
DW $0280,$02D0,$0320,$0370,$03C0,$0410,$0460,$04B0
DW $0500,$0550,$05A0,$05F0,$0640,$0690,$06E0,$0730
DW $0780,$07D0,$0820,$0870,$08C0,$0910,$0960,$09B0
DW $0A00,$0A50,$0AA0,$0AF0,$0B40,$0B90,$0BE0,$0C30
DW $0C80,$0CD0,$0D20,$0D70,$0DC0,$0E10,$0E60,$0EB0
DW $0F00,$0F50,$0FA0,$0FF0,$1040,$1090,$10E0,$1130
DW $1180,$11D0,$1220,$1270,$12C0,$1310,$1360,$13B0
DW $1400,$1450,$14A0,$14F0,$1540,$1590,$15E0,$1630
DW $1680,$16D0,$1720,$1770,$17C0,$1810,$1860,$18B0
DW $1900,$1950,$19A0,$19F0,$1A40,$1A90,$1AE0,$1B30
DW $1B80,$1BD0,$1C20,$1C70,$1CC0,$1D10,$1D60,$1DB0
DW $1E00,$1E50,$1EA0,$1EF0,$1F40,$1F90,$1FE0,$2030
DW $2080,$20D0,$2120,$2170,$21C0,$2210,$2260,$22B0
DW $2300,$2350,$23A0,$23F0,$2440,$2490,$24E0,$2530
DW $2580,$25D0,$2620,$2670,$26C0,$2710,$2760,$27B0
DW $2800,$2850,$28A0,$28F0,$2940,$2990,$29E0,$2A30
DW $2A80,$2AD0,$2B20,$2B70,$2BC0,$2C10,$2C60,$2CB0
DW $2D00,$2D50,$2DA0,$2DF0,$2E40,$2E90,$2EE0,$2F30
DW $2F80,$2FD0,$3020,$3070,$30C0,$3110,$3160,$31B0
DW $3200,$3250,$32A0,$32F0,$3340,$3390,$33E0,$3430
DW $3480,$34D0,$3520,$3570,$35C0,$3610,$3660,$36B0
DW $3700,$3750,$37A0,$37F0,$3840,$3890,$38E0,$3930
DW $3980,$39D0,$3A20,$3A70,$3AC0,$3B10,$3B60,$3BB0
DW $3C00,$3C50,$3CA0,$3CF0,$3D40,$3D90,$3DE0,$3E30
DW $3E80
END;
FUNCTION EMSinstalled(VAR PageFrameSeg:WORD):Boolean;
{ in: - }
{out: PageFrameSeg = segment address of EMS-frame }
{ TRUE/FALSE for: EMS installed/not installed }
{ Error, EMSError = evtl. error code}
{rem: If USEEMS=FALSE, the return value will always be FALSE }
TYPE Tag=ARRAY[1..8] OF CHAR;
VAR p:POINTER;
Begin
EMSError:=0;
IF NOT USEEMS
THEN BEGIN
EMSinstalled:=FALSE;
exit
END;
GetIntVec(EMSInt,p);
IF Tag(Ptr(SEG(p^),$A)^)='EMMXXXX0'
THEN BEGIN {EMS-driver exists, but ist must be at least V3.2:}
WITH Regs DO
BEGIN
AH:=$46; Intr(EMSInt,Regs); {fetch version number}
EMSInstalled:=AL>=$32; {version >= 3.2 ? }
AH:=$41; Intr(EMSInt,Regs); {BX=segment address, AH=evtl. error}
PageFrameSeg:=BX;
EmsError:=AH;
IF EmsError<>0 THEN Error:=Err_EMSError;
END;
END
ELSE EMSInstalled:=FALSE
End;
FUNCTION EMSPagesAvailable:WORD;
{ in: - }
{out: returns number of available EMS pages (each one = 16K)}
{ Error, EMSError = evtl. error code}
{rem: only call this routine, if EMSinstalled = TRUE!}
BEGIN
WITH Regs DO
BEGIN
AH:=$42; Intr(EMSInt,Regs); {determine number of available pages}
EMSPagesAvailable:=BX;
EmsError := AH;
IF EmsError<>0 THEN Error:=Err_EMSError;
END;
END;
FUNCTION EMSAllocate(pages:Word):WORD;
{ in: pages = #number of pages to allocate}
{out: handle to this EMS-block }
{ Error, EMSError = evtl. error code}
{rem: pages may not exceed the number of available pages PagesAvail!}
BEGIN
With Regs do
BEGIN
AH:=$43;
BX:=pages;
Intr(EMSInt,Regs);
EmsError := AH;
IF EmsError<>0 THEN Error:=Err_EMSError;
EMSAllocate := DX;
END;
END;
Procedure EMSSwapPageIn(EMSHandle, LogicNr,PhysicalNr:Word);
{ in: EMSHandle = handle to an allocated EMS-block }
{ LogicNr = logical page number within that EMS-block }
{ PhysicalNr = physical page number of the EMS-frame (=0..3) }
{out: Error, EMSError = evtl. error code}
{rem: Maps the logical page "LogicNr" of the EMS-area, which has been }
{ allocated with the handle "EMSHandle" into the physical EMS-page }
{ "PhysicalNr"}
{ Afterwards, accesses of the form MEM[BACKGNDADR:PhysicalNr*$4000] }
{ are possible}
{ PhysicalNr = 0..3}
{ LogicNr = 0..allocated number of pages-1}
BEGIN
With Regs do
BEGIN
AH:=$44; DX:=EMSHandle; BX:=LogicNr; AL:=PhysicalNr;
Intr(EMSInt,Regs);
EmsError := AH;
IF EmsError<>0 THEN Error:=Err_EMSError;
END;
END;
PROCEDURE EMSFillFrame(EMSHandle:WORD);
{ in: EMSHandle = handle to the EMS-frame (4 pages in size)}
{out: EMS-frame-buffer has been filled with the first 4 pages}
{ Error, EMSError = evtl. error code}
CONST a:ARRAY[0..7] OF WORD=(0,0,1,1,2,2,3,3);
BEGIN
With Regs do
BEGIN
AX:=$5000; DX:=EMSHandle; CX:=4;
DS:=Seg(a); SI:=Ofs(a);
Intr(EMSInt,Regs);
EmsError := AH;
IF EmsError<>0 THEN Error:=Err_EMSError;
END;
END;
PROCEDURE EMSRelease(handle:WORD);
{ in: handle = handle to the EMS-block to release }
{out: Error, EMSError = evtl. error code}
{rem: EMS-block has been released}
BEGIN
With Regs do
BEGIN
AH:=$45;
DX:=handle;
Intr(EMSInt,Regs);
EmsError:=AH;
IF EmsError<>0 THEN Error:=Err_EMSError;
END;
END;
FUNCTION EMSIsHardwareEMS:BOOLEAN;
{ in: - }
{out: FALSE, if EMS is only simulated }
{ Error, EMSError = evtl. error code}
{rem: This check is done as suggested in the LIM4.0-specifications: }
{ one logical page is mapped into all physical pages and then the }
{ resulting configuration is checked}
CONST a:ARRAY[0..7] OF WORD=(0,0,0,1,0,2,0,3);
VAR SegAdr,handle:WORD;
mirror:BOOLEAN;
BEGIN
IF NOT(EMSInstalled(SegAdr)) OR (EMSPagesAvailable<1)
THEN EMSIsHardwareEMS:=TRUE {someone having a better idea?}
ELSE BEGIN
handle:=EMSAllocate(1);
With Regs do
BEGIN {map logical page #0 into all 4 physical pages }
AX:=$5000; DX:=handle; CX:=4;
DS:=Seg(a); SI:=Ofs(a);
Intr(EMSInt,Regs);
EmsError := AH;
IF EmsError<>0 THEN Error:=Err_EMSError;
END;
MEM[SegAdr:16383]:=0;
mirror:=(MEM[SegAdr:1*16384+16383] OR
MEM[SegAdr:2*16384+16383] OR
MEM[SegAdr:3*16384+16383])=0;
MEM[SegAdr:16383]:=$FF;
mirror:=mirror AND
((MEM[SegAdr:1*16384+16383] AND
MEM[SegAdr:2*16384+16383] AND
MEM[SegAdr:3*16384+16383])=$FF);
EMSRelease(handle);
EMSIsHardWareEMS:=mirror
END;
END;
PROCEDURE EnsureEMSConsistency(EMSHandle:WORD);
{ in: EMSused = TRUE}
{ EMSHandle = handle of allocated EMS-block }
{ BACKGNDADR:0 = start of EMS-frame }
{out: The EMS-frame is filled with the wanted 64K data}
BEGIN
EMSFillFrame(EMSHandle); {prepare EMS access }
END;
FUNCTION AT:BOOLEAN;
{ in: - }
{out: TRUE/FALSE, if the machine is (at least) an AT }
BEGIN
AT:=MEM[$F000:$FFFE]=$FC
END;
PROCEDURE SetCycleTime(milliseconds:WORD);
{ in: min. time for one animation cycle in milliseconds }
{out: CycleTime := that value in microseconds }
{ TimeFlag := $80}
{rem: Because of TimeFlag:=$80, the timing mechanism won't work }
{ for the very first animation cycle, yet! }
{ If you don't use the timing mechanism (by supplying a value}
{ of 0 milliseconds), this will result in IsAT:=$80, that is }
{ the routine will fake "computer is a PC". Else IsAT=0 }
BEGIN
TimeFlag:=$80;
CycleTime:=LONGINT(milliseconds)*LONGINT(1000);
IF (milliseconds<>0) AND AT
THEN IsAT:=0 {yes, time control mechanism shall be used }
ELSE IsAT:=$80 {no, none possible or not wanted }
END;
PROCEDURE SetSpriteCycle(nr,len:WORD);
{ in: nr = spriteloadnumber of the first sprite in the cycle }
{ len = length of your sprite cycle }
{out: NextSprite[nr] through NextSprite[nr+len-1] have been set }
{ set such that they build a "ring", that is: together }
{ they make up a sprite cycle}
{rem: If the sprite cycle shall consist of (physical) sprites }
{ whose load numbers aren't consecutive, then you }
{ have to make the appropriate entries into NextSprite[] }
{ yourself manually }
{ This routine uses spriteLOADnumbers! }
VAR i:WORD;
BEGIN
IF (nr<1) OR (nr+len-1>LoadMAX)
THEN Error:=Err_InvalidSpriteLoadNumber
ELSE BEGIN
FOR i:=nr TO nr+len-2 DO NextSprite[i]:=SUCC(i);
NextSprite[PRED(nr+len)]:=nr {last sprite points to first one}
END;
END;
FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:WORD):POINTER;
{ in: (x1,y1) = upper left corner of the area which shall be stored }
{ (x2,y2) = according lower right corner (in virtual coordinates!) }
{ pa = graphic page from which the image should be taken (0..3) }
{ StartVirtualX,StartVirtualY = upper left image corner }
{out: pointer to heap address where the copied screen area is stored }
{ left_cut= evtl. needed left cut off of the image (this determines, }
{ how many pixels at the left edge of the image lie outside }
{ of the screen) }
{ right_cut, top_cut, bottom_cut = dto., for other edges }
{ was_cut = TRUE/FALSE, if it was necessary/not necessary to clip the }
{ fetched image }
{rem: The memory needed will be reserved by the routine automatically }
{ If that is impossible (or the image is completely offscreen), the }
{ routine will return NIL! }
{ Only if "was_cut" is set to TRUE, the (global) "..._cut" variables }
{ will be set to something other then 0, that is: if the window lies }
{ _completely_ offscreen (this means: returned ptr=NIL), then the }
{ routine still returns "was_cut"=FALSE!}
VAR len,breite,hoehe,StartAdr,actualAdr,SegmAdr:WORD;
p:POINTER;
BEGIN
was_cut:=FALSE; left_cut:=0; right_cut:=0; top_cut:=0; bottom_cut:=0;
dec(x1,StartVirtualX); {compute screen coordinates }
dec(y1,StartVirtualY);
IF (x1>XMAX) or (y1>YMAX) or (x2<0) or (y2<0) or (x1>x2) or (y1>y2)
THEN BEGIN {window is offscreen }
GetImage:=NIL;
exit
END;
{cut clipping according to visible screen:}
IF x1<0 THEN BEGIN left_cut :=-x1; x1:=0; was_cut:=TRUE END;
IF y1<0 THEN BEGIN top_cut:=-y1; y1:=0; was_cut:=TRUE END;
IF x2>XMAX THEN BEGIN right_cut :=x2-XMAX; x2:=XMAX; was_cut:=TRUE END;
IF y2>YMAX THEN BEGIN bottom_cut:=y2-YMAX; y2:=YMAX; was_cut:=TRUE END;
breite:=SUCC(x2-x1); hoehe:=SUCC(y2-y1);
len:=breite*hoehe+2*2; {1 pixel = 1 byte; add 2 words for width & height }
IF len>MaxAvail
THEN BEGIN
Error:=Err_NotEnoughMemory;
GetImage:=NIL;
exit
END;
IF (pa<0) OR (pa>SCROLLPAGE) {page number must be 0..3 }
THEN BEGIN
Error:=Err_InvalidPageNumber;
GetImage:=NIL;
exit
END
ELSE SegmAdr:=Segment_Adr[pa];
GetMem(p,len); {get memory from the heap }
IF pa<>BACKGNDPAGE
THEN ASM {VRAM to RAM }
CLD
LES DI,p {ES:DI = pointer to the acquired memory }
MOV AX,breite
STOSW {store width first... }
MOV AX,hoehe
STOSW {...then store height, followed by the data }
MOV BX,AX {BX := hoehe (to be used later) }
MOV SI,y1
SHL SI,1
MOV SI,CS:[OFFSET gadr + SI] {SI := y1 * LINESIZE}
MOV AX,x1
MOV DL,AL
SHR AX,1
SHR AX,1
ADD SI,AX {SI := offset part of the start address}
MOV StartAdr,SI
MOV actualAdr,SI
AND DL,3
MOV AH,DL
MOV AL,4
MOV DX,3CEh
OUT DX,AX {select start plane }
MOV DS,SegmAdr
{DS:SI = pointer to first byte to store; ES:DI = its target address }
{AH = startplane, AL = 4, BX = number of rows to process }
MOV DX,breite
ADD DX,3
SHR DX,1
SHR DX,1 {DX = number of bytes to store for each row}
@L1:
MOV CX,DX {store data of one row }
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
MOV SI,actualAdr {increment source pointer by 1 graphic row}
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX {decrease row counter }
JNE @L1
INC AH {select next plane }
CMP AH,4
JNE @nowrap1 {wrapping the bitplane means: start address }
MOV AH,0 {needs mending: increment address by 1! }
INC StartAdr
@nowrap1:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
INC DX
INC DX
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L2:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L2
INC AH
CMP AH,4
JNE @nowrap2
MOV AH,0
INC StartAdr
@nowrap2:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
INC DX
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L3:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L3
INC AH
CMP AH,4
JNE @nowrap3
MOV AH,0
INC StartAdr
@nowrap3:
MOV DX,3CEh
OUT DX,AX
MOV BX,hoehe
MOV DX,breite
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L4:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L4
MOV AX,SEG @DATA
MOV DS,AX
END
ELSE BEGIN
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
ASM {RAM to RAM }
CLD
LES DI,p {ES:DI = pointer to the acquired memory }
MOV AX,breite
STOSW {store width first... }
MOV AX,hoehe
STOSW {...then store height, followed by the data }
MOV DX,AX {DX := hoehe (to be used later) }
MOV SI,y1
SHL SI,1
MOV SI,CS:[OFFSET gadr + SI] {SI := y1 * LINESIZE}
MOV AX,x1
MOV BL,AL
SHR AX,1
SHR AX,1
ADD SI,AX {SI := offset part of the start address}
AND BX,3
MOV AH,BL
SHL BX,1
ADD SI,[OFFSET BACKTab + BX] {select start plane }
MOV StartAdr,SI
MOV actualAdr,SI
MOV DS,SegmAdr
MOV BX,DX
{DS:SI = pointer to first byte to store; ES:DI = its target address }
{AH = startplane, BX = number of rows to process }
MOV DX,breite
ADD DX,3
SHR DX,1
SHR DX,1 {DX = number of bytes to store for each row}
@L1:
MOV CX,DX {store data of one row }
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
MOV SI,actualAdr {increment source pointer by 1 graphic row}
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX {decrease row counter }
JNE @L1
INC AH {select next plane }
ADD StartAdr,PAGESIZE
CMP AH,4
JNE @nowrap1 {wrapping the bitplane means: start address }
MOV AH,0 {needs mending: increment address by 1! }
SUB StartAdr,4*PAGESIZE -1
@nowrap1:
MOV BX,hoehe
MOV DX,breite
INC DX
INC DX
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L2:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L2
INC AH
ADD StartAdr,PAGESIZE
CMP AH,4
JNE @nowrap2
MOV AH,0
SUB StartAdr,4*PAGESIZE -1
@nowrap2:
MOV BX,hoehe
MOV DX,breite
INC DX
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L3:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L3
INC AH
ADD StartAdr,PAGESIZE
CMP AH,4
JNE @nowrap3
MOV AH,0
SUB StartAdr,4*PAGESIZE -1
@nowrap3:
MOV BX,hoehe
MOV DX,breite
SHR DX,1
SHR DX,1
MOV SI,StartAdr
MOV actualAdr,SI
@L4:
MOV CX,DX
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
MOV SI,actualAdr
ADD SI,LINESIZE
MOV actualAdr,SI
DEC BX
JNE @L4
MOV AX,SEG @DATA
MOV DS,AX
END;
END;
GetImage:=p
END;
PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:WORD);
{ in: (x,y) = upper left corner of destination (virtual coordinates) }
{ p = pointer to the cutting (returned by GetImage) }
{ pa = graphic page to which the cutting shall be pasted to }
{ StartVirtualX,StartVirtualY = upper left image corner }
{out: - }
{rem: The cutting has been properly clipped before being displayed }
{ If you supply NIL as pointer, the routine will display nothing }
{ That is useful when you are going to use the routine directly on }
{ the return value of GetImage!}
VAR breite,hoehe,SegmAdr,actualAdr,StartAdr,breite1,breite2,breite3,breite4,
licut_div4,topcut,pl_adr1,pl_adr2,pl_adr3,pl_adr4:WORD;
licutoff,temp:INTEGER;
BEGIN
IF p=NIL THEN exit;
dec(x,StartVirtualX); {compute screen coordinates }
dec(y,StartVirtualY);
IF (x>XMAX) or (y>YMAX) THEN exit;
IF (pa<0) OR (pa>SCROLLPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber;
exit
END
ELSE SegmAdr:=Segment_Adr[pa];
breite:=MEMW[SEG(p^):OFS(p^)];
hoehe :=MEMW[SEG(p^):OFS(p^)+2];
IF (x+breite<=0) or (y+hoehe<=0) THEN exit;
IF x<0 THEN BEGIN licutoff:=-x; x:=0 END
ELSE licutoff:=0;
IF y<0 THEN BEGIN
topcut:=-y;
y:=0
END
ELSE topcut:=0;
breite1:=(breite + 3) shr 2; {Width of a row for the first, second, }
breite2:=(breite + 2) shr 2; {third and fourth bitplane, respectively}
breite3:=(breite + 1) shr 2;
breite4:=(breite + 0) shr 2;
{Compute starting addresses of the 4 bitplanes; take into account evtl. }
{left cutoff (+4 bytes to jump over "breite" (width) and "hoehe" (height) }
licut_div4:=licutoff shr 2;
pl_adr1:=4 +licut_div4 +topcut*breite1;
pl_adr2:=4 +licut_div4 +topcut*breite2 +hoehe*breite1;
pl_adr3:=4 +licut_div4 +topcut*breite3 +hoehe*(breite1+breite2);
pl_adr4:=4 +licut_div4 +topcut*breite4 +hoehe*(breite1+breite2+breite3);
{licutoff mod 4 determines the order in which the points must be read }
{of the heap: 0 = plane order (1,2,3,4); 1 = plane order (2,3,4,1); }
{note that the widths of the bitplane tables }
{are (and remain) linked to these; therefore, they will be swapped }
{to accomplish that! }
ASM
CLD
MOV AX,licutoff
AND AL,3
OR AL,AL
JE @no_exchange
CMP AL,1
JNE @L10
MOV AX,pl_adr2 {displacement of 1 bit: }
MOV BX,pl_adr3 {AX = Plane2, BX = Plane3, CX = Plane4, DX = Plane1}
MOV CX,pl_adr4
MOV DX,pl_adr1 {wrap-around, thus: increment address by 1, which }
INC DX {corresponds to an ajustment of 4 points }
MOV pl_adr1,AX {(e.g.: pixels (1,5,9,...),(2,6,10,...),(3,7,11,...)}
MOV pl_adr2,BX {and (0,4,8,...); the last bitplane needs a cor- }
MOV pl_adr3,CX {rection of +1 byte: this results in (4,8,12,...) }
MOV pl_adr4,DX {(read planes top-down, in alternating order!) }
MOV AX,breite2 {Now the plane widths: }
MOV BX,breite3 {AX = Plane2, BX = Plane3, CX = Plane4, DX = Plane1}
MOV CX,breite4
MOV DX,breite1
JMP @store
@L10:
CMP AL,2
JNE @L20
MOV AX,pl_adr3 {displacement of 2 bit: }
MOV BX,pl_adr4 {AX = Plane3, BX = Plane4, CX = Plane1, DX = Plane2}
MOV CX,pl_adr1
INC CX
MOV DX,pl_adr2
INC DX
MOV pl_adr1,AX
MOV pl_adr2,BX
MOV pl_adr3,CX
MOV pl_adr4,DX
MOV AX,breite3 {dto. for plane widths: }
MOV BX,breite4 {AX = Plane3, BX = Plane4, CX = Plane1, DX = Plane2}
MOV CX,breite1
MOV DX,breite2
JMP @store
@L20:
MOV AX,pl_adr4 {displacement of 3 bit: }
MOV BX,pl_adr1 {AX = Plane4, BX = Plane1, CX = Plane2, DX = Plane3}
INC BX
MOV CX,pl_adr2
INC CX
MOV DX,pl_adr3
INC DX
MOV pl_adr1,AX
MOV pl_adr2,BX
MOV pl_adr3,CX
MOV pl_adr4,DX
MOV AX,breite4 {dto. for plane widths: }
MOV BX,breite1 {AX = Plane4, BX = Plane1, CX = Plane2, DX = Plane3}
MOV CX,breite2
MOV DX,breite3
@store:
MOV breite1,AX
MOV breite2,BX
MOV breite3,CX
MOV breite4,DX
@no_exchange: {precondition here: (pl_adr?,breite?) contain the }
{source bitplanes/-widths in the correct order }
MOV AX,topcut
SUB hoehe,AX {evtl. adjust height for upper cutoff }
MOV AX,licutoff
SUB breite,AX {dto. for width and left cutoff }
MOV AX,x {if image would spread over the right screen }
ADD AX,breite {boundary: compute right cutoff }
SUB AX,XMAX+1
JLE @no_recutoff
SUB breite,AX {cut off AX points at the right}
@no_recutoff:
MOV AX,y {exactly the same for the lower screen border}
ADD AX,hoehe
SUB AX,YMAX+1
JLE @no_bocutoff
SUB hoehe,AX {cut off AX rows at the bottom}
@no_bocutoff:
END;
IF pa<>BACKGNDPAGE
THEN ASM {RAM to VRAM }
LDS SI,p
ADD pl_adr2,SI {add pointer's offset part to the plane address }
ADD pl_adr3,SI
ADD pl_adr4,SI
ADD SI,pl_adr1 {width, height and parts above the screen }
MOV ES,SegmAdr
MOV DI,y
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI := y * LINESIZE}
MOV AX,x
MOV BL,AL
SHR AX,1
SHR AX,1
ADD DI,AX {DI := y * LINESIZE + (x DIV 4)}
MOV StartAdr,DI
MOV actualAdr,DI
AND BX,3 {startplane := x mod 3}
MOV AH,CS:[OFFSET CS_TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
OUT DX,AX {use it as write plane }
MOV DX,hoehe
MOV DI,actualAdr
{DS:SI = pointer to data, ES:DI = dest. address on screen for them }
{AH = bitmask for access, AL = 2 }
MOV BX,breite
ADD BX,3
SHR BX,1
SHR BX,1
mov cx,bx
@L1:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
pop si
mov cx,bx
add si,breite1
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L1
SHL AH,1 {select next bitplane; if wrap-around occurs }
CMP AH,16 {from bitplane 3 to bitplane 0, then the start-}
JNE @nowrap1 {ing address must be incremented by 1 byte }
MOV AH,1
INC StartAdr
@nowrap1:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr2
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
INC BX
INC BX
SHR BX,1
SHR BX,1
mov cx,bx
@L2:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
pop si
mov cx,bx
add si,breite2
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L2
SHL AH,1
CMP AH,16
JNE @nowrap2
MOV AH,1
INC StartAdr
@nowrap2:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr3
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
INC BX
SHR BX,1
SHR BX,1
mov cx,bx
@L3:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
pop si
mov cx,bx
add si,breite3
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L3
SHL AH,1
CMP AH,16
JNE @nowrap3
MOV AH,1
INC StartAdr
@nowrap3:
MOV DX,3C4h
OUT DX,AX
MOV SI,pl_adr4
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
SHR BX,1
SHR BX,1
mov cx,bx
@L4:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
pop si
mov cx,bx
add si,breite4
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L4
MOV AX,SEG @DATA
MOV DS,AX
END
ELSE BEGIN
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
ASM {RAM to RAM }
MOV AX,DS
MOV ES,AX {ES := old data segment }
LDS SI,p
ADD pl_adr2,SI {add pointer's offset part to the plane address }
ADD pl_adr3,SI
ADD pl_adr4,SI
ADD SI,pl_adr1 {width, height and parts above the screen }
MOV DI,y
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI := y * LINESIZE}
MOV AX,x
MOV BL,AL
SHR AX,1
SHR AX,1
ADD DI,AX {DI := y * LINESIZE + (x DIV 4)}
AND BX,3 {startplane := x mod 3}
MOV AL,BL {store copy to AL }
SHL BX,1
ADD DI,ES:[OFFSET BACKTab + BX] {use it as write plane }
MOV StartAdr,DI
MOV actualAdr,DI
MOV DX,hoehe
MOV DI,actualAdr
MOV ES,SegmAdr
{DS:SI = pointer to data, ES:DI = dest. address on screen for them }
{AL = startplane}
MOV BX,breite
ADD BX,3
SHR BX,1
SHR BX,1
mov cx,bx
@L1:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
pop si
mov cx,bx
add si,breite1
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L1
INC AL {select next bitplane; if wrap-around occurs }
ADD StartAdr,PAGESIZE {from bitplane 3 to bitplane 0, then the start-}
AND AL,3 {ing address must be incremented by 1 byte }
JNE @nowrap1
SUB StartAdr,4*PAGESIZE-1
@nowrap1:
MOV SI,pl_adr2
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
INC BX
INC BX
SHR BX,1
SHR BX,1
mov cx,bx
@L2:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
pop si
mov cx,bx
add si,breite2
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L2
INC AL {select next bitplane; if wrap-around occurs }
ADD StartAdr,PAGESIZE {from bitplane 3 to bitplane 0, then the start-}
AND AL,3 {ing address must be incremented by 1 byte }
JNE @nowrap2
SUB StartAdr,4*PAGESIZE-1
@nowrap2:
MOV SI,pl_adr3
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
INC BX
SHR BX,1
SHR BX,1
mov cx,bx
@L3:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
pop si
mov cx,bx
add si,breite3
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L3
INC AL {select next bitplane; if wrap-around occurs }
ADD StartAdr,PAGESIZE {from bitplane 3 to bitplane 0, then the start-}
AND AL,3 {ing address must be incremented by 1 byte }
JNE @nowrap3
SUB StartAdr,4*PAGESIZE-1
@nowrap3:
MOV SI,pl_adr4
MOV DI,StartAdr
MOV actualAdr,DI
MOV DX,hoehe
MOV BX,breite
SHR BX,1
SHR BX,1
mov cx,bx
@L4:
push si
SHR CX,1 {faster than "REP MOVSB" }
REP MOVSW
ADC CX,CX
REP MOVSB
pop si
mov cx,bx
add si,breite4
MOV DI,actualAdr
ADD DI,LINESIZE
MOV actualAdr,DI
DEC DX
JNE @L4
MOV AX,SEG @DATA
MOV DS,AX
END
END;
END;
PROCEDURE FreeImageMem(p:POINTER);
{ in: p = pointer to image memory, allocated by GetImage()}
{out: - }
{rem: the heap memory allocated for the image has been released }
BEGIN
IF p<>NIL THEN FreeMem(p,MEMW[Seg(p^):Ofs(p^)]*MEMW[Seg(p^):Ofs(p^)+2] + 2*2)
END;
PROCEDURE Screen(pa:WORD);
{ in: pa = screen page to display (0..1) }
{out: - }
{rem: The display has been switched to graphic page pa }
{ The routine does NOT synchronize on any retrace-signal }
{ Sensible page values are only 0 or 1 here, but the routine }
{ doesn't make any checks!}
BEGIN
ASM
MOV DX,CRTAddress {CRT-Controller}
MOV AL,$0D {LB-startaddress-register}
CLI {May not be interrupted! }
OUT DX,AL
INC DX
{realize "AX := Offset_Adr[pa]": }
MOV BX,pa
MOV SI,BX
AND SI,3 {page value *2 (word-sized entries!)}
SHL SI,1 {add start address of array to that }
ADD SI,OFFSET Offset_Adr-StartIndex*2 {evtl. correct displacement }
LODSW {and fetch value}
OUT DX,AL {set LB of new starting address }
DEC DX
MOV AL,$0C
OUT DX,AL
INC DX
MOV AL,AH {set HB of new starting address }
OUT DX,AL
STI
END;
END;
PROCEDURE InitGraph;
{ in: PAGE = actual graphic page }
{out: - }
{rem: switches the VGA-card into 320x200x256x4-mode; ATTENTION! }
{ This mode is different from mode $13 of the VGA-BIOS!!! }
{ The display will be switched to graphic page 1-PAGE }
{ The default colors of mode $13 will be set! }
BEGIN
ASM
MOV AX,0013h {use BIOS to set graphic mode $13 (320x200x256) }
INT 10h
MOV DX,03C4h {select memory-mode-register at sequencer port }
MOV AL,04
OUT DX,AL
INC DX {read in data via the according data register }
IN AL,DX
AND AL,0F7h {bit 3 := 0: don't chain the 4 planes}
OR AL,04 {bit 2 := 1: no odd/even mechanism }
OUT DX,AL {activate new settings }
MOV DX,03C4h {s.a.: address sequencer reg. 2 (=map-mask),... }
MOV AL,02
OUT DX,AL
INC DX
MOV AL,0Fh {...and allow access to all 4 bit maps }
OUT DX,AL
MOV AX,0A000h {starting with segment A000h, set 8000h logical }
MOV ES,AX {words = 4*8000h physical words (because of 4 }
SUB DI,DI {bitplanes) to 0 }
MOV AX,DI
MOV CX,8000h
CLD
REP STOSW
MOV DX,CRTAddress {address the underline-location-register at }
MOV AL,14h {the CRT-controller port, read out the according }
OUT DX,AL {data register: }
INC DX
IN AL,DX
AND AL,0BFh {bit 6:=0: no double word addressing scheme in}
OUT DX,AL {video RAM }
DEC DX
MOV AL,17h {select mode control register }
OUT DX,AL
INC DX
IN AL,DX
OR AL,40h {bit 6 := 1: memory access scheme=linear bit array }
OUT DX,AL
END;
Screen(1-PAGE); {ALWAYS is the non-actual graphic page the visible one!}
SetPalette(DefaultColors,FALSE) {set default palette, just to be sure! }
END;
VAR d,dp,dq,WindowY2:INTEGER;
PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
{ in: x1,y1,x2,y2 = coordinates of two points, }
{ Color = color (0..255) }
{ StartVirtualX,StartVirtualY = upper left image corner }
{ WinClip = TRUE, if line shall be clipped to the window area }
{ WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
{out: - }
{rem: A line has been drawn between the VIRTUAL points (x1,y1) and (x2,y2) }
{ using the color COLOR; the routine will take care of transforming }
{ the coordinates to absolute screen coordinates and evtl. necessary }
{ clipping actions. }
{ The line will NOT automatically be taken over into the background }
{ image, that is: it will be visible only for one animation cycle (if }
{ it shall stay permanent, you have to draw it into the background!) }
{ (For that reason, you should call this routine AFTER calling ANIMATE }
{ because otherwise, the drawn line will vanish at once!) }
CONST CodeLinks =$7; {%0111}
CodeRechts=$B; {%1011}
CodeOben =$D; {%1101}
CodeUnten =$E; {%1110}
BEGIN
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
{first clip line to visible window; use Sutherland-Cohen-algorithm: }
{use 4 bit-codes for: left|right|top|bottom }
ASM
CLD
XOR BX,BX
MOV SI,XMAX
XOR DI,DI
MOV AX,YMAX
CMP WinClip,FALSE
JE @Start
MOV BX,WinXMIN
MOV SI,WinXMAX
MOV DI,WinYMIN
MOV AX,WinYMAX
@Start:
MOV WindowY2,AX
{BX|SI|DI|WindowY2 = WinXMIN|WinXMAX|WinYMIN|WinYMAX or 0|XMAX|0|YMAX }
MOV CL,$F {start with %1111 }
MOV AX,x2
SUB AX,StartVirtualX {transform x2 into absolute coordinates}
MOV x2,AX
CMP AX,BX {x2 < WindowX1 ?}
JL @GC1Punkt2 {yes, don't change flag for "point is left of window"}
AND CL,CodeLinks {no, reset flag }
@GC1Punkt2:
CMP AX,SI {x2 > WindowX2 ?}
JG @GC2Punkt2 {yes, don't change flag for "point is right of window"}
AND CL,CodeRechts {no, reset flag }
@GC2Punkt2:
MOV AX,y2
SUB AX,StartVirtualY {transform y2 into absolute coordinates}
MOV y2,AX
CMP AX,DI {y2 < WindowY1 ?}
JL @GC3Punkt2 {yes, don't change flag for "point is above window"}
AND CL,CodeOben {no, reset flag }
@GC3Punkt2:
CMP AX,WindowY2 {y2 > WindowY2 ?}
JG @GC4Punkt2 {yes, don't change flag for "point is below window"}
AND CL,CodeUnten
@GC4Punkt2: {CL holds the area code for point 2 }
MOV AX,x1
SUB AX,StartVirtualX {transform x1 into absolute coordinates}
MOV x1,AX
MOV AX,y1
SUB AX,StartVirtualY {transform y1 into absolute coordinates}
MOV y1,AX
@Punkt1:
MOV CH,$F {start with %1111 }
MOV AX,x1
CMP AX,BX {x1 < WindowX1 ?}
JL @GC1Punkt1 {yes, don't change flag for "point is left of window"}
AND CH,CodeLinks {no, reset flag }
@GC1Punkt1:
CMP AX,SI {x1 > WindowX2 ?}
JG @GC2Punkt1 {yes, don't change flag for "point is right of window"}
AND CH,CodeRechts {no, reset flag }
@GC2Punkt1:
MOV AX,y1
CMP AX,DI {y1 < WindowY1 ?}
JL @GC3Punkt1 {yes, don't change flag for "point is above window"}
AND CH,CodeOben {no, reset flag }
@GC3Punkt1:
CMP AX,WindowY2 {y1 > WindowY2 ?}
JG @GC4Punkt1 {yes, don't change flag for "point is below window"}
AND CH,CodeUnten
@GC4Punkt1: {CH holds the area code for point 1 }
{CL holds the area code for point 2, CH the one for point 1}
MOV AX,CX
AND AL,AH {Code1 AND Code2 <> 0 ?}
JNZ @LineReady {yes, line is completely outside the window}
MOV AX,CX
OR AL,AH {Code1 OR Code2 = 0 ?}
JZ @DrawLine {yes, line is completely inside the window}
{Now do the clipping itself: }
MOV AX,CX
OR AH,AH {Code1 =0 ?}
JNZ @CL3 {no, everything ok}
MOV AX,x1 {yes, swap points! }
XCHG AX,x2
MOV x1,AX
MOV AX,y1
XCHG AX,y2
MOV y1,AX
XCHG CL,CH
@CL3:
MOV AX,x2
SUB AX,x1
MOV dp,AX {dp := x2 - x1}
MOV AX,y2
SUB AX,y1
MOV dq,AX {dq := y2 - y1}
MOV AL,CH {AL := Code1}
TEST AL,NOT CodeLinks {point1 left of window? }
JZ @CL4 {no }
{yes, compute new coordinates: }
{ y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX1 - X1) }
{ and x1 := WindowX1}
MOV AX,BX
SUB AX,x1 {AX := WindowX1-x1}
IMUL dq
IDIV dp
ADD y1,AX
MOV x1,BX
JMP @Punkt1
@CL4:
TEST AL,NOT CodeRechts {point1 right of window? }
JZ @CL5 {no }
{yes, compute:}
{ y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX2 - X1), x1 := WindowX2}
MOV AX,SI {SI = WindowX2}
SUB AX,x1
IMUL dq
IDIV dp
ADD y1,AX
MOV x1,SI
JMP @Punkt1
@CL5:
TEST AL,NOT CodeOben {point1 above window? }
JZ @CL6 {no }
{yes, compute:}
{ x1 := x1 + (x2 - x1) / (y2 - y1) * (WindowY1 - y1), y1 := WindowY1 }
MOV AX,DI {DI = WindowY1}
SUB AX,y1
IMUL dp
IDIV dq
ADD x1,AX
MOV y1,DI
JMP @Punkt1
@CL6:
TEST AL,NOT CodeUnten {point below window? }
JZ @Punkt1 {no }
{yes, compute:}
{ x1 := x1 + (x2 - x1) / (y2 - y1) * (WindowY2 - y1), y1 := WindowY2 }
MOV AX,WindowY2
PUSH AX
SUB AX,y1
IMUL dp
IDIV dq
ADD x1,AX
POP AX
MOV y1,AX
JMP @Punkt1
{precondition here: both points have been clipped to the visible window;}
{if the line is completely offscreen, the program jumped directly to }
{@LineReady, instead! }
@DrawLine:
MOV DX,x1
SUB DX,x2
JGE @posdx
NEG DX
@posdx:
MOV AX,y1
SUB AX,y2
JGE @posdy
NEG AX
@posdy:
{AX = new deltaY, DX = new deltaX }
XOR CX,CX
CMP AX,DX
JBE @noswap1
XCHG AX,DX
INC CX
@noswap1:
{AX = deltaY, DX = deltaX (if CX=0), AX = deltaX, DX = deltaY (if CX=1) }
SHL AX,1
MOV dp,AX
SUB AX,DX
MOV d,AX
SUB AX,DX
MOV dq,AX
JCXZ @then
JMP @else
@then:
MOV CX,x2
MOV AX,x1
MOV DX,y1
MOV BX,y2
CMP AX,CX
JBE @noswap2
XCHG AX,CX
XCHG DX,BX
@noswap2:
{AX = new X1, CX = new X2, DX = new Y1, BX = new Y2 }
SUB CX,AX
INC CX {CX := x2 - x1 + 1 }
MOV SI,LINESIZE
CMP DX,BX
JBE @okay1
NEG SI
@okay1:
{SI = ±LINESIZE, CX = #pixel, AX = X1, DX = Y1}
MOV DI,DX
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI := y1 * LINESIZE}
MOV BL,AL
SHR AX,1
SHR AX,1
ADD DI,AX {DI := y1 * LINESIZE + (x1 DIV 4) }
AND BX,3 {BX := (x1 AND 4) }
SHL BX,1 {*2, because word-sized}
ADD DI,[OFFSET BACKTab + BX] {get mask for access: BX * 16000 }
MOV BL,BACKGNDPAGE {BH = 0 -> BX = drawing page}
SHL BX,1
MOV ES,[BX + OFFSET Segment_Adr -StartIndex*2] {ES:DI=pointer to 1.pixel}
MOV AL,Color
MOV DX,d
MOV BX,dq
@loop1:
MOV ES:[DI],AL
ADD DI,PAGESIZE
JC @wrap1
CMP DI,4*PAGESIZE
JB @nowrap1
@wrap1:
SUB DI,4*PAGESIZE-1 {back in plane0, but 1 byte down }
@nowrap1: {AL = color, SI = ±LINESIZE, ES:DI=pointer to 1.pixel}
{BX = dq, DX = d}
OR DX,DX
JGE @newline
ADD DX,dp
LOOP @loop1
JMP @raus
@newline:
ADD DI,SI
ADD DX,BX
LOOP @loop1
JMP @raus
@else:
MOV CX,y2
MOV AX,y1
MOV DX,x1
MOV BX,x2
CMP AX,CX
JBE @noswap3
XCHG AX,CX
XCHG DX,BX
@noswap3:
{AX = new Y1, BX = new X2, CX = new Y2, DX = new X1 }
SUB CX,AX
INC CX
MOV SI,PAGESIZE
CMP DX,BX
JBE @okay2
NEG SI
@okay2:
{SI = ±PAGESIZE, CX = #pixel, DX = X1, AX = Y1}
MOV DI,AX
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI := y1 * LINESIZE}
MOV BL,DL
SHR DX,1
SHR DX,1
ADD DI,DX {DI := y1 * LINESIZE + (x1 DIV 4) }
AND BX,3 {BX := (x1 AND 4) }
SHL BX,1 {*2, because word-sized}
ADD DI,[OFFSET BACKTab + BX] {get mask for access: BX * 16000 }
MOV BL,BACKGNDPAGE {BH = 0 -> BX = drawing page}
SHL BX,1
MOV ES,[BX + OFFSET Segment_Adr -StartIndex*2] {ES:DI=pointer to 1.pixel}
MOV AL,Color
MOV DX,d
MOV BX,dq
@loop2:
MOV ES:[DI],AL
ADD DI,LINESIZE
OR DX,DX
JGE @newcolumn
ADD DX,dp
LOOP @loop2
JMP @raus
@newcolumn:
OR SI,SI
JGE @plus
{increment SI<0, check for underflow: }
ADD DI,SI
JC @nowrap2
ADD DI,4*PAGESIZE-1
JMP @nowrap2
@plus:
{increment SI>0, check vor overflow & >= 4*PAGESIZE }
ADD DI,SI
JC @wrap2
CMP DI,4*PAGESIZE
JB @nowrap2
@wrap2:
SUB DI,4*PAGESIZE-1
@nowrap2:
ADD DX,BX
LOOP @loop2
JMP @raus
@raus:
@LineReady:
END;
END;
PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:WORD);
{ in: x1,y1,x2,y2 = coordinates of two points, }
{ Color = color (0..255) }
{ StartVirtualX,StartVirtualY = upper left image corner }
{ pa = graphic page to be drawn upon (0..3) }
{ WinClip = TRUE, if line shall be clipped to the window area }
{ WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
{out: - }
{rem: A line has been drawn between the VIRTUAL points (x1,y1) and (x2,y2) }
{ using the color COLOR; the routine will take care of transforming }
{ the coordinates to absolute screen coordinates and evtl. necessary }
{ clipping actions. }
{ The line will NOT automatically be taken over into the background }
{ image, that is: it will be visible only for one animation cycle (if }
{ it shall stay permanent, you have to draw it into the background!) }
{ (For that reason, you should call this routine AFTER calling ANIMATE }
{ because otherwise, the drawn line will vanish at once!) }
CONST CodeLinks =$7; {%0111}
CodeRechts=$B; {%1011}
CodeOben =$D; {%1101}
CodeUnten =$E; {%1110}
BEGIN
IF (pa<0) OR (pa>SCROLLPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE IF pa=BACKGNDPAGE
THEN BackgroundLine(x1,y1,x2,y2)
ELSE
{first clip line to visible window; use Sutherland-Cohen-algorithm: }
{use 4 bit-codes for: left|right|top|bottom }
ASM
CLD
XOR BX,BX
MOV SI,XMAX
XOR DI,DI
MOV AX,YMAX
CMP WinClip,FALSE
JE @Start
MOV BX,WinXMIN
MOV SI,WinXMAX
MOV DI,WinYMIN
MOV AX,WinYMAX
@Start:
MOV WindowY2,AX
{BX|SI|DI|WindowY2 = WinXMIN|WinXMAX|WinYMIN|WinYMAX or 0|XMAX|0|YMAX }
MOV CL,$F {start with %1111 }
MOV AX,x2
SUB AX,StartVirtualX {transform x2 into absolute coordinates}
MOV x2,AX
CMP AX,BX {x2 < WindowX1 ?}
JL @GC1Punkt2 {yes, don't change flag for "point is left of window"}
AND CL,CodeLinks {no, reset flag }
@GC1Punkt2:
CMP AX,SI {x2 > WindowX2 ?}
JG @GC2Punkt2 {yes, don't change flag for "point is right of window"}
AND CL,CodeRechts {no, reset flag }
@GC2Punkt2:
MOV AX,y2
SUB AX,StartVirtualY {transform y2 into absolute coordinates}
MOV y2,AX
CMP AX,DI {y2 < WindowY1 ?}
JL @GC3Punkt2 {yes, don't change flag for "point is above window"}
AND CL,CodeOben {no, reset flag }
@GC3Punkt2:
CMP AX,WindowY2 {y2 > WindowY2 ?}
JG @GC4Punkt2 {yes, don't change flag for "point is below window"}
AND CL,CodeUnten
@GC4Punkt2: {CL holds the area code for point 2 }
MOV AX,x1
SUB AX,StartVirtualX {transform x1 into absolute coordinates}
MOV x1,AX
MOV AX,y1
SUB AX,StartVirtualY {transform y1 into absolute coordinates}
MOV y1,AX
@Punkt1:
MOV CH,$F {start with %1111 }
MOV AX,x1
CMP AX,BX {x1 < WindowX1 ?}
JL @GC1Punkt1 {yes, don't change flag for "point is left of window"}
AND CH,CodeLinks {no, reset flag }
@GC1Punkt1:
CMP AX,SI {x1 > WindowX2 ?}
JG @GC2Punkt1 {yes, don't change flag for "point is right of window"}
AND CH,CodeRechts {no, reset flag }
@GC2Punkt1:
MOV AX,y1
CMP AX,DI {y1 < WindowY1 ?}
JL @GC3Punkt1 {yes, don't change flag for "point is above window"}
AND CH,CodeOben {no, reset flag }
@GC3Punkt1:
CMP AX,WindowY2 {y1 > WindowY2 ?}
JG @GC4Punkt1 {yes, don't change flag for "point is below window"}
AND CH,CodeUnten
@GC4Punkt1: {CH holds the area code for point 1 }
{CL holds the area code for point 2, CH the one for point 1}
MOV AX,CX
AND AL,AH {Code1 AND Code2 <> 0 ?}
JNZ @LineReady {yes, line is completely outside the window}
MOV AX,CX
OR AL,AH {Code1 OR Code2 = 0 ?}
JZ @DrawLine {yes, line is completely inside the window}
{Now do the clipping itself: }
MOV AX,CX
OR AH,AH {Code1 =0 ?}
JNZ @CL3 {no, everything ok}
MOV AX,x1 {yes, swap points! }
XCHG AX,x2
MOV x1,AX
MOV AX,y1
XCHG AX,y2
MOV y1,AX
XCHG CL,CH
@CL3:
MOV AX,x2
SUB AX,x1
MOV dp,AX {dp := x2 - x1}
MOV AX,y2
SUB AX,y1
MOV dq,AX {dq := y2 - y1}
MOV AL,CH {AL := Code1}
TEST AL,NOT CodeLinks {point1 left of window? }
JZ @CL4 {no }
{yes, compute new coordinates: }
{ y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX1 - X1) }
{ and x1 := WindowX1}
MOV AX,BX
SUB AX,x1 {AX := WindowX1-x1}
IMUL dq
IDIV dp
ADD y1,AX
MOV x1,BX
JMP @Punkt1
@CL4:
TEST AL,NOT CodeRechts {point1 right of window? }
JZ @CL5 {no }
{yes, compute:}
{ y1 := y1 + (y2 - y1) / (x2 - x1) * (WindowX2 - X1), x1 := WindowX2}
MOV AX,SI {SI = WindowX2}
SUB AX,x1
IMUL dq
IDIV dp
ADD y1,AX
MOV x1,SI
JMP @Punkt1
@CL5:
TEST AL,NOT CodeOben {point1 above window? }
JZ @CL6 {no }
{yes, compute:}
{ x1 := x1 + (x2 - x1) / (y2 - y1) * (WindowY1 - y1), y1 := WindowY1 }
MOV AX,DI {DI = WindowY1}
SUB AX,y1
IMUL dp
IDIV dq
ADD x1,AX
MOV y1,DI
JMP @Punkt1
@CL6:
TEST AL,NOT CodeUnten {point below window? }
JZ @Punkt1 {no }
{yes, compute:}
{ x1 := x1 + (x2 - x1) / (y2 - y1) * (WindowY2 - y1), y1 := WindowY2 }
MOV AX,WindowY2
PUSH AX
SUB AX,y1
IMUL dp
IDIV dq
ADD x1,AX
POP AX
MOV y1,AX
JMP @Punkt1
{precondition here: both points have been clipped to the visible window;}
{if the line is completely offscreen, the program jumped directly to }
{@LineReady, instead! }
@DrawLine:
PUSH BP
MOV Steigung,0 {reset Flag }
MOV CX,x2
SUB CX,x1 {point1 right of point2 ? }
JGE @posDX {no }
NEG CX {yes, swap points }
MOV AX,x1
XCHG AX,x2
MOV x1,AX
MOV AX,y1
XCHG AX,y2
MOV y1,AX
@posDX:
MOV DI,y1
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI] {DI := y1 * LINESIZE}
MOV AX,x1
MOV BL,AL
SHR AX,1
SHR AX,1
ADD DI,AX {DI := y1 * LINESIZE + (x1 DIV 4) }
AND BX,3 {BX := (x1 AND 4) }
MOV DH,[OFFSET TranslateTab + BX] {get mask for VRAM-access }
MOV DL,2
MOV BX,pa {BX = drawing page}
SHL BX,1
MOV ES,[BX +OFFSET Segment_Adr -StartIndex*2]
{ES:DI=pointer to graphic address of point1, DX=access mask for it}
MOV SI,LINESIZE
MOV BX,y2
SUB BX,y1 {point1 below point2 ? }
JG @posDY {no }
NEG BX {yes, negate deltaY and row-increment }
NEG SI
@posDY:
CMP BX,CX {deltaY > deltaX ?}
JLE @flach {no: small slope, <=1 }
XCHG BX,CX {yes, swap deltas and set flag }
MOV Steigung,1
{compute Bresenham-parameters: 2 * DY, 2 * DY - DX, 2 * (DY - DX) }
@flach:
SHL BX,1
MOV DY_mal2,BX
SUB BX,CX
MOV BP,BX {BP := 2 * DY - DX}
SUB BX,CX
MOV DY_m_DX_mal2,BX
INC CX {CX := number of pixels}
MOV BL,Color
MOV BH,1
CMP Steigung,0 {steep line? }
JNZ @high1 {yes}
@low1: {no }
MOV AX,3C4h
XCHG AX,DX
OUT DX,AX {select correct bitplane }
MOV DX,AX {save mask to DX again }
MOV AL,BL {get color of point }
STOSB {draw point }
SHL DH,1 {compute mask for next point }
CMP DH,16 {still addressable with the same address?}
JE @nextbyte1 {no, address had to be incremented by 1 }
DEC DI {yes, make incrementing DI undone }
@low1b:
OR BP,BP
JGE @low2
ADD BP,DY_mal2
LOOP @low1
JMP @raus
@nextbyte1:
MOV DH,BH {restore mask to 1 }
JMP @low1b {rest as above }
@low2:
ADD BP,DY_m_DX_mal2
ADD DI,SI
LOOP @low1
JMP @raus
@high1:
MOV AX,3C4h
XCHG AX,DX
OUT DX,AX
MOV DX,AX
MOV AL,BL
@high1b:
OR BP,BP
JGE @high2
ADD BP,DY_mal2
MOV ES:[DI],AL
ADD DI,SI
LOOP @high1b
JMP @raus
@high2:
ADD BP,DY_m_DX_mal2
SHL DH,1
CMP DH,16
JE @nextbyte2
MOV ES:[DI],AL
ADD DI,SI
LOOP @high1
JMP @raus
@nextbyte2:
MOV DH,BH
STOSB
ADD DI,SI
LOOP @high1
@raus:
POP BP
@LineReady:
END;
END;
FUNCTION GetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be read }
{ PAGEADR= graphic page(segment) to be read from }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: color of the point}
{rem: If the pixel lies outside the visible window, the routine }
{ will return "0" as the result }
{ Attention! As PAGEADR always specifies the NOT visible gra- }
{ phic page, this routine will read from there, too! }
ASM
XOR AL,AL {preset AL with 0 }
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BL,3 {BL = X MOD 4 = plane to read from}
MOV AL,4
MOV AH,BL
MOV DX,3CEh
MOV ES,PAGEADR
CLI
OUT DX,AX
MOV AL,ES:[DI]
STI
@offscrn:
END;
FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be read }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: color of the point in the background page}
{rem: If the pixel lies outside the visible window, the routine }
{ will return "0" as the result }
{ Because BACKGNDADR is used as background page, calling }
{ this routine only makes sense when using mode STATIC! }
{ If EMS is used then the calling routine must take care }
{ that the EMS page frame contains the proper data (which can }
{ be done by the statement "IF EMSused THEN EnsureEMSConsistency()")}
ASM
XOR AL,AL {preset AL with 0 }
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BX,3 {BX := (x1 AND 4) }
SHL BX,1 {*2, because word-sized}
ADD DI,[OFFSET BACKTab + BX] {get mask for access: BX * 16000 }
MOV ES,BACKGNDADR
MOV AL,ES:[DI]
@offscrn:
END;
FUNCTION PageGetPixel(x,y:INTEGER; pa:WORD):BYTE; ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be read }
{ pa = graphic page (0..3), from which the point shall be }
{ read out }
{ StartVirtualX, StartVirtualY = upper left image corner }
{out: color of the point in the background page}
{rem: If the pixel lies outside the visible window, the routine }
{ will return "0" as the result }
{ If you want to read from the actually VISIBLE page, then }
{ then you must call the routine with pa=1-PAGE! }
{ Sensible values for "pa" are either 0 or 1 (and evtl. BACK- }
{ GNDPAGE, if you are using background mode STATIC), however, }
{ the routine doesn't check that! }
ASM
CMP pa,BACKGNDPAGE
JNE @doit
PUSH x
PUSH y
CALL BackgroundGetPixel
JMP @offscrn
@doit:
XOR AL,AL {preset AL with 0 }
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BX,3 {BL = X MOD 4 = plane to read from; BH = 0}
MOV AL,4
MOV AH,BL
MOV BX,pa {BX = graphic page}
AND BX,3 {only pages 0..3}
SHL BX,1
MOV ES,[BX +OFFSET Segment_Adr-StartIndex*2]
CLI
MOV DX,3CEh
OUT DX,AX
MOV AL,ES:[DI]
STI
@offscrn:
END;
PROCEDURE PutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be written }
{ color = color value for the pixel to be drawn}
{ 1-PAGE = graphic page to be drawn upon }
{ StartVirtualX, StartVirtualY = upper left image corner }
{ WinClip= TRUE, if line shall be clipped to the window area }
{ WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
{out: - }
{rem: The point (x,y) has been transformed to absolute screen coordinates }
{ and has been drawn (if it lies within the visible window) }
{ The pixel will NOT automatically be overtaken into the background }
{ image, that is: it will be visible only for one animation cycle! }
{ (For that reason, you should call this routine AFTER calling ANIMATE }
{ because otherwise, the drawn point will vanish at once!) }
ASM
CMP WinClip,TRUE
JE @goClip
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV BX,1 {ES := Segment_Adr[1-PAGE], because 1-PAGE = visible page}
SUB BX,PAGE
SHL BX,1
MOV ES,[BX +OFFSET Segment_Adr-StartIndex*2]
CLI
OUT DX,AX
MOV AL,color
MOV ES:[DI],AL {faster than STOSB on >=386!}
STI
JMP @ende
@goClip: {to this address, if clipping active}
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
CMP DI,WinYMIN
JL @offscrn
CMP DI,WinYMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
CMP BX,WinXMIN
JL @offscrn
CMP BX,WinXMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV BX,1 {ES := Segment_Adr[1-PAGE], because 1-PAGE = visible page}
SUB BX,PAGE
SHL BX,1
MOV ES,[BX +OFFSET Segment_Adr-StartIndex*2]
CLI
OUT DX,AX
MOV AL,color
MOV ES:[DI],AL {faster than STOSB on >=386!}
STI
@offscrn:
@ende:
END;
PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be written }
{ color = color value for the pixel to be drawn}
{ StartVirtualX, StartVirtualY = upper left image corner }
{ WinClip=TRUE, if line shall be clipped to the window area }
{ WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
{out: - }
{rem: The point (x,y) has been transformed to absolute screen coordinates and}
{ has been drawn into the background (if it is onscreen) }
{ The pixel will NOT be visible until the next animation cycle takes }
{ place (but then, it remains permanent) (For that reason, you should }
{ call this routine BEFORE calling ANIMATE. That way, evtl. changes to }
{ the background will be visible "at once"!) }
{ Because BACKGNDPAGE is used as background page, calling this }
{ routine only makes sense when using background mode STATIC!}
{ If EMS is used then the calling routine must take care }
{ that the EMS page frame contains the proper data (which can }
{ be done by the statement "IF EMSused THEN EnsureEMSConsistency()")}
ASM
CMP WinClip,TRUE
JE @goClip
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BX,3 {BX := (x1 AND 4) }
SHL BX,1 {*2, because word-sized}
ADD DI,[OFFSET BACKTab + BX] {get mask for access: BX * 16000 }
MOV ES,BACKGNDADR
MOV AL,color
MOV ES:[DI],AL {faster than STOSB on >=386!}
JMP @ende
@goClip: {to this address, if clipping active}
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
CMP DI,WinYMIN
JL @offscrn
CMP DI,WinYMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
CMP BX,WinXMIN
JL @offscrn
CMP BX,WinXMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BX,3 {BX := (x1 AND 4) }
SHL BX,1 {*2, because word-sized}
ADD DI,[OFFSET BACKTab + BX] {get mask for access: BX * 16000 }
MOV ES,BACKGNDADR
MOV AL,color
MOV ES:[DI],AL {faster than STOSB on >=386!}
@offscrn:
@ende:
END;
PROCEDURE PagePutPixel(x,y:INTEGER; color:BYTE; pa:WORD); ASSEMBLER;
{ in: x,y = VIRTUAL pixel coordinates of the point to be written }
{ color = color value for the pixel to be drawn}
{ pa = graphic page (0..3) to be drawn upon }
{ PAGEADR= graphic page(segment) to be drawn upon }
{ StartVirtualX, StartVirtualY = upper left image corner }
{ WinClip= TRUE, if line shall be clipped to the window area }
{ WinXMIN,WinXMAX,WinYMIN,WinYMAX = window for evtl. clipping }
{out: - }
{rem: The point (x,y) has been transformed to absolute screen coordinates }
{ and has been drawn (if it lies within the visible window) }
{ If you want to draw at the actually VISIBLE graphic page }
{ then you must call the routine with pa=1-PAGE! }
{ Again, the drawn pixel will _NOT_ automatically be taken }
{ over into the background image, that is: it will be visible }
{ only until the next animation cycle (=till calling ANIMATE) }
{ (For that reason, you should call this routine AFTER }
{ calling ANIMATE, because otherwise, your drawn pixel will }
{ vanish at once!) }
{ Sensible values for "pa" are either 0 or 1 (and evtl. BACK- }
{ GNDPAGE, if you are using background mode STATIC), however, }
{ the routine doesn't check that! }
ASM
CMP pa,BACKGNDPAGE
JNE @doit
PUSH x
PUSH y
PUSH WORD PTR color
CALL BackgroundPutPixel
JMP @offscrn
@doit:
CMP WinClip,TRUE
JE @goClip
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
JS @offscrn
CMP DI,YMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
JS @offscrn
CMP BX,XMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV BX,pa {BX = graphic page}
SHL BX,1
MOV ES,[BX +OFFSET Segment_Adr+StartIndex*2]
CLI
OUT DX,AX
MOV AL,color
MOV ES:[DI],AL {faster than STOSB on >=386!}
STI
JMP @ende
@goClip: {to this address, if clipping active}
MOV DI,y
SUB DI,StartVirtualY {transform y into absolute coordinates}
CMP DI,WinYMIN
JL @offscrn
CMP DI,WinYMAX
JG @offscrn
MOV BX,x
SUB BX,StartVirtualX {transform x into absolute coordinates}
CMP BX,WinXMIN
JL @offscrn
CMP BX,WinXMAX
JG @offscrn
SHL DI,1
MOV DI,CS:[OFFSET gadr + DI]
{DI = Y * LINESIZE, BX = X, coordinates admissible}
MOV AX,BX
SHR AX,1
SHR AX,1
ADD DI,AX {DI = Y * LINESIZE + (X SHR 2) }
AND BX,3
MOV AH,[OFFSET TranslateTab + BX]
MOV AL,2
MOV DX,3C4h
MOV BX,pa {BX = graphic page}
SHL BX,1
MOV ES,[BX +OFFSET Segment_Adr+StartIndex*2]
CLI
OUT DX,AX
MOV AL,color
MOV ES:[DI],AL {faster than STOSB on >=386!}
STI
@offscrn:
@ende:
END;
PROCEDURE LoadFont(s:STRING);
{ in: s = name of the font file to load, '' for: internal font }
{ FontType = type of the actual font}
{out: CurrentFont=pointer to new font }
{ FontType = type of the font loaded}
{ FontHeight=its height in rows }
{ FontWidth =its width in pixels }
{ FontProportion = TagProportional, if proportional font }
{ FontWidthTable[] = font widths for each letter }
{The initial call to "ResetToInternalFont" assures that FontType }
{ always has a defined value when this routine gets called! }
PROCEDURE ResetToInternalFont;
VAR i,j:BYTE;
BEGIN
IF CurrentFont<>NIL {very first call? }
THEN BEGIN {no! }
IF FontType=TagMonoFont
THEN FreeMem(CurrentFont,SizeOf(MonoFont))
ELSE IF FontType=TagColorFont
THEN FreeMem(CurrentFont,SizeOf(ColorFont))
END;
IF MaxAvail<SizeOf(MonoFont)
THEN BEGIN {not even enough memory for internal font! }
Error:=Err_NotEnoughMemory;
exit
END;
GetMem(CurrentFont,SizeOf(MonoFont));
FontType:=TagMonoFont;
FontWidth:=6;
FontHeight:=6;
FontProportion:=0;
FillChar(FontWidthTable,SizeOf(FontWidthTable),FontWidth);
FOR i:=0 TO 255 DO
FOR j:=0 TO FontHeight-1 DO
MonoFont(CurrentFont^)[i][j]:=internalFont[i][j]
END;
VAR f:FileOfByte;
CONST Tag:STRING='FNT';
VAR Header:STRING[3];
size,i,j:WORD;
newFontWidth,newFontHeight,newFontType,newFontProp:BYTE;
tempName:STRING;
BEGIN
IF s=''
THEN BEGIN {switch to internal font }
ResetToInternalFont;
exit
END;
tempName:=FindFile(s);
IF tempName<>'' THEN s:=tempName;
{open font file and read in header: }
_Assign(f,s);
{$I-}
_Reset(f);
Header[0]:=CHAR(Length(Tag));
_BlockRead(f,Header[1],Length(Tag));
_BlockRead(f,newFontWidth,1);
_BlockRead(f,newFontHeight,1);
_BlockRead(f,newFontType,1);
{$IFDEF IOcheck} {$I+} {$ENDIF}
IF (IOresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN
{$I-}
_Close(f);
{$IFDEF IOcheck} {$I+} {$ENDIF}
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
exit;
END;
newFontProp:=newFontType AND TagProportional; {<>0, if proportional }
newFontType:=newFontType AND Pred(TagProportional);
size:=Length(Tag)+3; {length of header }
IF newFontType=TagMonoFont
THEN inc(size,((newFontWidth+7) SHR 3)*newFontHeight SHL 8)
ELSE inc(size,newFontWidth*newFontHeight SHL 8); {256 chars }
IF newFontProp=TagProportional THEN inc(size,256); {font widths}
IF (Header<>Tag) OR
( (newFontType<>TagMonoFont) AND (newFontType<>TagColorFont) ) OR
(newFontWidth>MaxFontWidth) OR
(newFontHeight>MaxFontHeight) OR
(_FileSize(f)<>size)
THEN BEGIN {no FONT-file }
Error:=Err_NoFont;
{$I-}
_Close(f);
{$IFDEF IOcheck} {$I+} {$ENDIF}
CompressError:=CompressErr_NoError;
exit
END;
IF newFontType=TagMonoFont
THEN size:=SizeOf(MonoFont)
ELSE size:=SizeOf(ColorFont);
{now release old memory and allocate new mem: }
IF FontType<>newFontType
THEN BEGIN {only necessary, if old and new fonts are different: }
IF FontType=TagMonoFont THEN FreeMem(CurrentFont,SizeOf(MonoFont))
ELSE {FontType=TagColorFont} FreeMem(CurrentFont,SizeOf(ColorFont));
IF MaxAvail<size
THEN BEGIN {not enough memory }
Error:=Err_NotEnoughMemory;
ResetToInternalFont;
{$I-}
_Close(f);
{$IFDEF IOcheck} {$I+} {$ENDIF}
exit
END;
GetMem(CurrentFont,size)
END;
FontWidth :=newFontWidth;
FontHeight :=newFontHeight;
FontType :=newFontType;
FontProportion:=newFontProp;
IF FontType=TagMonoFont
THEN BEGIN
FOR i:=0 TO 255 DO
BEGIN
Fillchar(MonoFont (CurrentFont^)[i],SizeOf(MonoFontChar),0);
FOR j:=0 TO FontHeight-1 DO
{$I-}
_BlockRead(f,MonoFont(CurrentFont^)[i][j],(FontWidth+7) SHR 3);
{$IFDEF IOcheck} {$I+} {$ENDIF}
END;
END
ELSE FOR i:=0 TO 255 DO
FOR j:=0 TO FontHeight-1 DO
{$I-}
_BlockRead(f,ColorFont(CurrentFont^)[i][j],FontWidth);
{$IFDEF IOcheck} {$I+} {$ENDIF}
{$I-}
IF FontProportion=TagProportional
THEN _BlockRead(F,FontWidthTable,SizeOf(FontWidthTable))
ELSE FillChar(FontWidthTable,SizeOf(FontWidthTable),FontWidth);
_Close(f);
{$IFDEF IOcheck} {$I+} {$ENDIF}
CompressError:=CompressErr_NoError; {reset evtl. compress error }
END;
FUNCTION OutTextLength(s:STRING):WORD;
{ text string }
{ Font* = the font describing datas}
{out: Width of the given string in _pixels_; proportional fonts }
{ will be treated correctly }
VAR i:BYTE;
temp:WORD;
BEGIN
IF FontProportion=TagProportional
THEN BEGIN
temp:=0;
FOR i:=1 TO length(s) DO inc(temp,FontWidthTable[BYTE(s[i])]);
END
ELSE temp:=FontWidth*length(s);
OutTextLength:=temp
END;
PROCEDURE OutTextXY(x,y:INTEGER; pa:WORD; s:STRING);
{ in: (x,y) = (virtual) starting coordinates for the text to be written}
{ s = textstring to be displayed }
{ pa = graphic page where the text shall be written }
{ GraphTextColor=color for text }
{ GraphTextBackground=color to be used for text background; if }
{ this value equals GraphTextColor, only the text-pixels }
{ themselves will be drawn while the surrounding ones }
{ don't change (=normal behaviour of TP's OutText-procs) }
{ GraphTextOrientation="vertical" or "horizontal" }
{ StartVirtualX,StartVirtualY = upper left image corner }
{ Font* = the font describing datas}
{out: text has been written to the screen }
VAR offs,z,bit,i,CharWidth:BYTE;
data1:MonoFontchar;
data2:ColorFontChar;
b:WORD;
BEGIN
IF (pa<0) OR (pa>SCROLLPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber;
exit
END;
IF (pa=BACKGNDPAGE) AND EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
offs:=MaxFontWidth-FontWidth;
IF (FontType=TagMonoFont)
THEN FOR i:=1 TO Length(s) DO
BEGIN
data1:=MonoFont(CurrentFont^)[BYTE(s[i])];
CharWidth:=FontWidthTable[BYTE(s[i])];
FOR z:=0 TO FontHeight-1 DO
BEGIN
b:=WORD(data1[z]);
FOR bit:=0 TO CharWidth-1 DO
IF b and FontMask[bit+offs]<>0
THEN PagePutPixel(x+bit,y+z,GraphTextColor,pa)
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN PagePutPixel(x+bit,y+z,GraphTextBackground,pa);
END;
IF GraphTextOrientation=horizontal
THEN INC(x,CharWidth)
ELSE INC(y,FontHeight);
END
ELSE FOR i:=1 TO Length(s) DO
BEGIN
data2:=ColorFont(CurrentFont^)[BYTE(s[i])];
CharWidth:=FontWidthTable[BYTE(s[i])];
FOR z:=0 TO FontHeight-1 DO
FOR bit:=0 TO CharWidth-1 DO
BEGIN
b:=data2[z][bit];
IF b<>0 THEN PagePutPixel(x+bit,y+z,b,pa)
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN PagePutPixel(x+bit,y+z,GraphTextBackground,pa);
END;
IF GraphTextOrientation=horizontal
THEN INC(x,CharWidth)
ELSE INC(y,FontHeight);
END
END;
PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
{rem: Functionally equivalent to OutTextXY(), but the text will be }
{ written to the background page instead of page PAGEADR! }
{ Because BACKGNDADR is used as background page, calling }
{ this routine only makes sense when using mode STATIC!}
VAR offs,z,bit,i,CharWidth:BYTE;
data1:MonoFontchar;
data2:ColorFontChar;
b:WORD;
BEGIN
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
offs:=MaxFontWidth-FontWidth;
IF (FontType=TagMonoFont)
THEN FOR i:=1 TO Length(s) DO
BEGIN
data1:=MonoFont(CurrentFont^)[BYTE(s[i])];
CharWidth:=FontWidthTable[BYTE(s[i])];
FOR z:=0 TO FontHeight-1 DO
BEGIN
b:=WORD(data1[z]);
FOR bit:=0 TO CharWidth-1 DO
IF b and FontMask[bit+offs]<>0
THEN BackgroundPutPixel(x+bit,y+z,GraphTextColor)
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN BackgroundPutPixel(x+bit,y+z,GraphTextBackground);
END;
IF GraphTextOrientation=horizontal
THEN INC(x,CharWidth)
ELSE INC(y,FontHeight);
END
ELSE FOR i:=1 TO Length(s) DO
BEGIN
data2:=ColorFont(CurrentFont^)[BYTE(s[i])];
CharWidth:=FontWidthTable[BYTE(s[i])];
FOR z:=0 TO FontHeight-1 DO
FOR bit:=0 TO CharWidth-1 DO
BEGIN
b:=data2[z][bit];
IF b<>0 THEN BackgroundPutPixel(x+bit,y+z,b)
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN BackgroundPutPixel(x+bit,y+z,GraphTextBackground);
END;
IF GraphTextOrientation=horizontal
THEN INC(x,CharWidth)
ELSE INC(y,FontHeight);
END
END;
PROCEDURE MakeTextSprite(s:STRING; nr:WORD);
{ in: s = text which shall be converted into a sprite}
{ nr = spriteLOADnumber for the sprite to be generated}
{ Font*, CurrentFont = actual font }
{ GraphTextOrientation = orientation of the font}
{Sprite?[nr] has been defined so, that it represents the contents }
{ of "s" as a sprite}
{rem: The routine behaves like LoadSprite() }
CONST DefaultHeader:SpriteHeader=
(Zeiger_auf_Plane:(0,0,0,0);
Breite_in_4er_Gruppen:0;
Hoehe_in_Zeilen:0;
Translate:(1,2,4,8);
SpriteLength:0;
Dummy:(0,0,0,0,0,0,0,0,0,0);
Kennung:'KR';
Version:1;
Modus:Display_NORMAL;
ZeigerL:0;
ZeigerR:0;
ZeigerO:0;
ZeigerU:0
);
VAR header:SpriteHeader;
p1,p2:POINTER;
segm,offs,b:WORD;
x,y,i:INTEGER;
z,wert,bit,CharWidth:BYTE;
data1:MonoFontchar;
data2:ColorFontChar;
BEGIN
IF (nr=0) or (nr>LoadMAX)
THEN BEGIN
Error:=Err_InvalidSpritenumber;
Exit
END;
header:=DefaultHeader; {use standard values }
WITH header DO
BEGIN
IF GraphTextOrientation=horizontal
THEN BEGIN {sprite height is only 1 char cell}
Hoehe_in_Zeilen:=FontHeight;
Breite_in_4er_Gruppen:=(OutTextLength(s)+3) SHR 2
END
ELSE BEGIN {sprite width is only 1 char cell}
Hoehe_in_Zeilen:=FontHeight*Length(s);
Breite_in_4er_Gruppen:=(FontWidth+3) SHR 2
END;
SpriteLength:=Kopf+ {size of sprite header}
2*(2*Hoehe_in_Zeilen)+ {le. & ri. boundaries }
2*(2*4*Breite_in_4er_Gruppen)+ {up. & lo. boundaries }
Breite_in_4er_Gruppen*4*Hoehe_in_Zeilen; {data}
IF (Breite_in_4er_Gruppen*Hoehe_in_Zeilen=0)
OR (SpriteLength>65521-15) {GetMem-upper bound=65521}
THEN BEGIN
Error:=Err_NoSprite;
Exit
END;
{enough space left? }
IF (Header.SpriteLength+15>MaxAvail+SPRITESIZE[nr])
THEN BEGIN
Error:=Err_NotEnoughMemory;
Exit
END
ELSE FreeSpriteMem(nr); {evtl. release old memory }
getmem(p1,Header.SpriteLength+15); {get enough space }
SPRITESIZE[nr]:=Header.SpriteLength+15;
SPRITEPTR [nr]:=p1;
IF (LONGINT(p1) mod 16)=0
THEN p2:=p1 {make p2 fall on segment boundary}
ELSE LONGINT(p2):=LONGINT(p1) + (16-LONGINT(p1) mod 16);
segm:=LONGINT(p2) SHR 16 +(LONGINT(p2) AND 65535) SHR 4;
SPRITEAD[nr]:=segm;
ZeigerL:=Kopf;
ZeigerR:=ZeigerL+Hoehe_in_Zeilen*2;
ZeigerO:=ZeigerR+Hoehe_in_Zeilen*2;
ZeigerU:=ZeigerO+(Breite_in_4er_Gruppen*4)*2;
FOR i:=0 TO 3 DO
Zeiger_auf_Plane[i]:=ZeigerU+(Breite_in_4er_Gruppen*4)*2+
i*Breite_in_4er_Gruppen*Hoehe_in_Zeilen;
FOR i:=0 TO Hoehe_in_Zeilen-1 DO
BEGIN
MEMW[segm:ZeigerL +i SHL 1]:=+16000;
MEMW[segm:ZeigerR +i SHL 1]:=WORD(-16000);
END;
FOR i:=0 TO Breite_in_4er_Gruppen*4-1 DO
BEGIN
MEMW[segm:ZeigerO +i SHL 1]:=+16000;
MEMW[segm:ZeigerU +i SHL 1]:=WORD(-16000);
END;
MOVE(Header,p2^,Kopf); {store sprite header to heap }
{now compute sprite data: do this by drawing pixels into the memory}
offs:=MaxFontWidth-FontWidth; x:=0; y:=0;
IF (FontType=TagMonoFont)
THEN FOR i:=1 TO Length(s) DO
BEGIN
data1:=MonoFont(CurrentFont^)[BYTE(s[i])];
CharWidth:=FontWidthTable[BYTE(s[i])];
FOR z:=0 TO FontHeight-1 DO
BEGIN
b:=WORD(data1[z]);
FOR bit:=0 TO CharWidth-1 DO
BEGIN
IF b and FontMask[bit+offs]<>0
THEN wert:=GraphTextColor
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN wert:=GraphTextBackground
ELSE wert:=0;
{pixel (a,b) -> b*Breite_in_4er_Gruppen+(x div 4) at plane x mod 4 }
MEM[segm:Zeiger_auf_Plane[(x+bit) AND 3]+
(y+z)*Breite_in_4er_Gruppen+
(x+bit) SHR 2]:=wert;
IF wert<>0
THEN BEGIN {evtl. re-compute boundaries}
IF x+bit<INTEGER(MEMW[segm:ZeigerL +(y+z) SHL 1])
THEN MEMW[segm:ZeigerL +(y+z) SHL 1]:=x+bit;
IF x+bit>INTEGER(MEMW[segm:ZeigerR +(y+z) SHL 1])
THEN MEMW[segm:ZeigerR +(y+z) SHL 1]:=x+bit;
IF y+z<INTEGER(MEMW[segm:ZeigerO +(x+bit) SHL 1])
THEN MEMW[segm:ZeigerO +(x+bit) SHL 1]:=y+z;
IF y+z>INTEGER(MEMW[segm:ZeigerU +(x+bit) SHL 1])
THEN MEMW[segm:ZeigerU +(x+bit) SHL 1]:=y+z;
END;
END;
END;
IF GraphTextOrientation=horizontal
THEN INC(x,CharWidth)
ELSE INC(y,FontHeight);
END
ELSE FOR i:=1 TO Length(s) DO
BEGIN
data2:=ColorFont(CurrentFont^)[BYTE(s[i])];
CharWidth:=FontWidthTable[BYTE(s[i])];
FOR z:=0 TO FontHeight-1 DO
FOR bit:=0 TO CharWidth-1 DO
BEGIN
b:=data2[z][bit];
IF b<>0 THEN wert:=b
ELSE IF (GraphTextColor<>GraphTextBackground)
THEN wert:=GraphTextBackground
ELSE wert:=0;
{pixel (a,b) -> b*Breite_in_4er_Gruppen+(x div 4) at plane x mod 4 }
MEM[segm:Zeiger_auf_Plane[(x+bit) AND 3]+
(y+z)*Breite_in_4er_Gruppen+
(x+bit) SHR 2]:=wert;
IF wert<>0
THEN BEGIN {evtl. re-compute boundaries}
IF x+bit<INTEGER(MEMW[segm:ZeigerL +(y+z) SHL 1])
THEN MEMW[segm:ZeigerL +(y+z) SHL 1]:=x+bit;
IF x+bit>INTEGER(MEMW[segm:ZeigerR +(y+z) SHL 1])
THEN MEMW[segm:ZeigerR +(y+z) SHL 1]:=x+bit;
IF y+z<INTEGER(MEMW[segm:ZeigerO +(x+bit) SHL 1])
THEN MEMW[segm:ZeigerO +(x+bit) SHL 1]:=y+z;
IF y+z>INTEGER(MEMW[segm:ZeigerU +(x+bit) SHL 1])
THEN MEMW[segm:ZeigerU +(x+bit) SHL 1]:=y+z;
END;
END;
IF GraphTextOrientation=horizontal
THEN INC(x,CharWidth)
ELSE INC(y,FontHeight);
END
END; {of WITH}
END;
FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN; ASSEMBLER;
{ in: s1,s2 = sprite position numbers of two sprites}
{ SpriteN[s1],SpriteX[s1],SpriteY[s1] = sprite data of sprite s1 }
{ SpriteN[s2],SpriteX[s2],SpriteY[s2] = sprite data of sprite s2 }
{out: TRUE/FALSE for "sprites collide"/"sprites do not collide" }
{rem: This check is pixel-precise and doesn't depend on the sprites being }
{ visible (=onscreen) or not! }
{ inactive sprites (SpriteN[s?]=0) cannot collide }
{ A sprite can't collide with itself, (thus: s1=s2 -> FALSE) }
ASM
MOV SI,s1 {get 1st parameter s1 from stack}
MOV DI,s2 {get 2nd parameter s2 from stack}
CMP SI,DI
JE @NOHIT1 {sprite can't collide with itself }
SHL SI,1
mov cx,[SI + OFFSET SpriteN]
jcxz @NOHIT1 {sprite <>0, that is: sprite active?}
SHL DI,1
MOV BX,[DI + OFFSET SpriteN]
OR BX,BX {dto. for other sprite }
JNE @PRUEF2
@NOHIT1:
JMP @NOHIT7 {inactive sprites can't collide }
{either -> return FALSE }
{here: SI (DI) = pointer to 1. (2.) sprite in ?YWRTD[..],}
{ CX (BX) = spritenumber of sprite 1 (2) }
{(a bit later, DS (ES) becomes segment addr. of sprite data of spr. 1 (2) )}
@PRUEF2:
MOV AX,[SI + OFFSET SpriteY]
MOV DX,[DI + OFFSET SpriteY]
mov si,[SI + OFFSET SpriteX] {SI = x1}
mov di,[DI + OFFSET SpriteX] {DI = x2}
shl bx,1 {BX = sprite number2 * 2}
mov es,[BX + OFFSET SPRITEAD] {ES = segment of 2nd sprite's data}
mov bx,cx {(CX = spritenumber1)}
shl bx,1 {BX = spritenumber1 * 2}
MOV ds,[BX + OFFSET SPRITEAD]
mov [y1],ax
mov [y2],dx
sub dx,ax
mov CS:WORD PTR @y2_y1+1,dx
mov [x1],si
mov [x2],di
mov dx,di
sub dx,si
mov CS:WORD PTR @x2_x1+1,dx
mov ax,es:[Left] {AX = pointer to left boundary data}
mov CS:WORD PTR @lirand2+1,ax
mov ax,es:[Right] {AX = pointer to right boundary data}
mov CS:WORD PTR @rerand2+1,ax
mov ax,es:[Top] {AX = pointer to upper boundary data}
mov CS:WORD PTR @orand2+1,ax
mov ax,es:[Bottom] {AX = pointer to lower boundary data}
mov CS:WORD PTR @urand2+1,ax
mov ax,es:[Breite] {AX = max. width in groups of 4 }
shl al,1
shl al,1
mov CS:WORD PTR @breite2+1,ax {*4 = width in points }
mov ax,es:[Hoehe]
mov CS:WORD PTR @hoehe2+1,ax {height of sprite2 in points}
MOV AX,[Left] {AX = pointer to left boundary data}
MOV CS:WORD PTR @LIRAND1+1,AX
MOV AX,[Right] {AX = pointer to right boundary data}
MOV CS:WORD PTR @RERAND1+1,AX
MOV AX,[Top] {AX = pointer to upper boundary data}
MOV CS:WORD PTR @ORAND1+1,AX
MOV AX,[Bottom] {AX = pointer to lower boundary data}
MOV CS:WORD PTR @URAND1+1,AX
MOV BX,[Breite] {BX = max. width in groups of 4 }
SHL BX,1
SHL BX,1 {*4 = width in points }
MOV CS:WORD PTR @BREITE1+2,BX
lea bx,[si+bx-1] {BX := x1 + breite1 - 1 (=x1last)}
@breite2:
mov bp,1234h {dummy value}
mov cx,bp {CX = breite2 will be needed again later on}
lea bp,[di+bp-1] {BP := x2 + breite2 - 1 (=x2last)}
cmp bx,bp
jle @noex1
mov bp,bx
@noex1: {here: BP = max(x1last,x2last) (=maxx)}
cmp si,di
jle @X1_klgl_X2
xchg si,di
@X1_klgl_X2: {here: SI = min(x1,x2) (=minx)}
stc
sbb si,bp {SI := minx - maxx - 1 = - (maxx - minx + 1)}
@breite1:
add cx,1234h {(dummy value) CX := breite1 + breite2}
add cx,si {CX := breite1 + breite2 - (maxx - minx + 1)}
dec cx {CX := breite1 + breite2 - (maxx - minx + 1) - 1 (=ueberlappx - 1)}
js @NOHIT2 {no collision, if ueberlappx <= 0 }
mov [ueberlappx_1],cx
mov ax,[Hoehe]
mov bx,ax {BX := hoehe1}
mov di,[y1] {DI := y1}
add ax,di {AX := y1 + hoehe1}
dec ax {AX := y1 + hoehe1 - 1 (=y1last)}
@hoehe2:
mov si,1234h
mov dx,[y2]
add dx,si {DX := y2 + hoehe2}
dec dx {DX := y2 + hoehe2 - 1 (=y2last)}
cmp ax,dx
jge @noex2
mov ax,dx
@noex2: {here: AX = max(y1last,y2last) (=maxy)}
mov dx,[y2]
cmp di,dx {(DI = y1)}
jle @noex3
mov di,dx
@noex3: {here: DI = min(y1,y2) (=miny)}
sub di,ax {DI := miny - maxy = - (maxy - miny)}
lea ax,[bx+si-2] {AX := hoehe1 + hoehe2 - 2}
add ax,di {AX := hoehe1 + hoehe2 - (maxy - miny + 1) - 1 (=ueberlappy - 1)}
js @NOHIT2 {no collision, if ueberlappy <= 0 }
mov [ueberlappy_1],ax
{here: AX = ueberlappy - 1, CX = ueberlappx - 1}
@x2_x1:
mov dx,1234h {dummy value}
xor bx,bx {from now on: BX = 0 !}
or dx,dx
js @X2_X1_kl_0 {if x2 - x1 >= 0 then...}
mov [hit2xfirst],bx {...hit2xfirst := 0}
mov [hit1xfirst],dx {...hit1xfirst := x2 - x1}
jmp @Yhits {SHORT}
{jump-rail for NOHIT (this is a good place)}
@NOHIT2:
JMP @NOHIT7
{now back at "normal" program }
@X2_X1_kl_0: {else (x2 - x1 < 0)...}
mov [hit1xfirst],bx {...hit1xfirst := 0}
neg dx {DX := x1 - x2}
mov [hit2xfirst],dx {...hit2xfirst := x1 - x2}
@Yhits: {here: AX = ueberlappy - 1}
@y2_y1:
mov dx,1234h {dummy value}
or dx,dx
js @Y2_Y1_kl_0 {if y2 - y1 >= 0 then...}
mov [hit2yfirst],bx {...hit2yfirst := 0}
mov [hit1yfirst],dx {...hit1yfirst := y2 - y1}
jmp @iterate {SHORT}
@Y2_Y1_kl_0: {else (y2 - y1 < 0)...}
mov [hit1yfirst],bx {...hit1yfirst := 0}
neg dx {DX := y1 - y2}
mov [hit2yfirst],dx {...hit2yfirst := y1 - y2}
{Now check the overlapping rows and columns more closely by iteration: }
@iterate:
mov cx,[ueberlappy_1] {number of rows -1 to compare }
shl cx,1 {*2, because word-sized!}
@lirand1:
mov si,1234h {dummy value}
@lirand2:
mov di,1234h {dummy value}
@rerand1:
mov bx,1234h {dummy value}
@rerand2:
mov bp,1234h {dummy value}
sub bx,si {BX := rerand1 - lirand1}
sub bp,di {BP := rerand2 - lirand2}
mov ax,[hit1yfirst]
shl ax,1
add si,ax {SI := 1st row where sprite1 overlaps sprite2 }
mov ax,[hit2yfirst]
shl ax,1
add di,ax {DI := 1st row where sprite2 overlaps sprite1 }
add si,cx {dto., last row }
add di,cx
@one_line:
mov ax,[si] {DS:AX := x1li[row] }
mov dx,es:[di] {ES:DX := x2li[row] }
add ax,[x1] {AX := x1li[row] + x1 (=c) }
add dx,[x2] {DX := x2li[row] + x2 (=d) }
cmp ax,dx
jge @C_grgl_D
mov ax,dx
@C_grgl_D: {here: AX = max(c,d)}
mov cx,[si+bx] {DS:CX := x1re[row] }
mov dx,es:[di+bp] {ES:DX := x2re[row] }
add cx,[x1] {CX := x1re[row] + x1 (=a) }
add dx,[x2] {DX := x2re[row] + x2 (=b) }
cmp cx,dx
jle @A_klgl_B
mov cx,dx
@A_klgl_B: {here: CX = min(a,b)}
cmp cx,ax {min(a,b) >= max(c,d) ?}
jge @found_Xhit {yes: collision in X-direction found!}
dec si {next row (-> word-sized values!)}
dec si
dec di
dec di
dec WORD PTR [ueberlappy_1]
jns @one_line
{no collision in X-direction -> no collision at all! }
jmp @NOHIT7
{otherwise: collision in X-direction, now check Y-dir. also (as above) and }
{report "collision!" only, if there is at least 1 collision in Y-dir., too }
@found_Xhit:
mov cx,[ueberlappx_1] {number of columns -1 to compare }
shl cx,1 {*2, because word-sized!}
@orand1:
mov si,1234h {dummy value}
@orand2:
mov di,1234h {dummy value}
@urand1:
mov bx,1234h {dummy value}
@urand2:
mov bp,1234h {dummy value}
sub bx,si {BX := urand1 - orand1}
sub bp,di {BP := urand2 - orand2}
mov ax,[hit1xfirst]
shl ax,1 {*2, because word-sized!}
add si,ax {SI := orand1 + 2 * hit1xfirst}
mov ax,[hit2xfirst]
shl ax,1 {*2, because word-sized!}
add di,ax {DI := orand2 + 2 * hit2xfirst}
add si,cx
add di,cx
@one_column: mov ax,[si] {AX := y1ob[column]}
cmp ax,16000 {dummy value for "empty column"?}
je @next_column {yes, thus: surely no collision }
mov dx,es:[di] {DX := y2ob[column]}
cmp dx,16000 {check 2nd sprite too: "empty column"?}
je @next_column {yes, no collision}
add ax,[y1] {AX := y1ob + y1 (=c)}
add dx,[y2] {DX := y2ob + y2 (=d)}
cmp ax,dx
jge @C_grgl_D2
mov ax,dx
@C_grgl_D2: {here: AX = max(c,d)}
mov cx,[si+bx] {DS:CX := y1un[column]}
mov dx,es:[di+bp] {ES:DX := y2un[column]}
add cx,[y1] {CX := y1un + y1 (=a)}
add dx,[y2] {DX := y2un + y2 (=b)}
cmp cx,dx
jle @A_klgl_B2
mov cx,dx
@A_klgl_B2: {here: CX = min(a,b)}
cmp cx,ax {min(a,b) >= max(c,d) ?}
jge @HIT2 {yes: collision detected!}
@next_column:
dec si {no, next column (-> word-sized values!)}
dec si
dec di
dec di
dec WORD PTR [ueberlappx_1]
jns @one_column
@NOHIT7:
XOR AX,AX {return 0 = FALSE as result }
JMP @TREFF_END {SHORT}
@HIT2:
MOV AX,1 {return 1 = TRUE as result }
@TREFF_END:
{$IFOPT G+}
mov bp,sp {only necessary for compiler switch G+!}
{$ENDIF}
mov dx,seg @DATA {else, BP will be restored by TP itself}
mov ds,dx
END;
PROCEDURE SetSplitIndex(number:INTEGER);
{ in: number = index number of the sprite, up to which sprites won't }
{ be clipped to the animation windows }
{out: - }
{rem: After calling the routine, sprites SpriteN[0..number] will not-}
{ and SpriteN[number+1..NMAX] will be clipped to the animation window}
{ If number <0 oder >NMAX, then all sprites will be clipped!}
BEGIN
IF number>NMAX THEN number:=-1;
SplitIndex:=number;
SplitIndex_mal2:=number*2
END;
FUNCTION GetSplitIndex:INTEGER;
{ in: - }
{out: Actually set value of SplitIndex }
BEGIN
GetSplitIndex:=SplitIndex
END;
PROCEDURE SetAnimateWindow(x1,y1,x2,y2:INTEGER);
{ in: (x1,y1) = left upper corner of the animation window to set }
{ (x2,y2) = dto., lower right corner}
{out: Win* have been updated accordingly }
{ BWin* = backups of the most important values}
{rem: The points must be specified in absolute coordinates }
{ The window's size must at least be 32x32 pixels, }
{ x1 and x2-x1+1 must be multiples of 4 (or they will be made so)! }
BEGIN
x1:=x1 AND $FFFC; {make x1 a multiple of 4 }
WinXMIN:=x1; WinXMINdiv4:=x1 SHR 2;
WinYMIN:=y1; WinYMIN_mul_LINESIZE:=y1*LINESIZE;
WinYMINmLINESIZEaWinXMINdiv4:=WinYMIN_mul_LINESIZE+WinXMINdiv4;
WinWidth :=succ(x2-x1) AND $FFFC; {make the width a multiple of 4 }
WinHeight:=succ(y2-y1);
WinXMAX:=WinXMIN+WinWidth-1;
WinYMAX:=WinYMIN+WinHeight-1;
WinWidthDiv4:=WinWidth SHR 2;
WinLowerRight:=(XMAX-WinXMAX) SHR 2 + (YMAX-WinYMAX)*LINESIZE;
IF (WinXMIN<0) OR (WinYMIN<0) OR (WinWidth<32) OR (WinHeight<32) OR
(WinXMAX>XMAX) OR (WinYMAX>YMAX)
THEN Error:=Err_InvalidCoordinates;
BWinXMIN:=WinXMIN; {make backups }
BWinYMIN:=WinYMIN;
BWinXMAX:=WinXMAX;
BWinYMAX:=WinYMAX;
BWinLowerRight:=WinLowerRight;
BWinYMIN_mul_LINESIZE:=WinYMIN_mul_LINESIZE
END;
PROCEDURE Animate;
{ in: PAGEADR = actual graphicpage(address) on which to draw upon }
{ BACKGNDADR = background page(address) }
{ BACKGROUNDMODE = STATIC/SCROLLING for solid/scrollable background }
{ SpriteN[] = spritenumber of sprite to be displayed }
{ SpriteX[],SpriteY[] = their (virtual) coordinates }
{ StartVirtualX,StartVirtualY = upper left image corner }
{ (PAGE = actually displayed graphic page) }
{ Win* = data describing the animation window }
{out: PAGE = 0/1, if PAGE has been 1/0, respectively }
{ PAGEADR = new, actual graphic page(address) }
{rem: Animate erases the old contents of the page (using the background page }
{ information), draws all visible sprites, synchronizes to the display- }
{ enable signal and switches the display to that now completed page }
VAR leftcut,rightcut,topcut,bottomcut:WORD;
{x,y,} xtil,ytil,actindex:INTEGER;
KachelnWegLinks,KachelnWegOben,
innerTilesX,innerTilesY,
stepX1,stepX2,
Xoffscreen,Yoffscreen,
counter,
Korrektur,
BytesPerPlane,LINESIZE_sub_BytesPerPlane,
leftcutDIV4,
tempActIndex,tempDI,tempXtil,tempYtil,{tempX,tempY,}
oldActIndex,oldDI,
StartWritePlane,StartLesePlane: INTEGER;
BEGIN
IF EMSused AND (BackgroundMode=STATIC)
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
ASM
CLD
{first copy the background picture to the actual graphic page:}
CMP BackgroundMode,STATIC {which background mode? }
JE @static_bckgnd
JMP @scrolling_bckgnd
@static_bckgnd:
MOV BX,WinHeight
MOV DX,WinWidth
MOV SI,WinYMINmLINESIZEaWinXMINdiv4 {1.1st start/dest. address}
MOV DI,SI
MOV ES,PAGEADR {fill graphic page with background pattern}
CMP UpdateOuterArea,0 {update outer background area?}
MOV DS,BACKGNDADR
je @skip_outer
{do inner and outer background area in one }
xor si,si
xor di,di
mov ax,0102h
mov dx,3c4h
mov bx,pagesize/2
out dx,ax {write plane 0 }
mov cx,bx
rep movsw
mov ah,2
out dx,ax {write plane 1 }
mov cx,bx
xor di,di
rep movsw
mov ah,4
out dx,ax {write plane 2 }
mov cx,bx
xor di,di
rep movsw
mov ah,8
out dx,ax {write plane 3 }
mov cx,bx
xor di,di
rep movsw
mov ax,seg @data
mov ds,ax
dec UpdateOuterArea
jmp @sprites_zeichnen
@skip_outer: {draw only inner area}
PUSH BP
CMP DX,XMAX+1 {window from the very left to the very right?}
JNE @innen
MOV AX,0102h
MOV DX,3C4h
SHL BX,1 {yes, can be done by a REP MOVSB instruction }
MOV BX,CS:[OFFSET gadr + BX] {BX := WinHeight * LINESIZE}
MOV BP,PAGESIZE
SUB BP,BX
OUT DX,AX {select write plane 0 }
MOV CX,BX
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB
SUB DI,BX {reset DI }
ADD SI,BP {set si to the next "plane" }
MOV AH,2
OUT DX,AX {select write plane 1 }
MOV CX,BX
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB
SUB DI,BX {reset DI }
ADD SI,BP {set si to the next "plane" }
MOV AH,4
OUT DX,AX {select write plane 2 }
MOV CX,BX
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB
SUB DI,BX {reset DI }
ADD SI,BP {set si to the next "plane" }
MOV AH,8
OUT DX,AX {select write plane 3 }
MOV CX,BX
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB
jmp @end_static
@innen:
SHR DX,1
SHR DX,1 {DX := bytes per row }
MOV BP,LINESIZE
SUB BP,DX {BP := offset to the next row }
MOV BH,DL
XOR CH,CH
MOV AX,0102h
MOV DX,3C4h
OUT DX,AX {select write plane 0 }
MOV AH,BL
PUSH SI
PUSH DI
@loop_innen1:
MOV CL,BH
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB {move data of one row }
ADD SI,BP {position to next row }
ADD DI,BP
DEC BL {one row completed}
JNZ @loop_innen1
POP DI
POP SI
ADD SI,PAGESIZE
PUSH SI
PUSH DI
MOV BL,AH
MOV AH,02h
OUT DX,AX
MOV AH,BL
@loop_innen2:
MOV CL,BH
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB {move data of one row }
ADD SI,BP {position to next row }
ADD DI,BP
DEC BL {one row completed}
JNZ @loop_innen2
POP DI
POP SI
ADD SI,PAGESIZE
PUSH SI
PUSH DI
MOV BL,AH
MOV AH,04h
OUT DX,AX
MOV AH,BL
@loop_innen3:
MOV CL,BH
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB {move data of one row }
ADD SI,BP {position to next row }
ADD DI,BP
DEC BL {one row completed}
JNZ @loop_innen3
POP DI
POP SI
ADD SI,PAGESIZE
MOV BL,AH
MOV AH,08h
OUT DX,AX
MOV AH,BL
@loop_innen4:
MOV CL,BH
SHR CX,1
REP MOVSW
ADC CX,CX
REP MOVSB {move data of one row }
ADD SI,BP {position to next row }
ADD DI,BP
DEC BL {one row completed}
JNZ @loop_innen4
@end_static:
POP BP
MOV AX,SEG @DATA
MOV DS,AX
JMP @Sprites_zeichnen
{---------------------------------}
@scrolling_bckgnd: {now: create background image from tiles }
{Do we have to redraw the outer area of the animation window?}
cmp UpdateOuterArea,0
je @old_scrolling_bckgnd
{Yes, but perhaps there is nothing to draw at all?:}
mov bx,WinHeight
dec bx
mov bh,bl
mov bl,WinWidthDiv4
mov ax,WinYMINmLINESIZEaWinXMINdiv4
{BL=WinWidthDiv4, BH=WinHeight-1, AX=WinYMIN*LINESIZE+WinXMIN DIV 4}
or ax,ax {upper left corner of the window = pixel (0,0)?}
jne @do_outer {no: draw outer area }
cmp bx,YMAX SHL 8 +LINESIZE {lower right corner = pixel (XMAX,YMAX)?}
je @old_scrolling_bckgnd {yes, thus no outer area to draw }
@do_outer: {now draw the outer area }
{╔════════════════╗ outer area is divided into 3 regions: }
{║1111111111111111║ }
{║1111111111111111║ }
{║111┌───────┐2222║ }
{║222│ │2222║ }
{║222│ │2222║ }
{║222└───────┘3333║ }
{║3333333333333333║ }
{║3333333333333333║ }
{╚════════════════╝ }
{BL=WinWidthDiv4, BH=WinHeight-1, AX=WinYMIN*LINESIZE+WinXMIN DIV 4}
push bp
push WinLowerRight
mov bp,ax
mov es,PAGEADR
mov ds,BACKGNDADR
mov dx,3C4h
mov ax,0102h {write plane 0 }
out dx,ax
xor si,si {region 1 starts at offset 0 }
xor di,di
mov cx,bp
shr cx,1
rep movsw
adc cx,cx
rep movsb
mov ah,2 {write plane 1 }
out dx,ax
mov si,1*PAGESIZE
xor di,di
mov cx,bp
shr cx,1
rep movsw
adc cx,cx
rep movsb
mov ah,4 {write plane 2 }
out dx,ax
mov si,2*PAGESIZE
xor di,di
mov cx,bp
shr cx,1
rep movsw
adc cx,cx
rep movsb
mov ah,8 {write plane 3 }
out dx,ax
mov si,3*PAGESIZE
xor di,di
mov cx,bp
shr cx,1
rep movsw
adc cx,cx
rep movsb
mov ah,1
out dx,ax
mov al,bl
cbw {AX:=WinWidth DIV 4; works, because }
mov dl,LINESIZE
sub dl,al {DL:=LINESIZE-WinWidth DIV 4}
jz @region3 {does window run continously from the far left to right?}
mov dh,bh {DH:=WinHeight-1}
or dh,dh
jz @region3 {window height only 1 row?}
xor ch,ch
mov bx,ax
add di,ax
mov si,di {destination address=starting address due to memory layout}
mov bp,di
push dx
@region2a:
{DL = width of one row of the animation window }
{DH = WinHeight-1}
{BX = WinWidth DIV 4 = width of the animation window DIV 4 }
{CX:=width of one row from the right edge of the animation window to}
{the left edge of the animation window in the next row: }
mov cl,dl {ch=0}
shr cx,1
rep movsw
adc cx,cx
rep movsb
add si,bx
add di,bx
dec dh
jnz @region2a
mov ax,0202h
mov dx,3c4h
out dx,ax
pop dx
push dx
mov si,bp {SI=DI=BP for plane #0, SI=DI+1*PAGESIZE for plane #1, etc.}
add si,1*PAGESIZE
mov di,bp
@region2b:
mov cl,dl {ch=0}
shr cx,1
rep movsw
adc cx,cx
rep movsb
add si,bx
add di,bx
dec dh
jnz @region2b
mov ah,04h
mov dx,3c4h
out dx,ax
pop dx
push dx
mov si,bp
add si,2*PAGESIZE
mov di,bp
@region2c:
mov cl,dl {ch=0}
shr cx,1
rep movsw
adc cx,cx
rep movsb
add si,bx
add di,bx
dec dh
jnz @region2c
mov ah,08h
mov dx,3c4h
out dx,ax
pop dx
mov si,bp
add si,3*PAGESIZE
mov di,bp
@region2d:
mov cl,dl {ch=0}
shr cx,1
rep movsw
adc cx,cx
rep movsb
add si,bx
add di,bx
dec dh
jnz @region2d
@region3:
pop cx {CX:=WinLowerRight}
jcxz @endregion3 {there is no region 3 to be drawn}
mov bx,cx {store copy in BX }
mov si,PAGESIZE
sub si,cx {starting address of region 3}
mov di,si {destination address:=starting address (works due to memory layout!)}
mov bp,di {save destination address }
mov ax,0102h
mov dx,3c4h
out dx,ax
shr cx,1
rep movsw
adc cx,cx
rep movsb
mov ah,2
out dx,ax
mov cx,bx
mov di,bp
sub si,cx {reset SI to its old value, then increment it to the next page}
add si,PAGESIZE
shr cx,1
rep movsw
adc cx,cx
rep movsb
mov ah,4
out dx,ax
mov cx,bx
mov di,bp
sub si,cx {reset SI to its old value, then increment it to the next page}
add si,PAGESIZE
shr cx,1
rep movsw
adc cx,cx
rep movsb
mov ah,8
out dx,ax
mov cx,bx
mov di,bp
sub si,cx {reset SI to its old value, then increment it to the next page}
add si,PAGESIZE
shr cx,1
rep movsw
adc cx,cx
rep movsb
@endregion3:
pop bp
mov ax,seg @data
mov ds,ax
dec UpdateOuterArea {reset UpdateOuterArea }
@old_scrolling_bckgnd:
MOV DI,$F {often used constants }
MOV CX,4
{#tiles, really being cut off at their left edge:}
{IF StartVirtualX+WinXMIN-BackX1<0}
{ THEN KachelnWegLinks:=(StartVirtualX+WinXMIN-BackX1-15) DIV 16}
{ ELSE KachelnWegLinks:=(StartVirtualX+WinXMIN-BackX1) DIV 16;}
MOV AX,StartVirtualX
ADD AX,WinXMIN
SUB AX,BackX1
MOV BX,AX {BX = StartVirtualX + WinXMIN - BackX1}
SAR AX,CL
MOV KachelnWegLinks,AX
{pixels, which are cut off at the left of the 1st (partially) visible tile: }
{really: leftcut := ((StartVirtualX + WinXMIN - BackX1) MOD 16) AND $F }
{but this is equivalent to: }
{leftcut := (StartVirtualX + WinXMIN - BackX1) AND $F}
{the "AND $F" is for evtl. underflow <0 }
AND BX,DI
MOV leftcut,BX
MOV AX,BX
SHR AX,1
SHR AX,1
MOV leftcutDIV4,AX
{compute 1st read plane of the left-cut tiles: leftcut AND 3 }
{ Then compute the according mask out of this }
MOV AH,BL {BL = leftcut}
AND AH,3
MOV AL,4
MOV StartLesePlane,AX {must lie in the stack segment!}
{dto., for last (partially) visible tile at the right}
{really: rightcut := (16 - leftcut - (WinWidth MOD 16)) AND $F, aber s.o.! }
{rightcut := (16 - leftcut - WinWidth) AND $F}
{the "AND $F" is for evtl. underflow <0 }
NEG BX {BX=-leftcut}
MOV SI,WinWidth
MOV AX,16
ADD AX,BX
SUB AX,SI
AND AX,DI {AX = (16 - leftcut - WinWidth) AND $F}
MOV rightcut,AX
{#_non-cut_ tiles in the inner area of the window per row:}
{innerTilesX:=(WinWidth - (-rightcut AND $F) - (-leftcut AND $F)) SHR 4;}
NEG AX
AND AX,DI
AND BX,DI
SUB SI,AX
SUB SI,BX
SHR SI,CL
MOV innerTilesX,SI
{stepX1=additonal factor to position from the rightmost (evtl. only partially}
{ visible) tile of one line to the 1st tile of the next row, which }
{ is _not_ cut at its left side}
{stepX2=dto., but to the 1st (evtl. only partially) visible tile of the }
{ next row }
{stepX1 := XTiles -(innerTilesX);}
{stepX2 := stepX1;}
{IF leftcut<>0 THEN dec(stepX2)} {stepX2 = XTiles -(innerTiles + (leftcut<>0))}
MOV DX,XTiles
MOV AX,DX
SUB AX,SI
MOV stepX1,AX
OR BX,BX {for the given range holds: leftcut = 0 <-> -leftcut and $F=0}
JE @nodec
DEC AX
@nodec:
MOV stepX2,AX
{compute 1st write plane of the non-left-cut tiles: }
{ (WinXMIN - leftcut) AND 3, is computed here by }
{ (WinXMIN - (leftcut AND $F) AND 3}
{ Then compute the according mask out of this }
ADD BX,WinXMIN
AND BX,3
MOV AH,CS:[OFFSET CS_TranslateTab +BX]
MOV AL,2
MOV StartWritePlane,AX {must lie in the stack segment!}
{now the same thing for the Y-direction:}
{IF StartVirtualY+WinYMIN-BackY1<0}
{ THEN KachelnWegOben := (StartVirtualY + WinYMIN - BackY1 - 15) DIV 16}
{ ELSE KachelnWegOben := (StartVirtualY + WinYMIN - BackY1) DIV 16;}
MOV AX,StartVirtualY
ADD AX,WinYMIN
SUB AX,BackY1
MOV BX,AX
SAR AX,CL
MOV KachelnWegOben,AX
{compute index of 1st (evtl. only partially) visible tile: }
{actIndex := KachelnWegOben * XTiles + KachelnWegLinks + 1;}
{The "+1" is to accomodate that the BackTile[]-counting starts at 0}
{to keep OffscreenTile free! }
IMUL DX
ADD AX,KachelnWegLinks
INC AX
MOV actIndex,AX
{topcut := (StartVirtualY + WinYMIN - BackY1) AND $F;}
AND BX,DI
MOV topcut,BX
{bottomcut := (16 - topcut - WinHeight) AND $F;}
NEG BX
MOV SI,WinHeight
MOV AX,16
ADD AX,BX
SUB AX,SI
AND AX,DI
MOV bottomcut,AX
{innerTilesY := (WinHeight - (-bottomcut AND $F) - (-topcut AND $F)) SHR 4;}
NEG AX
AND AX,DI
AND BX,DI
SUB SI,AX
SUB SI,BX
SHR SI,CL
MOV innerTilesY,SI
{---now draw!--- }
{ has been set in SetAnimateWindow() already: }
{ MOV AX,WinXMIN}
{ (* MOV x,AX *)}
{ MOV BX,WinYMIN}
{ (* MOV y,BX *)}
{ SHR AX,1}
{ SHR AX,1}
{ MOV WinXMINdiv4,AX}
{ SHL BX,1}
{ MOV BX,CS:[OFFSET GADR +BX]}
{ MOV WinYMIN_mul_LINESIZE,BX}
{ ADD AX,BX}
{ MOV WinYMINmLINESIZEaWinXMINdiv4,AX}
{ }
MOV AX,KachelnWegLinks
MOV xtil,AX
MOV CX,AX {CX = copy of KachelnWegLinks = xtil }
MOV AX,KachelnWegOben
MOV ytil,AX
MOV ES,PAGEADR {once and for all!}
MOV BX,leftcut
{here: AX = ytil, BX = Leftcut, CX = xtil, ES = ^graphic segment }
TEST BL,3 {leftcut MOD 4 = 0 ?}
JZ @useMode1 {yes, write mode 1 can be used}
JMP @useMode0 {no, use write mode 0 }
@useMode1:
{Go here if width of window is a multiple of 4, leftcut mod 4=0 and the left }
{edge of the window is a multiple of 4 (this also makes rightcut mod 4=0) }
{then write mode 1 can be used }
MOV AX,4105h {enable write mode 1 }
MOV DX,3CEh
OUT DX,AX
MOV AX,0F02h {access all 4 planes at once }
MOV DX,3C4h
OUT DX,AX
CMP topcut,0 {IF ytil ε [0..YTiles( }
JE @m1SkipTopRow { THEN DX = Yoffscreen := $FFFF }
MOV AX,ytil { ELSE DX = Yoffscreen := $0000 }
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Yoffscreen,DX
MOV AX,WinXMINdiv4 {AX =correct value, if we have to jump! }
OR BX,BX {BX=leftcut}
JZ @m1SkipTopLeftCorner
MOV SI,actIndex {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
AND SI,DX { THEN SI := 0 }
JZ @m1go1 { ELSE SI := actIndex }
MOV AX,CX {CX = xtil}
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go1:
{PROCEDURE DrawUpperLeftTile with WriteMode1: }
{ in: WinXMIN,WinYMIN = screen coordinates }
{ ES = ^graphic segment}
{ SI = tile index }
{ BX = leftcut }
{ CX = xtil }
{ leftcut MOD 4 = 0 }
{ topcut, Win*, SCROLLADR,...}
{out: ES = ^graphic segment}
{rem: WriteMode1 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,topcut {additionaly: the rows cut off at the upper edge:}
MOV CX,16 {4 bytes for each row }
SUB CX,SI {CX := 16 - topcut = rows to draw }
SHL SI,1
SHL SI,1
ADD SI,AX {SI = pointer to first tile *row* to copy }
MOV AX,BX {BX = leftcut}
SHR AX,1 {increment SI by left cut-off = leftcut DIV 4 }
SHR AX,1
ADD SI,AX {SI = pointer to first tile *byte* to copy }
MOV DI,WinYMINmLINESIZEaWinXMINdiv4 {ES:DI = destination address}
MOV DS,SCROLLADR {DS:SI = source address}
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16 {BP := (16 - leftcut) DIV 4 = bytes per tile row }
SUB BP,BX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE {is a constant }
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BX,CX {number of rows }
@m1eineZeile4a1:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m1eineZeile4a1
POP BP
MOV AX,SEG @DATA
MOV DS,AX
{position to next tile to the right of it: }
INC actIndex
INC xtil
MOV AX,WinXMIN
ADD AX,16
SUB AX,leftcut
{ MOV x,AX }
SHR AX,1
SHR AX,1
@m1SkipTopLeftCorner:
ADD AX,WinYMIN_mul_LINESIZE
MOV DI,AX {ES:DI = ^destination address}
{now draw innerTilesX tiles, which are only cut at their top:}
MOV AX,innerTilesX
OR AX,AX
JBE @m1UpperInnerTilesDone
MOV counter,AX
MOV BX,16 {adjustment factor, to set DI one tile row higher and }
SUB BX,topcut {one tile column further }
SHL BX,1
MOV BX,CS:[OFFSET GADR +BX]
NEG BX
ADD BX,4 {BX := -(16 - topcut) * LINESIZE + 4}
@m1repeat1:
MOV SI,actIndex {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
AND SI,Yoffscreen { THEN SI := 0 }
JZ @m1go2 { ELSE SI := actIndex }
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go2:
{PROCEDURE DrawUpperInnerTile with WriteMode1: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ BX = correction for row}
{ leftcut MOD 4 = 0 }
{ topcut, Win*, SCROLLADR,...}
{out: ES:DI = ^destination address of the next tile to the right of it}
{rem: WriteMode1 has been set already and remains set }
{ BX will not be changed }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,topcut {additionaly: the rows cut off at the upper edge:}
MOV CX,16 {4 bytes for each row }
SUB CX,SI {CX := 16 - topcut = rows to draw }
SHL SI,1
SHL SI,1
ADD SI,AX {SI = pointer to first tile *row* to copy }
MOV DX,DS
MOV DS,SCROLLADR
MOV AX,LINESIZE-4
@m1eineZeile4b1:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,AX
LOOP @m1eineZeile4b1
{DI = ^start of the next row below the tile, now position to the next }
{tile: }
{┌─┬─┬─┬─┐ ┌─┬─┬─┬─┐▄ }
{├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤ }
{└─┴─┴─┴─┘ └─┴─┴─┴─┘ }
{ ▀ }
MOV DS,DX {POP BP isn't necessary}
ADD DI,BX
{position to next tile to the right of it: }
INC actIndex
INC xtil
{ MOV AX,16 }
{ ADD x,AX }
DEC counter
JNZ @m1repeat1
@m1UpperInnerTilesDone:
{ES:DI = ^first row of the upper right corner tile}
CMP rightcut,0
JE @m1SkipTopRightCorner
MOV SI,actIndex {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
AND SI,Yoffscreen { THEN SI := 0 }
JZ @m1go3 { ELSE SI := actIndex }
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go3:
{PROCEDURE DrawUpperRightTile with WriteMode1: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ rightcut MOD 4 = 0 }
{ topcut, Win*, SCROLLADR,...}
{out: ES = ^graphic segment }
{rem: WriteMode1 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,topcut {additionaly: the rows cut off at the upper edge:}
MOV CX,16 {4 bytes for each row }
SUB CX,SI {CX := 16 - topcut = rows to draw }
SHL SI,1
SHL SI,1
ADD SI,AX {SI = pointer to first tile *row* to copy }
MOV AX,rightcut
MOV DS,SCROLLADR {DS:SI = source address}
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16 {BP := (16 - rightcut) DIV 4 = bytes per tile row }
SUB BP,AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BX,CX {BX := rows to draw }
@m1eineZeile4c1:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m1eineZeile4c1
POP BP
MOV AX,SEG @DATA
MOV DS,AX
@m1SkipTopRightCorner:
{position on first left tile, which is not cut at its top: }
MOV AX,stepx2
ADD actIndex,AX
{ MOV AX,WinXMIN }
{ MOV x,AX }
MOV AX,KachelnWegLinks
MOV xtil,AX
INC ytil
@m1SkipTopRow:
MOV AX,topcut {IF topcut = 0 }
NEG AX { THEN AX = y := WinYMIN}
JZ @m1l1 { ELSE AX = y := WinYMIN + (16 - topcut)}
ADD AX,16
@m1l1:
ADD AX,WinYMIN
{ MOV y,AX }
MOV DI,AX {DI := y * LINESIZE +X DIV 4}
SHL DI,1
MOV DI,CS:[OFFSET GADR +DI]
ADD DI,WinXMINdiv4
{ES:DI = ^destination address of the 1st tile of the 1st tile row not being cut at its top}
CMP leftcut,0
JZ @m1SkipLeftColumn
MOV DX,16
SUB DX,leftcut
SHR DX,1
SHR DX,1
MOV AX,LINESIZE
SUB AX,DX {correction factor for AX}
MOV LINESIZE_sub_BytesPerPlane,AX
MOV BytesPerPlane,DX {bytes to move }
MOV AX,innerTilesY
OR AX,AX
JBE @m1LeftLoopDone
MOV counter,AX
PUSH actIndex
{ PUSH y }
PUSH ytil
PUSH DI
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Xoffscreen,DX {is a constant for the tile column}
MOV BX,BytesPerPlane
@m1repeat5:
MOV SI,actIndex
AND SI,Xoffscreen
JZ @m1go11
MOV AX,ytil
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go11:
{PROCEDURE DrawLeftTile with WriteMode1: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ BX = BytesPerPlane }
{ leftcut MOD 4 = 0 }
{ LINESIZE_sub_BytesPerPlane}
{ leftcutDIV4, Win*, SCROLLADR,...}
{out: ES:DI = ^destination address of the next tile below}
{rem: WriteMode1 has been set already and remains set }
{ BX will not be changed }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
ADD SI,leftcutDIV4
MOV AX,LINESIZE_sub_BytesPerPlane
MOV DX,4
SUB DX,BX
MOV DS,SCROLLADR
MOV CX,BX {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BX {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {16.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV AX,SEG @DATA
MOV DS,AX
{next tile; DI already has the right value}
MOV AX,XTiles
ADD actIndex,AX
{ MOV AX,16 }
{ ADD y,AX }
INC ytil
DEC counter
JNZ @m1repeat5
POP DI
POP ytil
{ POP y }
POP actIndex
@m1LeftLoopDone:
INC actIndex
ADD DI,BytesPerPlane
INC xtil
{ MOV AX,16 }
{ SUB AX,leftcut }
{ ADD x,AX }
@m1SkipLeftColumn:
{ES:DI = ^destination address of the first inner tile (still)}
MOV AX,innerTilesY {are there any inner tiles at all?}
OR AX,AX
JBE @m1SkipInnerTiles
CMP innerTilesX,0
JB @m1SkipInnerTiles
MOV counter,AX
MOV AX,actIndex {make copies of the actual values }
MOV tempActIndex,AX
{ MOV AX,x }
{ MOV tempX,AX }
MOV AX,xtil
MOV tempXtil,AX
{ MOV AX,y }
{ MOV tempY,AX }
MOV AX,ytil
MOV tempYtil,AX
MOV tempDI,DI
CMP rightcut,0
JE @m1SkipRightColumn
MOV DX,16
SUB DX,rightcut
SHR DX,1
SHR DX,1
MOV BytesPerPlane,DX
MOV AX,LINESIZE
SUB AX,DX
MOV LINESIZE_sub_BytesPerPlane,AX
MOV AX,innerTilesX
ADD xtil,AX
ADD actIndex,AX
MOV CL,2
SHL AX,CL
ADD DI,AX {ES:DI = ^first right edge-tile which is not cut at its top }
{ SHL AX,CL }
{ ADD x,AX }
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Xoffscreen,DX
MOV BX,BytesPerPlane
@m1repeat6:
MOV SI,actIndex
AND SI,Xoffscreen
JZ @m1go12
MOV AX,ytil
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go12:
{PROCEDURE DrawRightTile with WriteMode1: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ BX = BytesPerPlane }
{ rightcut MOD 4 = 0 }
{ innerTilesY >= 1 }
{ LINESIZE_sub_BytesPerPlane}
{ SCROLLADR,...}
{out: ES:DI = ^destination address of the next tile below}
{rem: WriteMode1 has been set already and remains set }
{ BX will not be changed }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV AX,LINESIZE_sub_BytesPerPlane
MOV DX,4
SUB DX,BX
MOV DS,SCROLLADR
MOV CX,BX {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BX {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BX {16.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV AX,SEG @DATA
MOV DS,AX
{next tile; DI already has the right value}
MOV AX,XTiles
ADD actIndex,AX
{ MOV AX,16 }
{ ADD y,AX }
INC ytil
DEC counter
JNZ @m1repeat6
MOV DI,tempDI
MOV AX,tempActIndex
MOV actIndex,AX
{ MOV AX,tempX }
{ MOV x,tempX }
MOV AX,tempXtil
MOV xtil,AX
{ MOV AX,tempY }
{ MOV y,AX }
MOV AX,tempYtil
MOV ytil,AX
@m1RightLoopDone:
@m1SkipRightColumn:
{ES:DI = ^destination address of the first inner tile (still)}
{innerTilesX >= 0, innerTilesX >= 1 -> it would suffice to check innerTilesX=0:}
CMP innerTilesX,0 {IF (innerTilesX <= 0) OR (innerTilesY <= 0) THEN skip}
JBE @m1SkipInnerTiles {If there are no inner tiles, then the }
{actual position is located at the 1st tile which }
{is not cut at its left and is in the lowest tile row }
{now "FOR x:=1 TO innerTilesX DO FOR y:=1 TO innerTilesY DO .." }
MOV oldDI,DI {make temporary copies of DI and actIndex }
MOV BX,actIndex {BX corresponds to "oldActIndex"}
MOV AX,innerTilesX
MOV counter,AX {counter for X-direction}
MOV CL,6
@m1xloop:
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Xoffscreen,DX {is a constant for the tile column}
MOV AX,innerTilesY
MOV CH,AL {CH is used as a counter for the Y-direction}
@m1yloop:
MOV SI,BX {SI = temp. actIndex}
AND SI,Xoffscreen
JZ @m1go5
MOV AX,ytil
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go5:
{PROCEDURE DrawInnerTile with WriteMode1: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ CL = 6 }
{ SCROLLADR}
{out: ES:DI = ^destination address of the next tile below}
{ CL = 6}
{rem: WriteMode1 has been set already and remains set }
{ CH, BX may not be changed! }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
{each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV DX,DS {save DS to DX }
MOV DS,SCROLLADR
MOVSB {1.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4 {ES:DI = ^next tile }
MOVSB {2.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {3.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {4.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {5.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {6.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {7.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {8.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {9.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {10.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {11.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {12.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {13.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {14.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {15.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOVSB {16.row }
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
MOV DS,DX
{next tile; DI already has the right value}
ADD BX,XTiles {set temp. actIndex to next row }
INC ytil
{ MOV AX,16 }
{ ADD y,AX }
DEC CH
JNZ @m1yloop
{actIndex still has its old value as only "oldActIndex" has been changed }
INC actIndex {actIndex = next inner tile in uppermost tile row }
MOV BX,actIndex {and use it as starting value for next column }
MOV DI,oldDI {ES:DI = ^inner tile in uppermost tile row }
ADD DI,4 {increment by one tile }
MOV oldDI,DI {and use it as starting value for next column }
MOV AX,tempYtil
MOV ytil,AX {set Y-coordinate back to the uppermost inner tile row }
{ MOV AX,oldY }
{ MOV y,AX }
INC xtil {increment X-coordinate by one tile column }
{ MOV AX,16 }
{ ADD x,AX }
DEC counter
JNZ @m1xloop
MOV DI,tempDI {Thus: ES:DI, actIndex, xtil, ytil, x, y point to }
MOV AX,tempActIndex {the first inner tile again (N.B.: y, ytil have }
MOV actIndex,AX {restored some lines above, already! }
MOV AX,tempXtil
MOV xtil,AX
{ MOV AX,tempX }
{ MOV x,AX }
MOV AX,innerTilesY
MOV DX,AX {hold a copy in DX }
ADD ytil,AX {ytil points to the bottommost tile row }
MOV CL,5
SHL AX,CL {dto. for DI: inc(DI,16 * innerTilesY * LINESIZE) }
MOV BX,AX
ADD DI,CS:[OFFSET GADR +BX]
{ SHR AX,1 }
{ ADD y,AX } {dto. for y: inc(y,16 * innerTilesY) }
MOV AX,XTiles
MUL DX {AX := XTiles * innerTilesY}
ADD actIndex,AX {dto. for actIndex: inc(actIndex,XTiles * innerTilesY) }
@m1SkipInnerTiles:
{ES:DI, actIndex, xtil, ytil, x, y point to the first inner tile of the}
{bottommost tile row }
CMP bottomcut,0
JE @m1fertig
MOV AX,ytil
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Yoffscreen,DX
MOV AX,innerTilesX
OR AX,AX
JBE @m1LowerInnerTilesDone {are we standing at the right bottom corner tile? }
MOV counter,AX
{compute addition factor needed to position from bottom to top:}
{┌─┬─┬─┬─┐ ┌─┬─┬─┬─┐▄ }
{├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤ }
{└─┴─┴─┴─┘ └─┴─┴─┴─┘ }
{ ▀ }
{BX := -(16 - bottomcut) * LINESIZE + 4}
MOV BX,16
SUB BX,bottomcut
SHL BX,1
MOV BX,CS:[OFFSET GADR +BX]
NEG BX
ADD BX,4
@m1repeat4:
MOV SI,actIndex
AND SI,Yoffscreen
JZ @m1go8
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go8:
{PROCEDURE DrawLowerInnerTile with WriteMode1: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ BX = adjustment for row addresses }
{ bottomcut, Win*, SCROLLADR,...}
{out: ES:DI = ^destination address of the next tile to the right of it}
{ BX = adjustment for row addresses }
{rem: WriteMode1 has been set already and remains set }
{ DX is not used }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV CX,16
SUB CX,bottomcut
MOV AX,DS {save DS to AX }
MOV DS,SCROLLADR
@m1eineZeile4e1:
MOVSB
MOVSB
MOVSB
MOVSB
ADD DI,LINESIZE-4
LOOP @m1eineZeile4e1
MOV DS,AX
{DI = ^start of the next row below the tile, now position to the next }
{tile: }
{┌─┬─┬─┬─┐ ┌─┬─┬─┬─┐▄ }
{├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤ }
{└─┴─┴─┴─┘ └─┴─┴─┴─┘ }
{ ▀ }
ADD DI,BX
{position to next tile to the right of it: }
INC actIndex
INC xtil
{ MOV AX,16 }
{ ADD x,AX }
DEC counter
JNZ @m1repeat4
@m1LowerInnerTilesDone:
{ES:DI, actIndex, xtil, ytil, x, y point to the lower right corner tile}
CMP rightcut,0
JE @m1SkipLowerRightCorner
PUSH DI
MOV SI,actIndex
AND SI,Yoffscreen
JZ @m1go9
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go9:
{PROCEDURE DrawLowerRightTile with WriteMode1: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ rightcut MOD 4 = 0 }
{ rightcut, bottomcut, Win*, SCROLLADR,...}
{out: ES = ^graphic segment }
{rem: WriteMode1 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV CX,16
SUB CX,bottomcut
MOV BX,16
SUB BX,rightcut
SHR BX,1
SHR BX,1 {BX = BytesPerPlane = (16 - rightcut) DIV 4}
MOV DS,SCROLLADR
MOV AX,LINESIZE
SUB AX,BX
MOV DX,4
SUB DX,BX
PUSH BP
MOV BP,CX {BP=row counter }
@m1eineZeile4g1:
MOV CX,BX
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BP
JNZ @m1eineZeile4g1
POP BP
MOV AX,SEG @DATA
MOV DS,AX
POP DI {ES:DI etc. point to lower right corner tile }
@m1SkipLowerRightCorner:
CMP leftcut,0
JE @m1fertig
{now position on lower left corner tile: }
MOV AX,innerTilesX
INC AX
SUB actIndex,AX {dec(actIndex,innerTilesX + 1) }
SUB xtil,AX {dec(xtil,innerTilesX + 1) }
MOV CL,2
SHL AX,CL
SUB DI,AX {dec(DI,4 * (innerTilesX + 1) }
ADD DI,leftcutDIV4 {don't forget: corner tile may be cut at its left side}
{ MOV AX,WinXMIN }
{ MOV x,AX }
MOV SI,actIndex
AND SI,Yoffscreen
JZ @m1go7
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m1go7:
{PROCEDURE DrawLowerLeftTile with WriteMode1: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ rightcut MOD 4 = 0 }
{ leftcut, bottomcut, Win*, SCROLLADR,...}
{out: (ES = ^graphic segment) }
{rem: WriteMode1 has been set (and remains set) }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV AX,leftcut
MOV BX,AX
MOV CL,2
SHR AX,CL
ADD SI,AX
MOV CX,16
SUB CX,bottomcut
MOV DS,SCROLLADR
PUSH BP
MOV BP,16
SUB BP,BX
SHR BP,1
SHR BP,1 {BP:=(16 - leftcut) DIV 4}
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BX,CX {BX = row counter }
@m1eineZeile4d1:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BX
JNZ @m1eineZeile4d1
POP BP
MOV AX,SEG @DATA
MOV DS,AX
@m1fertig:
{now switch back to write mode 0: }
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
JMP @Sprites_zeichnen
{----------------------------------------------------}
@useMode0:
CMP topcut,0 {IF ytil ε [0..YTiles( }
JE @m0SkipTopRow { THEN DX = Yoffscreen := $FFFF }
MOV AX,ytil { ELSE DX = Yoffscreen := $0000 }
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Yoffscreen,DX
MOV AX,WinXMINdiv4 {AX = correct value, if we have to jump! }
OR BX,BX {BX=leftcut}
JZ @m0SkipTopLeftCorner
MOV SI,actIndex {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
AND SI,DX { THEN SI := 0 }
JZ @m0go1 { ELSE SI := actIndex }
MOV AX,CX {CX = xtil}
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go1:
{PROCEDURE DrawUpperLeftTile with WriteMode0: }
{ in: WinXMIN,WinYMIN = screen coordinates }
{ ES = ^graphic segment}
{ SI = tile index }
{ BX = leftcut }
{ CX = xtil }
{ topcut, Win*, SCROLLADR,...}
{out: ES = ^graphic segment}
{rem: WriteMode0 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,topcut {additionaly: the rows cut off at the upper edge:}
MOV CX,16 {4 bytes for each row }
SUB CX,SI {CX := 16 - topcut = rows to draw }
SHL SI,1
SHL SI,1
ADD SI,AX {SI = pointer to first tile *row* to copy }
MOV AX,BX {BX = leftcut}
SHR AX,1 {increment SI by left cut-off = leftcut DIV 4 }
SHR AX,1
ADD SI,AX {SI = pointer to first tile *byte* to copy }
MOV DI,WinYMINmLINESIZEaWinXMINdiv4 {ES:DI = destination address}
MOV DS,SCROLLADR {DS:SI = source address}
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
PUSH BP {will be needed when leaving the procedure! }
MOV BP,16+3 {BP:=(16 + 3 - leftcut) DIV 4 = bytes per tile row }
SUB BP,BX
PUSH BP {save BP for next plane }
SHR BP,1
SHR BP,1
MOV AH,BL {BL = leftcut}
AND AH,3
MOV AL,4
MOV DX,3CEh
OUT DX,AX {select first read plane}
PUSH AX
MOV DX,3C4h
MOV AX,0102h
OUT DX,AX {choose write plane 0 }
MOV AX,LINESIZE {is a constant }
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BX,CX {number of rows }
PUSH SI
PUSH DI
PUSH BX
@m0eineZeile4a1:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m0eineZeile4a1
POP BX
POP DI
POP SI
MOV DX,3C4h
MOV AX,0202h
OUT DX,AX {choose write plane 1 }
MOV DX,3CEh
POP AX
INC AH
AND AH,3
JNE @nowrap1a
INC SI
@nowrap1a:
OUT DX,AX {select next read plane }
POP BP {BP = 16 + 3 - leftcut }
DEC BP
PUSH BP
PUSH AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE {is a constant }
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH SI
PUSH DI
PUSH BX
@m0eineZeile4a2:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m0eineZeile4a2
POP BX
POP DI
POP SI
MOV DX,3C4h
MOV AX,0402h
OUT DX,AX {choose write plane 2 }
MOV DX,3CEh
POP AX
INC AH
AND AH,3
JNE @nowrap2a
INC SI
@nowrap2a:
OUT DX,AX {select next read plane }
POP BP {BP = 16 + 2 - leftcut }
DEC BP
PUSH BP
PUSH AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE {is a constant }
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH SI
PUSH DI
PUSH BX
@m0eineZeile4a3:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m0eineZeile4a3
POP BX
POP DI
POP SI
MOV DX,3C4h
MOV AX,0802h
OUT DX,AX {choose write plane 3 }
MOV DX,3CEh
POP AX
INC AH
AND AH,3
JNE @nowrap3a
INC SI
@nowrap3a:
OUT DX,AX {select next read plane }
POP BP {BP = 16+ 1 - leftcut }
DEC BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE {is a constant }
SUB AX,BP
MOV DX,4
SUB DX,BP
@m0eineZeile4a4:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m0eineZeile4a4
POP BP
MOV AX,SEG @DATA
MOV DS,AX
{position to next tile to the right of it: }
INC actIndex
INC xtil
MOV AX,WinXMIN
ADD AX,16
SUB AX,leftcut
{ MOV x,AX }
SHR AX,1
SHR AX,1
@m0SkipTopLeftCorner:
ADD AX,WinYMIN_mul_LINESIZE
MOV DI,AX {ES:DI = ^destination address}
{now draw innerTilesX tiles, which are only cut at their top:}
MOV AX,innerTilesX
OR AX,AX
JBE @m0UpperInnerTilesDone
MOV counter,AX
MOV BX,16 {correction factor to set DI one tile row higher and one}
SUB BX,topcut {one tile column further }
SHL BX,1
MOV AX,CS:[OFFSET GADR +BX]
NEG AX
ADD AX,4
MOV Korrektur,AX {Korrektur := -(16 - topcut) * LINESIZE + 4}
@m0repeat1:
MOV SI,actIndex {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
AND SI,Yoffscreen { THEN SI := 0 }
JZ @m0go2 { ELSE SI := actIndex }
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go2:
{PROCEDURE DrawUpperInnerTile with WriteMode0: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ topcut, Win*, SCROLLADR,...}
{out: ES:DI = ^destination address of the next tile to the right of it}
{rem: WriteMode0 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,topcut {additionaly: the rows cut off at the upper edge:}
MOV CX,16 {4 bytes for each row }
SUB CX,SI {CX := 16 - topcut = rows to draw }
SHL SI,1
SHL SI,1
ADD SI,AX {SI = pointer to first tile *row* to copy }
PUSH BP {save BP }
MOV DX,3C4h
MOV AX,StartWritePlane
OUT DX,AX {select first write plane }
PUSH AX
MOV DX,3CEh
MOV AX,0004h
OUT DX,AX {choose read plane 0}
MOV DS,SCROLLADR {now unused: AX, BX, BP, DX}
MOV BX,CX {copy of row counter}
MOV BP,SI {BP = copy of SI }
MOV AX,DI {AX = copy of DI }
@m0eineZeile4b1:
MOVSW
MOVSW
ADD DI,LINESIZE-4
LOOP @m0eineZeile4b1
MOV SI,BP {restore old values }
MOV DI,AX
MOV CX,BX
MOV AX,0104h
OUT DX,AX {DX = 3CEh -> choose read plane 1}
MOV DX,3C4h
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1b
MOV AH,1
INC DI
@nowrap1b:
OUT DX,AX {select next write plane }
PUSH AX
MOV AX,DI
@m0eineZeile4b2:
MOVSW
MOVSW
ADD DI,LINESIZE-4
LOOP @m0eineZeile4b2
MOV SI,BP {restore old values }
MOV DI,AX
MOV CX,BX
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2b
MOV AH,1
INC DI
@nowrap2b:
OUT DX,AX {DX = 3C4h -> select next write plane }
PUSH AX
MOV DX,3CEh
MOV AX,0204h
OUT DX,AX {choose read plane 2}
MOV AX,DI
@m0eineZeile4b3:
MOVSW
MOVSW
ADD DI,LINESIZE-4
LOOP @m0eineZeile4b3
MOV SI,BP {restore old values }
MOV DI,AX
MOV CX,BX
MOV AX,0304h
OUT DX,AX {DX = 3CEh -> choose read plane 3}
MOV DX,3C4h
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3b
MOV AH,1
INC DI
@nowrap3b:
OUT DX,AX {select last write plane: no more PUSHs necessary}
@m0eineZeile4b4:
MOVSW
MOVSW
ADD DI,LINESIZE-4
LOOP @m0eineZeile4b4
{don't restore old values }
POP BP {make TP happy }
MOV AX,SEG @Data
MOV DS,AX
{DI = ^start of the next row below the tile, now position to the next }
{tile: }
{┌─┬─┬─┬─┐ ┌─┬─┬─┬─┐▄ }
{├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤ }
{└─┴─┴─┴─┘ └─┴─┴─┴─┘ }
{ ▀ }
ADD DI,Korrektur
{-1, because "inc di" has been executed exactly 1x}
DEC DI
{position to next tile to the right of it: }
INC actIndex
INC xtil
{ MOV AX,16 }
{ ADD x,AX }
DEC counter
JNZ @m0repeat1
@m0UpperInnerTilesDone:
{ES:DI = ^first row of the upper right corner tile}
CMP rightcut,0
JE @m0SkipTopRightCorner
MOV SI,actIndex {IF (xtil < 0) OR (xtil >= XTiles) OR Yoffscreen }
AND SI,Yoffscreen { THEN SI := 0 }
JZ @m0go3 { ELSE SI := actIndex }
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go3:
{PROCEDURE DrawUpperRightTile with WriteMode0: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ StartWritePlane = first bitplane to write to }
{ topcut, rightcut, Win*, SCROLLADR,...}
{out: ES = ^graphic segment }
{rem: WriteMode0 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,topcut {additionaly: the rows cut off at the upper edge:}
MOV CX,16 {4 bytes for each row }
SUB CX,SI {CX := 16 - topcut = rows to draw }
SHL SI,1
SHL SI,1
ADD SI,AX {SI = pointer to first tile *row* to copy }
MOV BX,rightcut
PUSH BP {will be needed when leaving the procedure! }
MOV DS,SCROLLADR {DS:SI = source address}
MOV DX,3C4h
MOV AX,StartWritePlane
OUT DX,AX
PUSH AX
MOV DX,3CEh
MOV AX,0004h
OUT DX,AX
{Now there is no further variable on stack, so BP can be used }
{for other purposes!}
MOV BP,16+3 {BP:=(16 + 3 - rightcut) DIV 4 = bytes per tile row }
SUB BP,BX
PUSH BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BX,CX {BX := rows to draw }
PUSH BX
PUSH SI
PUSH DI
@m0eineZeile4c1:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m0eineZeile4c1
POP DI
POP SI
POP BX
POP BP
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap1c
MOV AH,1
INC DI
@nowrap1c:
MOV DX,3C4h
OUT DX,AX
PUSH AX
MOV DX,3CEh
MOV AX,0104h
OUT DX,AX
DEC BP {BP := 16 + 2 - rightcut}
PUSH BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH BX
PUSH SI
PUSH DI
@m0eineZeile4c2:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m0eineZeile4c2
POP DI
POP SI
POP BX
POP BP
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap2c
MOV AH,1
INC DI
@nowrap2c:
MOV DX,3C4h
OUT DX,AX
PUSH AX
MOV DX,3CEh
MOV AX,0204h
OUT DX,AX
DEC BP {BP := 16 + 1 - rightcut}
PUSH BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH BX
PUSH SI
PUSH DI
@m0eineZeile4c3:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m0eineZeile4c3
POP DI
POP SI
POP BX
POP BP
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap3c
MOV AH,1
INC DI
@nowrap3c:
MOV DX,3C4h
OUT DX,AX
MOV DX,3CEh
MOV AX,0304h
OUT DX,AX
DEC BP {BP := 16 + 0 - rightcut}
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
@m0eineZeile4c4:
MOV CX,BP
REP MOVSB
ADD DI,AX
ADD SI,DX
DEC BX
JNZ @m0eineZeile4c4
POP BP
MOV AX,SEG @DATA
MOV DS,AX
@m0SkipTopRightCorner:
{position on first left tile, which is not cut at its top: }
MOV AX,stepx2
ADD actIndex,AX
{ MOV AX,WinXMIN }
{ MOV x,AX }
MOV AX,KachelnWegLinks
MOV xtil,AX
INC ytil
@m0SkipTopRow:
MOV AX,topcut {IF topcut = 0 }
NEG AX { THEN AX = y := WinYMIN}
JZ @m0l1 { ELSE AX = y := WinYMIN + (16 - topcut)}
ADD AX,16
@m0l1:
ADD AX,WinYMIN
{ MOV y,AX }
MOV DI,AX {DI := y * LINESIZE +X DIV 4}
SHL DI,1
MOV DI,CS:[OFFSET GADR +DI]
ADD DI,WinXMINdiv4
{ES:DI = ^destination address of the 1st tile of the 1st tile row not being cut at its top}
CMP leftcut,0
JZ @m0SkipLeftColumn
MOV DX,16+3
SUB DX,leftcut
SHR DX,1
SHR DX,1
MOV AX,LINESIZE
SUB AX,DX {correction factor for AX}
MOV LINESIZE_sub_BytesPerPlane,AX
MOV BytesPerPlane,DX {bytes to move }
MOV AX,innerTilesY
OR AX,AX
JBE @m0LeftLoopDone
MOV counter,AX
PUSH actIndex
{ PUSH y }
PUSH ytil
PUSH DI
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Xoffscreen,DX {is a constant for the tile column}
@m0repeat5:
MOV SI,actIndex
AND SI,Xoffscreen
JZ @m0go11
MOV AX,ytil
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go11:
{PROCEDURE DrawLeftTile with WriteMode0: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ StartLesePlane = first bitplane to read from }
{ LINESIZE_sub_BytesPerPlane, BytesPerPlane = correction factors}
{ leftcut, leftcutDIV4, Win*, SCROLLADR,...}
{out: ES:DI = ^destination address of the next tile below}
{rem: WriteMode0 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
PUSH BP
ADD SI,leftcutDIV4
MOV DX,3C4h
MOV AX,0102h
OUT DX,AX
MOV DX,3CEh
MOV AX,StartLesePlane
OUT DX,AX
MOV BX,AX
MOV AX,16+3
SUB AX,leftcut
PUSH AX
SHR AX,1
SHR AX,1
MOV BP,AX
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV DS,SCROLLADR
PUSH SI
PUSH DI
MOV CX,BP {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BP {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {16.row }
REP MOVSB
POP DI
POP SI
POP BP
INC BH
AND BH,3
JNE @nowrap11a
INC SI
@nowrap11a:
MOV AX,BX
MOV DX,3CEh
OUT DX,AX
MOV DX,3C4h
MOV AX,0202h
OUT DX,AX
DEC BP
PUSH BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH SI
PUSH DI
MOV CX,BP {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BP {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {16.row }
REP MOVSB
POP DI
POP SI
POP BP
INC BH
AND BH,3
JNE @nowrap11b
INC SI
@nowrap11b:
MOV AX,BX
MOV DX,3CEh
OUT DX,AX
MOV DX,3C4h
MOV AX,0402h
OUT DX,AX
DEC BP
PUSH BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH SI
PUSH DI
MOV CX,BP {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BP {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {16.row }
REP MOVSB
POP DI
POP SI
POP BP
INC BH
AND BH,3
JNE @nowrap11c
INC SI
@nowrap11c:
MOV AX,BX
MOV DX,3CEh
OUT DX,AX
MOV DX,3C4h
MOV AX,0802h
OUT DX,AX
DEC BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV CX,BP {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BP {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {16.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
POP BP
MOV AX,SEG @DATA
MOV DS,AX
{next tile; DI already has the right value}
MOV AX,XTiles
ADD actIndex,AX
{ MOV AX,16 }
{ ADD y,AX }
INC ytil
DEC counter
JNZ @m0repeat5
POP DI
POP ytil
{ POP y }
POP actIndex
@m0LeftLoopDone:
INC actIndex
ADD DI,BytesPerPlane
{-1, because "inc di" has been executed exactly 1x}
DEC DI
INC xtil
{ MOV AX,16 }
{ SUB AX,leftcut }
{ ADD x,AX }
@m0SkipLeftColumn:
{ES:DI = ^destination address of the first inner tile (still)}
MOV AX,innerTilesY {are there any inner tiles at all?}
OR AX,AX
JBE @m0SkipInnerTiles {no }
CMP innerTilesX,0
JB @m0SkipInnerTiles
MOV counter,AX
MOV AX,actIndex {make copies of the actual values }
MOV tempActIndex,AX
{ MOV AX,x }
{ MOV tempX,AX }
MOV AX,xtil
MOV tempXtil,AX
{ MOV AX,y }
{ MOV tempY,AX }
MOV AX,ytil
MOV tempYtil,AX
MOV tempDI,DI
CMP rightcut,0
JE @m0SkipRightColumn
MOV DX,16
SUB DX,rightcut
SHR DX,1
SHR DX,1
MOV BytesPerPlane,DX
MOV AX,LINESIZE
SUB AX,DX
MOV LINESIZE_sub_BytesPerPlane,AX
MOV AX,innerTilesX
ADD xtil,AX
ADD actIndex,AX
MOV CL,2
SHL AX,CL
ADD DI,AX {ES:DI = ^first right corner tile being not cut at its top }
{ SHL AX,CL }
{ ADD x,AX }
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Xoffscreen,DX
@m0repeat6:
MOV SI,actIndex
AND SI,Xoffscreen
JZ @m0go12
MOV AX,ytil
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go12:
{PROCEDURE DrawRightTile with WriteMode0: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ StartWritePlane = first bitplane to write to }
{ innerTilesY >= 1 }
{ rightcut, Win*, SCROLLADR,...}
{out: ES:DI = ^destination address of the next tile below}
{rem: WriteMode0 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
PUSH BP
MOV DX,3C4h
MOV AX,StartWritePlane
OUT DX,AX
MOV BX,AX
MOV DX,3CEh
MOV AX,0004h
OUT DX,AX
MOV AX,16+3
SUB AX,rightcut
PUSH AX
SHR AX,1
SHR AX,1
MOV BP,AX
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV DS,SCROLLADR
PUSH SI
PUSH DI
MOV CX,BP {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BP {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {16.row }
REP MOVSB
POP DI
POP SI
POP BP
SHL BH,1
CMP BH,16
JNE @nowrap12a
MOV BH,1
INC DI
@nowrap12a:
MOV AX,BX
MOV DX,3C4h
OUT DX,AX
MOV DX,3CEh
MOV AX,0104h
OUT DX,AX
DEC BP
PUSH BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH SI
PUSH DI
MOV CX,BP {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BP {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {16.row }
REP MOVSB
POP DI
POP SI
POP BP
SHL BH,1
CMP BH,16
JNE @nowrap12b
MOV BH,1
INC DI
@nowrap12b:
MOV AX,BX
MOV DX,3C4h
OUT DX,AX
MOV DX,3CEh
MOV AX,0204h
OUT DX,AX
DEC BP
PUSH BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH SI
PUSH DI
MOV CX,BP {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BP {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {16.row }
REP MOVSB
POP DI
POP SI
POP BP
SHL BH,1
CMP BH,16
JNE @nowrap12c
MOV BH,1
INC DI
@nowrap12c:
MOV AX,BX
MOV DX,3C4h
OUT DX,AX
MOV DX,3CEh
MOV AX,0304h
OUT DX,AX
DEC BP
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV CX,BP {1.row }
REP MOVSB
ADD SI,DX
ADD DI,AX {ES:DI = ^next tile }
MOV CX,BP {2.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {3.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {4.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {5.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {6.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {7.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {8.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {9.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {10.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {11.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {12.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {13.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {14.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {15.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
MOV CX,BP {16.row }
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC DI
POP BP
MOV AX,SEG @DATA
MOV DS,AX
{next tile; DI already has the right value}
MOV AX,XTiles
ADD actIndex,AX
{ MOV AX,16 }
{ ADD y,AX }
INC ytil
DEC counter
JNZ @m0repeat6
MOV DI,tempDI
MOV AX,tempActIndex
MOV actIndex,AX
{ MOV AX,tempX }
{ MOV x,tempX }
MOV AX,tempXtil
MOV xtil,AX
{ MOV AX,tempY }
{ MOV y,AX }
MOV AX,tempYtil
MOV ytil,AX
@m0RightLoopDone:
@m0SkipRightColumn:
{ES:DI = ^destination address of the first inner tile (still)}
{innerTilesX >= 0, innerTilesX >= 1 -> it would suffice to check innerTilesX=0:}
CMP innerTilesX,0 {IF (innerTilesX <= 0) OR (innerTilesY <= 0) THEN skip}
JBE @m0SkipInnerTiles {If there are no inner tiles, then the }
{actual position is located at the 1st tile which }
{is not cut at its left and is in the lowest tile row }
{now "FOR x:=1 TO innerTilesX DO FOR y:=1 TO innerTilesY DO .." }
MOV oldDI,DI {make temporary copies of DI and actIndex }
MOV AX,actIndex
MOV oldActIndex,AX
MOV AX,innerTilesX
MOV counter,AX {counter for X-direction}
MOV CL,6
@m0xloop:
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Xoffscreen,DX {is a constant for the tile column}
MOV AX,innerTilesY
MOV CH,AL {CH is used as a counter for the Y-direction}
@m0yloop:
MOV SI,oldActIndex {SI = temp. actIndex}
AND SI,Xoffscreen
JZ @m0go5
MOV AX,ytil
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go5:
{PROCEDURE DrawInnerTile with WriteMode0: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ CL = 6 }
{ SCROLLADR}
{out: ES:DI = ^destination address of the next tile below}
{ CL = 6}
{rem: WriteMode0 has been set already and remains set }
{ CH will not be changed! }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
{each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV DS,SCROLLADR
MOV DX,3C4h
MOV AX,StartWritePlane
MOV BX,AX
OUT DX,AX
MOV DX,3CEh
MOV AX,0004h
OUT DX,AX
MOVSW {1.row }
MOVSW
ADD DI,LINESIZE-4 {ES:DI = ^next tile }
MOVSW {2.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {3.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {4.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {5.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {6.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {7.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {8.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {9.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {10.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {11.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {12.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {13.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {14.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {15.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {16.row }
MOVSW
SUB SI,16*4
SUB DI,15*LINESIZE +4
MOV AX,0104h
OUT DX,AX {DX = 3CEh}
SHL BH,1
CMP BH,16
JNE @nowrap5a
MOV BH,1
INC DI
@nowrap5a:
MOV AX,BX
MOV DX,3C4h
OUT DX,AX
MOVSW {1.row }
MOVSW
ADD DI,LINESIZE-4 {ES:DI = ^next tile }
MOVSW {2.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {3.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {4.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {5.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {6.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {7.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {8.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {9.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {10.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {11.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {12.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {13.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {14.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {15.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {16.row }
MOVSW
SUB SI,16*4
SUB DI,15*LINESIZE +4
SHL BH,1
CMP BH,16
JNE @nowrap5b
MOV BH,1
INC DI
@nowrap5b:
MOV AX,BX
OUT DX,AX {DX = 3C4h}
MOV DX,3CEh
MOV AX,0204h
OUT DX,AX
MOVSW {1.row }
MOVSW
ADD DI,LINESIZE-4 {ES:DI = ^next tile }
MOVSW {2.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {3.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {4.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {5.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {6.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {7.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {8.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {9.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {10.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {11.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {12.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {13.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {14.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {15.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {16.row }
MOVSW
SUB SI,16*4
SUB DI,15*LINESIZE +4
MOV AX,0304h
OUT DX,AX {DX = 3CEh}
SHL BH,1
CMP BH,16
JNE @nowrap5c
MOV BH,1
INC DI
@nowrap5c:
MOV AX,BX
MOV DX,3C4h
OUT DX,AX
MOVSW {1.row }
MOVSW
ADD DI,LINESIZE-4 {ES:DI = ^next tile }
MOVSW {2.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {3.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {4.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {5.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {6.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {7.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {8.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {9.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {10.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {11.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {12.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {13.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {14.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {15.row }
MOVSW
ADD DI,LINESIZE-4
MOVSW {16.row }
MOVSW
MOV AX,SEG @Data
MOV DS,AX
ADD DI,LINESIZE-4-1
{next tile; DI already has the right value}
MOV AX,XTiles {set temp. actIndex to next row }
ADD oldActIndex,AX
INC ytil
{ MOV AX,16 }
{ ADD y,AX }
DEC CH
JNZ @m0yloop
{actIndex still has its old value, as only oldActIndex has been changed. }
INC actIndex {actIndex = next inner tile in uppermost tile row }
MOV AX,actIndex {and use it as starting value for next column }
MOV oldActIndex,AX
MOV DI,oldDI {ES:DI = ^inner tile in uppermost tile row }
ADD DI,4 {increment by one tile }
MOV oldDI,DI {and use it as starting value for next column }
MOV AX,tempYtil
MOV ytil,AX {set Y-coordinate back to the uppermost inner tile row }
{ MOV AX,oldY }
{ MOV y,AX }
INC xtil {increment X-coordinate by one tile column }
{ MOV AX,16 }
{ ADD x,AX }
DEC counter
JNZ @m0xloop
MOV DI,tempDI {Thus: ES:DI, actIndex, xtil, ytil, x, y point to }
MOV AX,tempActIndex {the first inner tile again (N.B.: y, ytil have }
MOV actIndex,AX {restored some lines above, already! }
MOV AX,tempXtil
MOV xtil,AX
{ MOV AX,tempX }
{ MOV x,AX }
MOV AX,innerTilesY
MOV DX,AX {hold a copy in DX }
ADD ytil,AX {ytil points to the bottommost tile row }
MOV CL,5
SHL AX,CL {dto. for DI: inc(DI,16 * innerTilesY * LINESIZE) }
MOV BX,AX
ADD DI,CS:[OFFSET GADR +BX]
{ SHR AX,1 }
{ ADD y,AX } {dto. for y: inc(y,16 * innerTilesY) }
MOV AX,XTiles
MUL DX {AX := XTiles * innerTilesY}
ADD actIndex,AX {dto. for actIndex: inc(actIndex,XTiles * innerTilesY) }
@m0SkipInnerTiles:
{ES:DI, actIndex, xtil, ytil, x, y point to the first inner tile of the}
{bottommost tile row }
CMP bottomcut,0
JE @m0fertig
MOV AX,ytil
CWD
SUB AX,YTiles
NOT AX
OR AX,DX
CWD
NOT DX
MOV Yoffscreen,DX
MOV AX,innerTilesX
OR AX,AX
JBE @m0LowerInnerTilesDone {are we standing at the right bottom corner tile? }
MOV counter,AX
{compute addition factor needed to position from bottom to top:}
{┌─┬─┬─┬─┐ ┌─┬─┬─┬─┐▄ }
{├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤ }
{└─┴─┴─┴─┘ └─┴─┴─┴─┘ }
{ ▀ }
{Korrektur:=-(16 - bottomcut) * LINESIZE + 4}
MOV BX,16
SUB BX,bottomcut
SHL BX,1
MOV AX,CS:[OFFSET GADR +BX]
NEG AX
ADD AX,4-1 {-1, because DI has been inremented 1x (due to planing)}
MOV Korrektur,AX
@m0repeat4:
MOV SI,actIndex
AND SI,Yoffscreen
JZ @m0go8
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go8:
{PROCEDURE DrawLowerInnerTile with WriteMode0: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ bottomcut, Win*, SCROLLADR,...}
{out: ES:DI = ^destination address of the next tile to the right of it}
{rem: WriteMode0 has been set already and remains set }
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV CX,16
SUB CX,bottomcut
MOV DX,3CEh
MOV AX,0004h
OUT DX,AX
MOV DX,3C4h
MOV AX,StartWritePlane
OUT DX,AX
MOV DS,SCROLLADR
MOV BX,CX
PUSH SI
PUSH DI
@m0eineZeile4e1:
MOVSW
MOVSW
ADD DI,LINESIZE-4
LOOP @m0eineZeile4e1
POP DI
POP SI
MOV CX,BX
SHL AH,1
CMP AH,16
JNE @nowrap8a
MOV AH,1
INC DI
@nowrap8a:
MOV DX,3C4h
OUT DX,AX
MOV BX,AX
MOV DX,3CEh
MOV AX,0104h
OUT DX,AX
MOV AX,CX
PUSH SI
PUSH DI
@m0eineZeile4e2:
MOVSW
MOVSW
ADD DI,LINESIZE-4
LOOP @m0eineZeile4e2
POP DI
POP SI
MOV CX,AX
MOV DX,3CEh
MOV AX,0204h
OUT DX,AX
SHL BH,1
CMP BH,16
JNE @nowrap8b
MOV BH,1
INC DI
@nowrap8b:
MOV AX,BX
MOV DX,3C4h
OUT DX,AX
MOV BX,CX
PUSH SI
PUSH DI
@m0eineZeile4e3:
MOVSW
MOVSW
ADD DI,LINESIZE-4
LOOP @m0eineZeile4e3
POP DI
POP SI
MOV CX,BX
SHL AH,1
CMP AH,16
JNE @nowrap8c
MOV AH,1
INC DI
@nowrap8c:
MOV DX,3C4h
OUT DX,AX
MOV BX,AX
MOV DX,3CEh
MOV AX,0304h
OUT DX,AX
@m0eineZeile4e4:
MOVSW
MOVSW
ADD DI,LINESIZE-4
LOOP @m0eineZeile4e4
MOV AX,SEG @Data
MOV DS,AX
{DI = ^start of the next row below the tile, now position to the next }
{tile: }
{┌─┬─┬─┬─┐ ┌─┬─┬─┬─┐▄ }
{├─┼─┼─┼─┤ ─> ├─┼─┼─┼─┤ }
{└─┴─┴─┴─┘ └─┴─┴─┴─┘ }
{ ▀ }
ADD DI,Korrektur
{position to next tile to the right of it: }
INC actIndex
INC xtil
{ MOV AX,16 }
{ ADD x,AX }
DEC counter
JNZ @m0repeat4
@m0LowerInnerTilesDone:
{ES:DI, actIndex, xtil, ytil, x, y point to the lower right corner tile}
CMP rightcut,0
JE @m0SkipLowerRightCorner
PUSH DI
MOV SI,actIndex
AND SI,Yoffscreen
JZ @m0go9
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go9:
{PROCEDURE DrawLowerRightTile with WriteMode0: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ StartWritePlane = first bitplane to write to }
{ rightcut MOD 4 = 0 }
{ rightcut, bottomcut, Win*, SCROLLADR,...}
{out: ES = ^graphic segment }
{rem: WriteMode0 has been set already and remains set }
PUSH BP
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV CX,16
SUB CX,bottomcut
MOV BX,16+3
SUB BX,rightcut
PUSH BX
SHR BX,1
SHR BX,1 {BX = BytesPerPlane = (16 + 3 - rightcut) DIV 4}
MOV DS,SCROLLADR
MOV DX,3C4h
MOV AX,StartWritePlane
OUT DX,AX
PUSH AX
MOV DX,3CEh
MOV AX,0004h
OUT DX,AX
MOV AX,LINESIZE
SUB AX,BX
MOV DX,4
SUB DX,BX
MOV BP,BX {BP = bytes per row }
MOV BL,CL {BL = row counter }
MOV BH,CL {copy to BH }
PUSH SI
PUSH DI
@m0eineZeile4g1:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BL
JNZ @m0eineZeile4g1
POP DI
POP SI
MOV BL,BH
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap9a
MOV AH,1
INC DI
@nowrap9a:
MOV DX,3C4h
OUT DX,AX
POP BP
DEC BP
PUSH BP
PUSH AX
MOV DX,3CEh
MOV AX,0104h
OUT DX,AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH SI
PUSH DI
@m0eineZeile4g2:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BL
JNZ @m0eineZeile4g2
POP DI
POP SI
MOV BL,BH
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap9b
MOV AH,1
INC DI
@nowrap9b:
MOV DX,3C4h
OUT DX,AX
POP BP
DEC BP
PUSH BP
PUSH AX
MOV DX,3CEh
MOV AX,0204h
OUT DX,AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
PUSH SI
PUSH DI
@m0eineZeile4g3:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BL
JNZ @m0eineZeile4g3
POP DI
POP SI
MOV BL,BH
POP AX
SHL AH,1
CMP AH,16
JNE @nowrap9c
MOV AH,1
INC DI
@nowrap9c:
MOV DX,3C4h
OUT DX,AX
POP BP
DEC BP
MOV DX,3CEh
MOV AX,0304h
OUT DX,AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
@m0eineZeile4g4:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BL
JNZ @m0eineZeile4g4
POP BP
MOV AX,SEG @DATA
MOV DS,AX
POP DI {ES:DI etc. point to lower right corner tile }
@m0SkipLowerRightCorner:
CMP leftcut,0
JE @m0fertig
{now position on lower left corner tile: }
MOV AX,innerTilesX
INC AX
SUB actIndex,AX {dec(actIndex,innerTilesX + 1) }
SUB xtil,AX {dec(xtil,innerTilesX + 1) }
MOV CL,2
SHL AX,CL
SUB DI,AX {dec(DI,4 * (innerTilesX + 1) }
ADD DI,leftcutDIV4 {don't forget: corner tile may be cut at its left side}
INC DI
{ MOV AX,WinXMIN }
{ MOV x,AX }
MOV SI,actIndex
AND SI,Yoffscreen
JZ @m0go7
MOV AX,xtil
CWD
SUB AX,XTiles
NOT AX
OR AX,DX
CWD
NOT DX
AND SI,DX
@m0go7:
{PROCEDURE DrawLowerLeftTile with WriteMode0: }
{ in: ES:DI = ^destination address}
{ SI = tile index }
{ StartLesePlane = first bitplane to read from }
{ rightcut MOD 4 = 0 }
{ leftcut, bottomcut, Win*, SCROLLADR,...}
{out: (ES = ^graphic segment) }
{rem: WriteMode0 has been set (and remains set) }
PUSH BP
MOV AL,[OFFSET BackTile +SI]
XOR AH,AH {compute offset address of tile: }
MOV CL,6 {each tile is 64 bytes in size, thus}
SHL AX,CL {AX := tile * 64 = tile SHL 6 }
MOV SI,AX
MOV AX,leftcut
MOV BX,AX
MOV CL,2
SHR AX,CL
ADD SI,AX
MOV CX,16
SUB CX,bottomcut
MOV DS,SCROLLADR
MOV DX,3C4h
MOV AX,0102h
OUT DX,AX
MOV DX,3CEh
MOV AX,StartLesePlane
OUT DX,AX
MOV BP,16+3
SUB BP,BX
PUSH BP
PUSH AX
SHR BP,1
SHR BP,1 {BP := (16 + 3 - leftcut) DIV 4}
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BL,CL {BL=row counter }
MOV BH,CL {copy of it }
PUSH SI
PUSH DI
@m0eineZeile4d1:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BL
JNZ @m0eineZeile4d1
POP DI
POP SI
POP AX
INC AH
AND AH,3
JNE @nowrap7a
INC SI
@nowrap7a:
MOV DX,3CEh
OUT DX,AX
POP BP
DEC BP
PUSH BP
PUSH AX
MOV DX,3C4h
MOV AX,0202h
OUT DX,AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BL,BH
PUSH SI
PUSH DI
@m0eineZeile4d2:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BL
JNZ @m0eineZeile4d2
POP DI
POP SI
POP AX
INC AH
AND AH,3
JNE @nowrap7b
INC SI
@nowrap7b:
MOV DX,3CEh
OUT DX,AX
POP BP
DEC BP
PUSH BP
PUSH AX
MOV DX,3C4h
MOV AX,0402h
OUT DX,AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BL,BH
PUSH SI
PUSH DI
@m0eineZeile4d3:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BL
JNZ @m0eineZeile4d3
POP DI
POP SI
POP AX
INC AH
AND AH,3
JNE @nowrap7c
INC SI
@nowrap7c:
MOV DX,3CEh
OUT DX,AX
POP BP
DEC BP
MOV DX,3C4h
MOV AX,0802h
OUT DX,AX
SHR BP,1
SHR BP,1
MOV AX,LINESIZE
SUB AX,BP
MOV DX,4
SUB DX,BP
MOV BL,BH
@m0eineZeile4d4:
MOV CX,BP
REP MOVSB
ADD SI,DX
ADD DI,AX
DEC BL
JNZ @m0eineZeile4d4
POP BP
MOV AX,SEG @DATA
MOV DS,AX
@m0fertig:
{------- starting here: put sprites on actual graphic page}
@Sprites_zeichnen:
MOV SI,NMAX*2
PUSH BP {pop BP in the end! }
@zeichne:
CMP SI,SplitIndex_mal2 {split index?}
JNE @SZeich
MOV AX,WinXMIN
{yes: set Win* variables to the whole area (0,0)..(XMAX,YMAX):}
OR AX,WinYMIN
JNZ @replace1
CMP WinXMAX,XMAX
JNE @replace2
CMP WinYMAX,YMAX
JE @replacedone
JMP @replace3 {short}
@replace1:
MOV WinXMIN,0
MOV WinYMIN,0
MOV WinXMINdiv4,0
MOV WinYMIN_mul_LINESIZE,0
MOV WinYMINmLINESIZEaWinXMINdiv4,0
@replace2:
MOV WinXMAX,XMAX
MOV WinWidth,XMAX+1
MOV WinWidthDiv4,(XMAX+1)/4
@replace3:
MOV WinYMAX,YMAX
MOV WinHeight,YMAX+1
MOV WinLowerRight,0
@replacedone:
{DS = normal data segment, ES = graphic page segment, }
{SI = sprite position number * 2 }
@SZeich:
MOV BX,[SI + OFFSET SpriteN] {BX = SpriteN[?] = sprite load number}
SHL BX,1 {BX = sprite load number * 2}
{now: compute "SpriteN[?] := SpriteN[NextSprite[?]]" }
MOV BX,[BX + OFFSET NextSprite] {AX = NextSprite[SpriteN[?]]}
MOV [SI + OFFSET SpriteN],BX {use it as new SpriteN[?] value}
SHL BX,1
JNZ @aktiv
JMP @noSprite
@aktiv:
PUSH SI {save sprite position number * 2 }
MOV DX,[SI + OFFSET SpriteX] {if SpriteX > xmax then skip_sprite}
SUB DX,StartVirtualX {virtual -> absolute coordinates }
MOV AX,WinXMAX
CMP DX,AX
JLE @L0
@ToSprite_fertig: {jump-rail to @Sprite_fertig }
JMP @Sprite_fertig
@L0:
MOV CS:WORD PTR @akt_SpriteX+1,DX
MOV DI,[SI + OFFSET SpriteY] {DI = SpriteY_virtual }
SUB DI,StartVirtualY {DI = SpriteY (absolute!)}
PUSH AX {WinXMAX}
MOV AX,WinYMIN
MOV SI,WinXMIN
MOV CX,WinYMAX {save old Win* values... }
MOV DS,[BX + OFFSET SPRITEAD] {!!!DS = ^sprite data !!!}
MOV [WinYMIN_],AX {...and store them in new DS segment}
MOV [WinXMIN_],SI
POP AX
MOV [WinXMAX_],AX {hold WinYMAX in CX }
MOV AX,[Breite] {AX = width in groups of 4 }
MOV CS:WORD PTR @max_Breite+1,AX
MOV SI,AX {SI = dto.}
SHL AX,1
SHL AX,1 {AX = max_width_in_points }
ADD AX,DX {AX = max_width_in_points+SpriteX }
CMP AX,[WinXMIN_] {is the right end to the left of the window area?}
JL @ToSprite_fertig
MOV BX,DI {if SpriteY - WinYMIN >= 0 }
SUB DI,[WinYMIN_] { then starty := 0}
NEG DI { else starty := -(SpriteY - WinYMIN)}
MOV BP,DI
JG @Top_cut
XOR DI,DI
@Top_cut: {DI = starty, BP = -(SpriteY - WinYMIN)}
MOV AX,[Hoehe] {AX = height (in rows) }
CMP DI,AX {if starty >= height then skip_sprite}
JGE @ToSprite_fertig
ADD BP,CX {BP = -(SpriteY - WinYMIN) + WinYMAX}
SUB BP,[WinYMIN_]
JL @ToSprite_fertig {(a bit lax:) }
CMP AX,BP {if height + SpriteY > WinYMAX }
JG @To_then { then [ endy := WinYMAX - SpriteY }
DEC AX { if endy < 0 then skip_sprite ] }
MOV BP,AX { else endy := height - 1 }
{BP = endy, SI=[@max_Breite+1] = max_width_in_groups of 4, }
{DI = starty, BX = SpriteY, DX=[@akt_SpriteX+1] = SpriteX, }
{DS = ^sprite data, ES = ^graphic page}
@To_then:
MOV AX,BP
SUB BP,DI
SHL BP,1
MOV [End_min_Start],BP {= (endy - starty) * 2 =Yactual * 2 }
ADD BX,AX
SHL BX,1
MOV BX,CS:[OFFSET gadr + BX] {BX =zeilenadr :=(endy + SpriteY) * LINESIZE}
MOV [zeilenadr],BX {store it in [zeilenadr], too }
MOV BP,DX
MUL SI {AX = endy * max_width_in_groups_of_4 = yoffset}
MOV [yoffset_],AX {store it in [yoffset_], too}
SHL DI,1 {DI = starty * 2}
MOV CS:WORD PTR @Starty_2+1,DI {store in [@Starty_2 + 1], too }
{Now: look at sprite's mode byte and determine if an other than the }
{momentary active sprite display method is needed; if so, then copy }
{in the right one! }
{used registers: AX and SI }
MOV AL,[Modus] {get mode byte of the sprite}
XOR AH,AH
SHL AX,1
MOV SI,AX
MOV SI,CS:[OFFSET Adressen +SI] {get pointer to according routine }
MOV AX,CS:[SI]
CMP AX,CS:[WORD PTR @Patch1] {is this routine already active? }
JE @no_newcode {yes, nothing to do}
PUSH DS {no, copy routine to all places }
PUSH CS {where it will be needed}
POP DS
MOV [WORD PTR @Patch1],AX
MOV [WORD PTR @Patch2],AX
MOV [WORD PTR @Patch3],AX
MOV [WORD PTR @Patch4],AX
INC SI
INC SI
LODSW
MOV [WORD PTR @Patch1+2],AX
MOV [WORD PTR @Patch2+2],AX
MOV [WORD PTR @Patch3+2],AX
MOV [WORD PTR @Patch4+2],AX
LODSW
MOV [WORD PTR @Patch1+4],AX
MOV [WORD PTR @Patch2+4],AX
MOV [WORD PTR @Patch3+4],AX
MOV [WORD PTR @Patch4+4],AX
LODSW
MOV [WORD PTR @Patch1+6],AX
MOV [WORD PTR @Patch2+6],AX
MOV [WORD PTR @Patch3+6],AX
MOV [WORD PTR @Patch4+6],AX
LODSW
MOV [WORD PTR @Patch1+8],AX
MOV [WORD PTR @Patch2+8],AX
MOV [WORD PTR @Patch3+8],AX
MOV [WORD PTR @Patch4+8],AX
LODSW
MOV [WORD PTR @Patch1+10],AX
MOV [WORD PTR @Patch2+10],AX
MOV [WORD PTR @Patch3+10],AX
MOV [WORD PTR @Patch4+10],AX
LODSW
MOV [WORD PTR @Patch1+12],AX
MOV [WORD PTR @Patch2+12],AX
MOV [WORD PTR @Patch3+12],AX
MOV [WORD PTR @Patch4+12],AX
LODSW
MOV [WORD PTR @Patch1+14],AX
MOV [WORD PTR @Patch2+14],AX
MOV [WORD PTR @Patch3+14],AX
MOV [WORD PTR @Patch4+14],AX
POP DS {restore DS }
@no_newcode:
{(AX=)[yoffset_] = yoffset }
{ BX = [zeilenadr] = (endy + SpriteY) * LINESIZE}
{ CX = WinYMAX }
{ DI = [@Starty_2 + 1] = starty * 2}
{(SI = [@max_Breite + 1] = max_width_in_groups_of_4) }
{ BP = [@akt_SpriteX + 1]= SpriteX}
{ DS = ^sprite data}
{ ES = ^graphic page}
{ [end_min_start] = (endy - starty) * 2 = Yactual * 2 }
{ [@max_Breite + 1] = max_width_in_groups_of_4 }
@eine_Zeile:
MOV SI,[end_min_start] {SI = Yactual * 2 }
ADD SI,DI {startx := sprite[WORD PTR sprite[L] + }
MOV DI,SI { (Yactual + starty) * 2] }
ADD SI,[Left]
MOV SI,[SI] {SI = startx, DI = (Yactual + starty) * 2 }
MOV AX,BP
MOV DX,AX {AX = DX = SpriteX}
SUB BP,[WinXMIN_] {BP = SpriteX - WinXMIN}
ADD AX,SI {AX = ScreenStartX := SpriteX + startx }
CMP AX,[WinXMAX_] {if ScreenStartX > WinXMAX then skip_zeile }
JG @ToZeile_fertig
MOV CX,SI {CX = startx}
SUB AX,[WinXMIN_] {lecutoff_in_points := startx }
JGE @L1 {if ScreenStartX < WinXMIN then }
SUB SI,AX { [dec(startx,ScreenStartX - WinXMIN) }
XOR AX,AX { ScreenStartX := WinXMIN }
MOV CX,BP { lecutoff_in_points := -SpriteX] }
NEG CX
@L1:
ADD AX,[WinXMIN_] {CX = [licutoff_] = lecutoff_in_points, }
MOV [licutoff_],CX {SI = startx, AX = ScreenStartX }
ADD DI,[Right]
MOV DI,[DI] {DI = endx := sprite[WORD PTR sprite[R] + }
{ (Yactual + starty) * 2] }
NEG DX {DX = -SpriteX }
MOV BP,DI
SUB BP,SI {BP = endx - startx }
SUB DX,DI {DX = -(SpriteX + endx) }
ADD DX,[WinXMAX_] {DX = overhang := WinXMAX - (SpriteX + endx) }
JNS @kein_Ueberhang_rechts
ADD BP,DX
@kein_Ueberhang_rechts: {BP = visible width of this row -1 }
OR BP,BP
JNS @L6
@ToZeile_fertig:
JMP @Zeile_fertig {if width <= 0 then skip_zeile }
@L6:
ADD BP,4
{ AX = ScreenStartX }
{ BX = [zeilenadr] = (endy + SpriteY) * LINESIZE }
{ CX = [licutoff_] = lecutoff_in_points }
{(DX = (negative) overhang (if value < 0) ) }
{(SI = startx) }
{(DI = endx) }
{ BP = width of this row in pixels + 3 }
{ DS = ^sprite data}
{ ES = ^graphic page}
{ [@max_Breite + 1] = max_width_in_groups_of_4) }
{ [end_min_start] = (endy - starty) * 2 =Yactual * 2 }
{ [@Starty_2 + 1] = starty * 2}
{ [@max_Breite + 1] = max_width_in_groups_of_4, }
{ [@akt_SpriteX + 1] = SpriteX}
MOV [bildx],AX {save ScreenStartX }
MOV DX,CX {DX = lecutoff_in_points }
MOV CX,BP
SHR CX,1
SHR CX,1 {CX = width DIV 4 }
JCXZ @Plane1
{SI = source pointer := sprite[WORD PTR (lecutoff_in_points + 0 AND 3) * 2}
{ + (lecutoff_in_points + 0) DIV 4 + yoffset }
MOV SI,DX
AND SI,3
SHL SI,1 {SI = ((lecutoff_in_points + 0) AND 3) * 2 }
MOV SI,[SI]
MOV DI,DX
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_] {SI = sprite[WORD PTR (licutoff_...)] }
{ + (lecutoff_in_points + i) DIV 4 }
{ + yoffset }
{DI = destination pointer := (ScreenStartX + 0) DIV 4 + zeilenadr}
MOV DI,AX {DI = ScreenStartX }
SHR DI,1
SHR DI,1
ADD DI,BX
MOV BL,AL
AND BX,3 {BX = (ScreenStartX + i) AND 3 }
MOV AH,Translate[BX] {AH = 1,2,4,8 for BX = 0,1,2,3 }
MOV AL,2
MOV DX,3C4h
OUT DX,AX {choose plane }
XCHG BX,DI
{copy CX bytes from DS:SI to ES:BX }
{this is the address where the data transfer routine will be patched!}
@Patch1:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane1:
MOV DX,[bildx]
INC DX {DX = ScreenStartX+1 }
MOV BX,DX
SHR BX,1
SHR BX,1 {BX = destination pointer := (ScreenStartX + 1) }
ADD BX,[zeilenadr] { DIV 4 + zeilenadr }
MOV CX,BP
DEC CX {CX = width of this line + 3 - 1 }
SHR CX,1
SHR CX,1 {CX = bytes_to_move for i = 1 }
JCXZ @Plane2
MOV DI,[licutoff_]
INC DI {DI = (lecutoff_in_points + 1) }
MOV SI,DI
AND SI,3
SHL SI,1 {SI = ((lecutoff_in_points + 1) AND 3) * 2 }
MOV SI,[SI] {SI = sprite[WORD PTR licutoff_...] }
SHR DI,1 { + (lecutoff_in_points + 1) DIV 4 }
SHR DI,1 { + yoffset }
ADD SI,DI
ADD SI,[yoffset_] {SI = source pointer, }
{DI = (lecutoff_in_points + 1) DIV 4 }
MOV DI,DX {DI = ScreenStartX + 1 }
AND DI,3 {DI = (ScreenStartX + 1) AND 3 }
MOV AH,Translate[DI] {load mask for port-access }
MOV AL,2
MOV DX,3C4h {select plane }
OUT DX,AX
{this is the address where the data transfer routine will be patched!}
@Patch2:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane2:
MOV DX,[bildx]
ADD DX,2
MOV BX,DX
SHR BX,1
SHR BX,1
ADD BX,[zeilenadr]
MOV CX,BP
SUB CX,2
SHR CX,1
SHR CX,1
JCXZ @Plane3
MOV DI,[licutoff_]
ADD DI,2
MOV SI,DI
AND SI,3
SHL SI,1
MOV SI,[SI]
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_]
MOV DI,DX {DI = ScreenStartX + 2 }
AND DI,3 {DI = (ScreenStartX + 1) AND 3 }
MOV AH,Translate[DI]
MOV AL,2
MOV DX,3C4h
OUT DX,AX
{this is the address where the data transfer routine will be patched!}
@Patch3:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Plane3:
MOV DX,[bildx]
ADD DX,3
MOV BX,DX
SHR BX,1
SHR BX,1
ADD BX,[zeilenadr]
MOV CX,BP
SUB CX,3
SHR CX,1
SHR CX,1
JCXZ @Zeile_fertig
MOV DI,[licutoff_]
ADD DI,3
MOV SI,DI
AND SI,3
SHL SI,1
MOV SI,[SI]
SHR DI,1
SHR DI,1
ADD SI,DI
ADD SI,[yoffset_]
MOV DI,DX {DI = ScreenStartX + 3 }
AND DI,3 {DI = (ScreenStartX + 1) AND 3 }
MOV AH,Translate[DI]
MOV AL,2
MOV DX,3C4h
OUT DX,AX
{this is the address where the data transfer routine will be patched!}
@Patch4:
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
@Zeile_fertig:
MOV AX,[yoffset_]
@max_Breite:
SUB AX,1234
MOV [yoffset_],AX
MOV BX,[zeilenadr]
SUB BX,LINESIZE
MOV [zeilenadr],BX
SUB WORD PTR [end_min_start],2
JS @Sprite_fertig
@Starty_2:
MOV DI,1234
@akt_SpriteX:
MOV BP,1234
JMP @eine_Zeile
@Sprite_fertig:
POP SI
MOV AX,SEG @Data
MOV DS,AX
@noSprite:
DEC SI
DEC SI
JS @fertig
JMP @zeichne
@fertig:
POP BP
{reset Win* variables to their old values --if necessary: }
MOV AX,SplitIndex
CMP AX,NMAX
JGE @skip {IF (SplitIndex<0) OR (SplitIndex>NMAX) THEN Skip}
MOV AX,BWinLowerRight
MOV WinLowerRight,AX
MOV BX,BWinXMIN
MOV WinXMIN,BX
MOV SI,BX {SI := WinXMIN}
SHR BX,1
SHR BX,1
MOV WinXMINdiv4,BX {BX := WinXMIN div 4}
MOV CX,BWinYMIN
MOV WinYMIN,CX {CX := WinYMIN}
MOV AX,BWinYMIN_mul_LINESIZE
MOV WinYMIN_mul_LINESIZE,AX
ADD AX,BX
MOV WinYMINmLINESIZEaWinXMINdiv4,AX
MOV AX,BWinXMAX
MOV WinXMAX,AX {AX := WinXMAX}
MOV DX,BWinYMAX
MOV WinYMAX,DX {DX := WinYMAX}
SUB DX,CX
INC DX
MOV WinHeight,DX
SUB AX,SI
INC AX
MOV WinWidth,AX
SHR AX,1
SHR AX,1
MOV WinWidthDiv4,AL
@skip:
{prepare "CX := Offset_Adr[Page]": }
MOV SI,PAGE {page value *2 (word-sized entries!)}
MOV BX,SI {save page value in BX!}
SHL SI,1 {add start address of array to that }
ADD SI,OFFSET Offset_Adr-StartIndex*2 {evtl. correct displacement }
LODSW {realize "AX := Offset_Adr[Page]" }
MOV CX,AX {bring value to CX}
MOV DI,CRTAddress {DI := CRTAddress }
{The graphic page is now ready to be displayed: }
cli
mov dx,StatusReg
{Wait for "display enable"=0 (that is: active), so that page flipping }
{for HB/LB takes place while displaying the same page:}
@WaitNotHSyncLoop:
in al,dx
and al,1
jz @WaitNotHSyncLoop
@WaitHSyncLoop:
in al,dx
and al,1
jnz @WaitHSyncLoop
MOV DX,DI {DX := CRTAddress}
MOV AL,$0D {LB-startaddress-register}
OUT DX,AL
INC DX
MOV AX,CX {AX := Offset_Adr[Page]}
OUT DX,AL {set LB of new starting address }
DEC DX
MOV AL,$0C
OUT DX,AL
INC DX
MOV AL,AH {set HB of new starting address }
OUT DX,AL
STI
NEG BX {new PAGE-value := 1-old PAGE-value, that is: }
ADD BX,1 {IF PAGE = 0 THEN PAGE := 1 ELSE (PAGE = 1) PAGE := 0 }
MOV PAGE,BX
SHL BX,1 {new PAGEADR-value := Segment_Adr[PAGE] }
ADD BX,OFFSET Segment_Adr-StartIndex*2
MOV AX,[BX]
MOV PAGEADR,AX
{Check whether the preset (min.) cycle time has passed: }
@L10:
MOV AL,TimeFlag {bit 7 = 0/1 for delay completed/not yet completed }
AND AL,$80
JE @L10
{start time control mechanism for next cycle:}
MOV AL,IsAT {is this an AT/386? ($0/$80 = yes/no) }
OR AL,AL {timing mechanism only works on AT/386 }
JNE @L11 {otherwise: no timing mechanism! }
MOV TimeFlag,AL {AL = 0 use this as initial value, too }
MOV DX,WORD PTR CycleTime {store min. time for 1 cycle (micro- }
MOV CX,WORD PTR CycleTime+2 {seconds): CX = HIGH-word, DX = LOW-word }
MOV BX,OFFSET TimeFlag {ES:BX = pointer to TimeFlag, bit 7 = 0/1}
MOV AX,DS {for: delay still lasts/has ended }
MOV ES,AX
MOV AX,8300h {start time control mechanism}
INT 15h
@L11:
END; {of ASM}
END; {of ANIMATE}
PROCEDURE FreeSpriteMem(number:WORD);
{ in: number = sprite load number of the sprite to deallocate }
{out: allocated memory has been released, SPRITEAD[number] has been set to 0 and}
{ SPRITEPTR[number] has been set to NIL}
{rem: sprite numbers referencing to the released sprite contents }
{ are not allowed after this routine, of course!}
{ If no sprite "number" had been loaded, nothing at all will happen}
{ evtl. sprite cycles must be freed by calling this routine for each}
{ of its members once. }
BEGIN
IF (number<1) OR (number>LoadMAX)
THEN Error:=Err_InvalidSpriteLoadNumber
ELSE IF SPRITEPTR[number]<>NIL
THEN BEGIN
FreeMem(SPRITEPTR[number],SPRITESIZE[number]);
SPRITEPTR[number]:=NIL;
SPRITESIZE[number]:=0;
SPRITEAD[number]:=0
END
END;
FUNCTION LoadSprite(name:String; number:WORD):WORD;
{ in: name = name of the sprite file to load (type: "*.COD" / "*.LIB") }
{ number = number for the first sprite of this file }
{out: number of the sprites read in from the file (0 = error occured) }
{The routine detects automatically, if the file holfds a single }
{ tains a single sprite or a complete sprite library and loads all }
{ sprite data onto the heap, in a way such that the address always }
{ lies on a segment (=paragraph) boundary. These starting addresses }
{ are then stored in table SPRITEAD[number]; if more than one sprite }
{ has been loaded, they will be stored with consecutive numbers, that }
{ is, number+i }
LABEL quit_loop;
VAR p1,p2:Pointer;
f:FileOfByte;
count:WORD;
Header:SpriteHeader;
tempName:STRING;
BEGIN
count:=0; {number of sprites already read in }
tempName:=FindFile(name);
IF tempName<>'' THEN name:=tempName;
_assign(f,name);
{$I-} _reset(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
if (ioresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN {File doesn't exist or at least not with that path }
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
loadSprite:=0; exit
END;
IF (number=0) or (number>LoadMAX)
THEN BEGIN
Error:=Err_InvalidSpritenumber;
goto quit_loop;
END;
WHILE NOT _physicalEOF(f) DO
BEGIN
{first, read in the sprite header: }
{$I-} {load sprite header vià BLOCKREAD into the heap }
_blockread(f,Header,Kopf);
{$IFDEF IOcheck} {$I+} {$ENDIF}
IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
goto quit_loop;
END;
IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
THEN BEGIN
Error:=Err_NoSprite;
goto quit_loop;
END;
{enough space left? }
IF (Header.SpriteLength+15>MaxAvail+SPRITESIZE[number+count])
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
FreeSpriteMem(number+count); {evtl. release old memory }
{Now read in the real sprite data: }
getmem(p1,Header.SpriteLength+15); {get enough space }
SPRITESIZE[number+count]:=Header.SpriteLength+15;
SPRITEPTR [number+count]:=p1;
IF (LONGINT(p1) mod 16)=0
THEN p2:=p1 {make p2 fall on segment boundary}
ELSE LONGINT(p2):=LONGINT(p1) + (16-LONGINT(p1) mod 16);
MOVE(Header,p2^,Kopf); {store sprite header to heap }
LONGINT(p1):=LONGINT(p2)+Kopf; {points exactly behind the header}
{$I-} {load the "rest" of the sprite }
_blockread(f,p1^,Header.SpriteLength-Kopf);
{$IFDEF IOcheck} {$I+} {$ENDIF}
IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
goto quit_loop;
END;
{assign it to the sprite number:}
spritead[number+count]:=(longint(p2) shr 16)
+(longint(p2) and 65535) shr 4;
INC(count);
IF (NOT _physicalEOF(f)) AND
( (NOT f.komprimiert) OR (_logicalEOF(f)) )
THEN Resync(f)
END;
quit_loop: ;
_close(f);
loadSprite:=count
END;
FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
{ in: name = name of the sprite file to load (type: "*.COD" / "*.LIB") }
{ number = 0..255 = tile number for the file's first sprite }
{out: number of the tiles read in from the file (0 = error occured) }
{The routine detects automatically, if the file holfds a single }
{ sprite or a complete sprite library. Always all sprites in the file }
{ sprites, splits them into tiles and stores them into the 4th graphic }
{ page, starting with the given number "number" }
{ Because a tile consists of 16x16 points, the sprites must be a mul- }
{ tiple of 16 points in each direction (x _and_ y) }
{ If the file contains more than one tile, they will be loaded by row, }
{ each row from left to right }
LABEL quit_loop;
TYPE split=RECORD loword,hiword:WORD END;
VAR p1:Pointer;
ad,p:LONGINT;
f:FileOfByte;
count,ZielOfs,step,yoffset:WORD;
pSeg,pOfs:ARRAY[0..3] OF WORD;
Breite_in_Tiles,Hoehe_in_Tiles,x,y,i,zeilen:BYTE;
Header:SpriteHeader;
tempName:STRING;
BEGIN
count:=0; {number of sprites already read in }
tempName:=FindFile(name);
IF tempName<>'' THEN name:=tempName;
_assign(f,name);
{$I-} _reset(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
if (ioresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN {File doesn't exist or at least not with that path }
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
LoadTile:=0; exit
END;
WHILE NOT _physicalEOF(f) DO
BEGIN
{first, read in the sprite header: }
{$I-} {load sprite header vià BLOCKREAD into the heap }
_blockread(f,Header,Kopf);
{$IFDEF IOcheck} {$I+} {$ENDIF}
IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
goto quit_loop;
END;
IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
THEN BEGIN
Error:=Err_NoTile; {or Err_NoSprite! }
goto quit_loop
END;
IF (Header.Breite_in_4er_Gruppen MOD 4<>0) OR
(Header.Hoehe_in_Zeilen MOD 16<>0) {size a multiple of 16? }
THEN BEGIN
Error:=Err_NoTile;
goto quit_loop
END
ELSE BEGIN {yes, get number of tiles in that sprite file }
Breite_in_Tiles:=Header.Breite_in_4er_Gruppen SHR 2;
Hoehe_in_Tiles :=Header.Hoehe_in_Zeilen SHR 4;
step:=Breite_in_Tiles*4; {"step" needed for correct addressing}
END;
IF (Header.SpriteLength>MaxAvail) {enough space left? }
THEN BEGIN
Error:=Err_NotEnoughMemory;
goto quit_loop;
END;
{Now read in the real sprite data: }
getmem(p1,Header.SpriteLength); {get enough space }
{$I-} {load the "rest" of the sprite }
_blockread(f,p1^,Header.SpriteLength-Kopf);
{$IFDEF IOcheck} {$I+} {$ENDIF}
IF (ioresult<>0) OR (CompressError<>CompressErr_NoError)
THEN BEGIN
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
goto quit_loop;
END;
ad:=(LONGINT(split(p1).HiWord) SHL 4) + split(p1).LoWord - Kopf;
FOR i:=0 TO 3 DO
BEGIN
p:=ad+Header.Zeiger_auf_Plane[i]; pSeg[i]:=p SHR 4; pOfs[i]:=p AND $F;
END;
FOR y:=0 TO Pred(Hoehe_in_Tiles) DO
BEGIN
yoffset:=y*Breite_in_Tiles*16*(16 DIV 4);
FOR x:=0 TO Pred(Breite_in_Tiles) DO
BEGIN
IF count+number>255
THEN BEGIN
Error:=Err_InvalidTileNumber;
goto quit_loop
END;
ZielOfs:=(number+count) SHL 6;
FOR i:=0 TO 3 DO
BEGIN
PORTW[$3C4]:=(TranslateTab[i] SHL 8) + 2;
FOR zeilen:=0 TO 15 DO
BEGIN
move(mem[pSeg[i]:pOfs[i] + yoffset + zeilen*step + x*(16 DIV 4)],
mem[SCROLLADR:ZielOfs + zeilen*(16 DIV 4)],
16 DIV 4);
END;
END;
INC(count);
END;
END;
FreeMem(p1,Header.SpriteLength); {release memory }
IF (NOT _physicalEOF(f)) AND
( (NOT f.komprimiert) OR (_logicalEOF(f)) )
THEN Resync(f)
END;
quit_loop: ;
_close(f);
LoadTile:=count
END;
PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
{ in: (x1,y1) = upper left corner of the area (in virtual coord.)}
{ (x2,y2) = dto., lower right corner}
{out: (BackX1,BackY1), (BackX2,BackY2) = coord., rounded to 16'grid }
{ XTiles, YTiles = width and height of the chosen range in tiles }
{rem: The upper left corner will be torn to the upper left, the lower }
{ right corner to the lower right!}
{ Obviously, calling the routine only makes sense when using SCROLLING }
{ as background mode! }
BEGIN
BackX1:=x1 AND $FFF0; BackX2:=x2 OR $F;
BackY1:=y1 AND $FFF0; BackY2:=y2 OR $F;
xtiles:=succ(BackX2-BackX1) shr 4;
ytiles:=succ(BackY2-BackY1) shr 4;
IF (xtiles OR ytiles)<=0
THEN Error:=Err_InvalidCoordinates
ELSE IF xtiles*ytiles>MaxTiles
THEN Error:=Err_BackgroundToBig;
END;
PROCEDURE SetBackgroundMode(mode:BYTE);
{ in: mode = wanted background mode, STATIC or SCROLLING }
{out: Backgroundmode = set mode, STATIC/SCROLLING }
BEGIN
IF (mode<>STATIC) AND (mode<>SCROLLING)
THEN Error:=Err_InvalidMode
ELSE Backgroundmode:=mode
END;
PROCEDURE MakeTileArea(FirstTile:BYTE; TileWidth,TileHeight:INTEGER);
{ in: FirstTile = starting index of first tile }
{ TileWidth = width (in tiles) of the rectangular pattern to be repeated}
{ TileHeight= dto., height}
{ BackX1,BackY1,BackX2,BackY2= background area set by calling }
{ SetScrollRange() }
{out: Starting with tile no. FirstTile, the next TileWidth*TileHeight }
{ tiles have been used as a repeating rectangular pattern of }
{ width TileWidth and height TileHeight into the background. }
{ This way, the complete defined background area has been built }
{ For example, MakeTileArea(7,3,2) will result in the following }
{ layout: }
{ ┌──┬──┬──┬──┬──┬──┬──┬ }
{ │ 7│ 8│ 9│ 7│ 8│ 9│ 7│... }
{ ├──┼──┼──┼──┼──┼──┼──┼ }
{ │10│11│12│10│11│12│10│... }
{ ├──┼──┼──┼──┼──┼──┼──┼ }
{ │ 7│ 8│ 9│ 7│ 8│ 9│ 7│... }
{ ├──┼──┼──┼──┼──┼──┼──┼ }
{ │10│11│12│10│11│12│10│... }
{ ├──┼──┼──┼──┼──┼──┼──┼ }
{ . . . . . . . }
{ . . . . . . . }
{rem: Calling this routine only makes sense, when SCROLLING has been}
{ set as background mode and SetScrollRange() has been called }
{ previously!}
VAR GY,StartRowTile,
x,y:INTEGER;
BEGIN
IF (TileWidth>0) AND (TileHeight>0)
THEN BEGIN
FOR y:=0 TO ytiles-1 DO
BEGIN
gy:=BackY1+(y SHL 4); {y-coordinate for this row }
{compute index of 1st tile of the actual row: }
StartRowTile:=(y MOD TileHeight)*TileWidth+FirstTile;
FOR x:=0 TO xtiles-1 DO
PutTile(BackX1+(x SHL 4),gy,StartRowTile+(x MOD TileWidth));
END
END
ELSE Error := Err_InvalidCoordinates
END;
PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
{ in: x,y = virtual coordinates where the tile shall be placed }
{ TileNr= number of the tile to be placed }
{out: - }
{rem: The point (x,y) first gets rounded to a grid with mesh 16 }
{ Calling this routine only makes sense when using SCROLLING}
{ as background mode! }
VAR index:WORD;
BEGIN
ASM
MOV AX,x {compute relative X-distance from left edge of }
SUB AX,BackX1 {the defined area and store it to "x", formula: }
SAR AX,1 { x := ((x AND $FFF0) - BackX1) DIV 16 (nicht: }
SAR AX,1 {SHR 4)! "AND $FFF0" kann dabei entfallen, da in}
SAR AX,1 {BackX1 last hex-digit is $0! }
SAR AX,1
MOV x,AX
MOV AX,y {dto. for distance between the y-coordinate and }
SUB AX,BackY1 {the upper edge: y := ((y AND $FFF0) - BackY1) DIV 16 }
SAR AX,1
SAR AX,1
SAR AX,1
SAR AX,1
MOV y,AX
END;
IF (x<0) OR (x>=XTiles) OR (y<0) OR (y>=YTiles)
THEN Error:=Err_InvalidCoordinates
ELSE BEGIN {a tile row has width XTiles, each tile consists of 16x16 points}
index:=y*XTiles+x; {to be exact: (x MOD XTiles)}
BackTile[Succ(index)]:=TileNr; {"succ", to hold free BackTile[0] }
END;
END;
FUNCTION GetTile(x,y:INTEGER):BYTE;
{ in: x,y = virtual coordinate for which the corresp. tile should be computed}
{out: no. of the tile at this position }
{rem: The point (x,y) first gets rounded to a grid with mesh 16}
{ If the point lies outside the defined scroll area, then the }
{ routine returns the value BackTile[0] of the offscreen tile}
BEGIN
ASM
MOV AX,x {compute relative X-distance from left edge of }
SUB AX,BackX1 {the defined area and store it to "x", formula: }
SAR AX,1 { x := ((x AND $FFF0) - BackX1) DIV 16 (nicht: }
SAR AX,1 {SHR 4)! "AND $FFF0" kann dabei entfallen, da in}
SAR AX,1 {BackX1 last hex-digit is $0! }
SAR AX,1
MOV x,AX
MOV AX,y {dto. for distance between the y-coordinate and }
SUB AX,BackY1 {the upper edge: y := ((y AND $FFF0) - BackY1) DIV 16 }
SAR AX,1
SAR AX,1
SAR AX,1
SAR AX,1
MOV y,AX
END;
IF (x<0) OR (x>=XTiles) OR (y<0) OR (y>=YTiles)
THEN GetTile:=BackTile[0]
ELSE GetTile:=BackTile[y*XTiles+x+1]
END;
PROCEDURE SetOffscreenTile(TileNr:BYTE);
{ in: TileNr= number of the tile to be placed }
{out: - }
{rem: all screen parts, which lie outside the window specified by }
{ SetBackgroundScrollRange will become the tile TileNr as }
{ pattern }
{ Calling this routine only makes sense when using SCROLLING }
{ as background mode! }
BEGIN
BackTile[0]:=TileNr
END;
PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
{ in: Sp = spriteLOADnumber, which mode byte shall be changed }
{out: M = method, which shall be used to display Sp from now on: }
{ Display_NORMAL, Display_FAST, Display_SHADOW or }
{ Display_SHADOWEXACT }
{rem: If the sprite doesn't exist yet (or the mode isn't allowed), }
{ nothing will happen at all }
VAR ad:WORD;
BEGIN
ad:=SPRITEAD[Sp];
IF ad=0 THEN Error:=Err_InvalidSpriteNumber {sprite must already be loaded}
ELSE IF (M<Display_NORMAL) OR (M>Display_SHADOWEXACT)
THEN Error:=Err_InvalidMode {only these 4 modes are allowed}
ELSE MEM[ad:Modus]:=M
END;
FUNCTION GetModeByte(Sp:WORD):BYTE;
{ in: Sp = spriteLOADnumber, which mode byte shall be determined }
{out: actually set display method for sprite Sp: Display_NORMAL, }
{ Display_FAST, Display_SHADOW, Display_SHADOWEXACT or }
{ Display_UNKNOW, if the sprite hasn't been loaded, yet! }
VAR ad:WORD;
BEGIN
ad:=SPRITEAD[Sp];
IF (ad=0)
THEN GetModeByte:=Display_UNKNOWN {sprite not yet loaded }
ELSE GetModeByte:=MEM[SPRITEAD[Sp]:Modus]
END;
PROCEDURE FillBackground(color:BYTE);
{ in: color = color for filling the background page BACKGNDPAGE }
{ BACKGNDADR = pointer to background memory }
{out: The graphic page BACKGNDPAGE has been filled with color "Color"}
{rem: Using the routine only makes sense when using background mode STATIC }
BEGIN
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
FillChar(MEM[BACKGNDADR:0],4*PAGESIZE,color);
END;
PROCEDURE FillPage(pa:WORD; color:BYTE);
{ in: pa = the page which shall be filled (0..3) }
{ color = color with which to fill the page}
{out: graphic page "pa" has been filled with color "Color"}
{rem: It only makes sense to use pages 0,1 and BACKGNDPAGE, }
{ but SCROLLPAGE is allowed, too }
BEGIN
IF (pa<0) OR (pa>SCROLLPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE IF pa=BACKGNDPAGE
THEN FillBackground(color)
ELSE BEGIN
portw[$3C4]:=$0F02; {use MapMask register to select all 4 planes }
fillchar(MEM[Segment_Adr[pa]:0],PAGESIZE,Color)
END;
END;
PROCEDURE GetBackgroundFromPage(pa:WORD);
{in : pa = 0 or 1 }
{out: - }
{The background memory BACKGNDPAGE becomes filles with the contents of the }
{ specified graphic page. }
{ Using the routine only makes sense when using background mode STATIC}
VAR p:POINTER;
BEGIN
IF (pa<>0) AND (pa<>1) AND (pa<>SCROLLPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE BEGIN
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
ASM
MOV ES,BACKGNDADR
MOV SI,pa
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV DS,AX
XOR SI,SI
XOR DI,DI
MOV DX,3CEh
MOV AX,0004h {plane to read from 0}
MOV BX,PAGESIZE / 2
{DS:SI = source pointer, ES:DI = destination pointer, BX = no. of words }
{AX = access mask for read plane 0, DX = port address for it}
CLI
OUT DX,AX {select plane 0 }
MOV CX,BX
REP MOVSW {store plane data }
XOR SI,SI {reset SI }
INC AH {select plane 1 }
OUT DX,AX
MOV CX,BX
REP MOVSW
XOR SI,SI
INC AH {select plane 2 }
OUT DX,AX
MOV CX,BX
REP MOVSW
XOR SI,SI
INC AH {select plane 3 }
OUT DX,AX
MOV CX,BX
REP MOVSW
STI
MOV AX,SEG @DATA
MOV DS,AX
END
END;
END;
PROCEDURE WritePage(name:STRING; pa:WORD);
{ in: name = file name for the picture to store }
{ pa = page to be saved (0..3) }
{ PAGESIZE = size of one bitplane }
{ PICHeader= tag for picture file }
{out: - }
{rem: The picture at page "pa" has been stored (as bitmap) to file "name" }
{ This file has size 4*PAGESIZE+3 = 64003 bytes: 320x200 points , }
{ 1 byte plus length(PICHeader) as tag }
VAR f:FILE;
i,oldMode:BYTE;
fehler:BOOLEAN;
BEGIN
IF (pa<0) OR (pa>SCROLLPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber; exit
END;
{$I-}
Assign(f,name); fehler:=IOResult<>0;
Rewrite(f,1); fehler:=fehler OR (IOResult<>0);
BlockWrite(f,PICHeader[1],Length(PICHeader));
fehler:=fehler OR (IOResult<>0);
{$IFDEF IOcheck} {$I+} {$ENDIF}
IF fehler
THEN BEGIN
{$I-} Close(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
Error:=Err_FileIO; exit
END;
IF pa<>BACKGNDPAGE
THEN BEGIN {VRAM to disk }
port[$3ce]:=5; {save old read-/write mode }
oldMode:=port[$3cf];
port[$3cf]:=$40; {set read mode 0 }
FOR i:=0 TO 3 DO
BEGIN
portw[$3CE]:=4+(i shl 8); {select read plane }
{$I-}
BlockWrite(f,mem[Segment_Adr[pa]:0],PAGESIZE);
{$IFDEF IOcheck} {$I+} {$ENDIF}
fehler:=fehler OR (IOResult<>0);
END;
port[$3ce]:=5; {restore old read-/write mode }
port[$3cf]:=oldMode;
END
ELSE BEGIN {RAM to disk }
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
FOR i:=0 TO 3 DO
BEGIN
{$I-}
BlockWrite(f,MEM[BACKGNDADR:BACKtab[i]],PAGESIZE);
{$IFDEF IOcheck} {$I+} {$ENDIF}
fehler:=fehler OR (IOResult<>0);
END
END;
{$I-}
Close(f);
{$IFDEF IOcheck} {$I+} {$ENDIF}
fehler:=fehler OR (IOResult<>0);
IF fehler THEN Error:=Err_FileIO
END;
PROCEDURE LoadPage(name:STRING; pa:WORD);
{ in: name = file name for the picture to load}
{ pa = destination page to which the image shall be loaded (0..3) }
{ PAGESIZE = size of one bitplane }
{ PICHeader= tag for picture files }
{out: - }
{rem: The bitmap-picture in file "name" has been loaded into page "pa" }
VAR f:FileOfByte;
i,oldMode:BYTE;
fehler:BOOLEAN;
s:STRING[3];
splane:WORD;
p1,p2:POINTER;
tempName:STRING;
TYPE tempArray=ARRAY[1..PAGESIZE] OF BYTE;
VAR temp:^tempArray;
BEGIN
IF (pa<0) OR (pa>SCROLLPAGE)
THEN BEGIN
Error:=Err_InvalidPageNumber; exit
END;
tempName:=FindFile(name);
IF tempName<>'' THEN name:=tempName;
{$I-}
_Assign(f,name); fehler:=IOResult<>0;
_Reset(f); fehler:=fehler OR (IOResult<>0);
s[0]:=PICHeader[0];
_BlockRead(f,s[1],Length(PICHeader));
fehler:=fehler OR (IOResult<>0) OR (CompressError<>CompressErr_NoError);
{$IFDEF IOcheck} {$I+} {$ENDIF}
IF fehler
THEN BEGIN
{$I-} _Close(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
Error:=Err_FileIO;
CompressError:=CompressErr_NoError;
exit
END
ELSE IF (_FileSize(f)<>4*PAGESIZE+Length(PICHeader)) OR (s<>PICHeader)
THEN BEGIN
{$I-} _Close(f); {$IFDEF IOcheck} {$I+} {$ENDIF}
Error:=Err_NoPicture;
exit
END;
IF pa<>BACKGNDPAGE
THEN BEGIN {disk to VRAM }
ASM cli END;
port[$3ce]:=5; {save old read-/write mode }
oldMode:=port[$3cf];
New(temp);
ASM sti END;
FOR i:=0 TO 3 DO
BEGIN
{$I-}
_BlockRead(f,temp^[1],PAGESIZE);
{$IFDEF IOcheck} {$I+} {$ENDIF}
fehler:=fehler OR (IOResult<>0);
splane:=2+(TranslateTab[i] shl 8); {which write plane }
p1:=@temp^[1];
p2:=ptr(Segment_Adr[pa],0);
ASM
cli
mov dx,$3CE {select write mode 0 }
mov ax,$4005
out dx,ax
mov ax,splane {select write plane }
mov dx,$3C4
out dx,ax
les di,p2
lds si,p1
mov cx,PAGESIZE SHR 1
rep movsw
mov ax,SEG @DATA
mov ds,ax
sti
END;
END;
portw[$3ce]:=oldMode SHL 8 + 5; {restore old read-/write mode }
Dispose(temp);
END
ELSE BEGIN {disk to RAM }
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
FOR i:=0 TO 3 DO
BEGIN
{$I-}
_BlockRead(f,MEM[BACKGNDADR:BACKtab[i]],PAGESIZE);
{$IFDEF IOcheck} {$I+} {$ENDIF}
fehler:=fehler OR (IOResult<>0);
END
END;
{$I-}
_Close(f);
{$IFDEF IOcheck} {$I+} {$ENDIF}
fehler:=fehler OR (IOResult<>0) OR (CompressError<>CompressErr_NoError);
IF fehler THEN Error:=Err_FileIO
END;
PROCEDURE WriteBackgroundPage(name:STRING);
{ in: name = file name for the background picture to store }
{ BACKGNDPAGE= page to be saved (=2) }
{ PAGESIZE = size of one bitplane }
{ PICHeader = tag for picture file }
{out: - }
{rem: The picture of the background page has been stored as file "name" }
{ This file has size 4*PAGESIZE+3 = 64003 bytes: 320x200 points }
{ at 1 byte each, plus length(PICHeader) as tag }
{ Using the routine only makes sense when using background mode STATIC }
BEGIN
WritePage(name,BACKGNDPAGE)
END;
PROCEDURE LoadBackgroundPage(name:STRING);
{ in: name = file name for the background picture to load}
{ BACKGNDPAGE= destination page, in which to load the picture (=2) }
{ PAGESIZE = size of one bitplane }
{ PICHeader= tag for picture files }
{out: - }
{rem: The bitmap-picture contained in file "name" has been loaded to the }
{ background page BACKGNDPAGE}
{ Using the routine only makes sense when using background mode STATIC}
BEGIN
LoadPage(name,BACKGNDPAGE)
END;
PROCEDURE FadeIn(pa,ti,style:WORD);
{ in: pa = page which shall be faded onto the actually visible page }
{ ti = time in milliseconds for this action }
{ style = algorithm which shall be used }
{ 1-PAGE = actually visible page }
{out: Error = Err_InvalidFade, if illegal "style" value has been used }
{rem: graphic mode must have been initialized already }
{ most often, you will use pa=BACKGNDPAGE }
PROCEDURE WipeIn(pa,time:WORD);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST hoehe =40; {divisor of Succ(YMAX)}
breite=40; {divisor of Succ(XMAX)}
br_x =Succ(XMAX) DIV breite; {blocks in X-direction}
br_y =Succ(YMAX) DIV hoehe; {blocks in Y-direction}
n=hoehe*br_x; {number of executions of the delay loop }
VAR i,x,y,ploty,plotx:WORD;
counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
p:POINTER;
BEGIN
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
FOR i:=0 TO pred(hoehe) DO
FOR x:=0 TO pred(br_x) DO
BEGIN
FOR y:=0 TO pred(br_y) DO
BEGIN
IF ODD(x)
THEN ploty:=y*hoehe+i +StartVirtualY
ELSE ploty:=y*hoehe+(hoehe-1-i)+StartVirtualY;
plotx:=x*breite +StartVirtualX;
p:=GetImage(plotx,ploty,plotx+pred(breite),ploty,pa);
PutImage(plotx,ploty,p,1-PAGE);
FreeImageMem(p);
END; {of FOR y}
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
END; {of FOR x}
END;
PROCEDURE Chaos(pa,time:WORD;m:BYTE);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ m = the way how this shall be done (1..14)}
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST n=Succ(XMAX)*Succ(YMAX); {number of screen pixels}
{e.g., good values are sums of powers of 2 +1 }
para:ARRAY[1..14] OF WORD=
(13477,65,337,129,257,513,769,1025,481,4097,5121,177,16385,16897);
VAR i,k,x,y:WORD;
counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
rand:WORD;
BEGIN
t:=ClockTicks;
counter:=0;
rand:=0;
IF (m<1) OR (m>14) THEN m:=1;
k:=para[m];
temp:=0.0182*time/n;
FOR i:=0 TO 65535 DO
BEGIN
ASM {compute: "x := rand MOD 320" and "y := rand DIV 320" }
XOR DX,DX
MOV AX,rand
MOV BX,XMAX+1
DIV BX
MOV y,AX
MOV x,DX
END;
IF y<=YMAX
THEN PutPixel(StartVirtualX+x,StartVirtualY+y,
PageGetPixel(StartVirtualX+x,StartVirtualY+y,pa));
ASM {compute: rand:=rand*k+1 }
MOV AX,rand
MUL k
INC AX
MOV rand,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
END; {of FOR i}
END;
PROCEDURE Chaos2(pa,time:WORD;m:BYTE);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ m = the way how this shall be done (1..1)}
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST n=Succ(XMAX)*Succ(YMAX); {number of screen pixels}
para:ARRAY[1..1] OF WORD=
(39551);
VAR i,k,x,y:WORD;
counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
rand:WORD;
BEGIN
t:=ClockTicks;
counter:=0;
rand:=0;
IF (m<1) OR (m>1) THEN m:=1;
k:=para[m];
temp:=0.0182*time/n;
FOR i:=0 TO 65535 DO
BEGIN
ASM {compute: "x:=rand MOD 320" and "y:=rand DIV 320" }
XOR DX,DX
MOV AX,rand
MOV BX,XMAX+1
DIV BX
MOV y,AX
MOV x,DX
END;
PutPixel(StartVirtualX+x,StartVirtualY+y,
PageGetPixel(StartVirtualX+x,StartVirtualY+y,pa));
ASM {compute: rand:=(rand+k) MOD n }
XOR DX,DX
MOV AX,rand
ADD AX,k
JNC @normal
ADD AX,(65536-n) {overflow, thus correct it }
@normal:
CMP AX,n
JB @ok
SUB AX,n
@ok:
MOV rand,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
END; {of FOR i}
END;
PROCEDURE SweepVertical(pa,time:WORD; down:BOOLEAN);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ down = TRUE/FALSE for: from bottom to top/vice versa }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST n=Succ(YMAX); {number of executions of the delay loop }
VAR y,counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
oldColor,step:BYTE;
p:POINTER;
BEGIN
oldColor:=Color;
Color:=white;
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
IF down
THEN FOR y:=0+StartVirtualY TO YMAX+StartVirtualY DO
BEGIN
Line(StartVirtualX,y,StartVirtualX+XMAX,y,1-PAGE);
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
p:=GetImage(StartVirtualX,y,StartVirtualX+XMAX,y,pa);
PutImage(StartVirtualX,y,p,1-PAGE);
FreeImageMem(p);
END
ELSE FOR y:=YMAX+StartVirtualY DOWNTO 0+StartVirtualY DO
BEGIN
Line(StartVirtualX,y,StartVirtualX+XMAX,y,1-PAGE);
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
p:=GetImage(StartVirtualX,y,StartVirtualX+XMAX,y,pa);
PutImage(StartVirtualX,y,p,1-PAGE);
FreeImageMem(p);
END;
Color:=oldColor
END;
PROCEDURE SweepHorizontal(pa,time:WORD; left_to_right:BOOLEAN);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ left_to_right=TRUE/FALSE for: from left to right/vice versa }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST n=Succ(XMAX); {number of executions of the delay loop }
VAR x,counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
oldColor,step:BYTE;
p:POINTER;
BEGIN
oldColor:=Color;
Color:=white;
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
IF left_to_right
THEN FOR x:=0+StartVirtualX TO XMAX+StartVirtualX DO
BEGIN
Line(x,StartVirtualY,x,StartVirtualY+YMAX,1-PAGE);
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
p:=GetImage(x,StartVirtualY,x,StartVirtualY+YMAX,pa);
PutImage(x,StartVirtualY,p,1-PAGE);
FreeImageMem(p);
END
ELSE FOR x:=XMAX+StartVirtualX DOWNTO 0+StartVirtualX DO
BEGIN
Line(x,StartVirtualY,x,StartVirtualY+YMAX,1-PAGE);
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
p:=GetImage(x,StartVirtualY,x,StartVirtualY+YMAX,pa);
PutImage(x,StartVirtualY,p,1-PAGE);
FreeImageMem(p);
END;
Color:=oldColor
END;
PROCEDURE ScrollVertical(pa,time:WORD; up:BOOLEAN);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ up = TRUE/FALSE for: from bottom to top/vice versa }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
LABEL oneLine1,oneLine2,oneLine3,oneLine4;
CONST n=Succ(YMAX); {number of executions of the delay loop }
VAR counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
BEGIN
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
IF pa<>BACKGNDPAGE
THEN BEGIN
IF up
THEN BEGIN {scroll upwards }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES := Segment_Adr[1 - PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX := Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,YMAX*LINESIZE+(LINESIZE-1) {DX:BX = ^source data}
MOV AX,YMAX {AX = row counter }
oneLine2:
STD {move backwards! }
MOV SI,ES {first scroll old contents upwards: }
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,(YMAX-1)*LINESIZE+(LINESIZE-1) {from last but one graphic row}
MOV DI,YMAX*LINESIZE+(LINESIZE-1) {to last graphic row }
MOV CX,YMAX*LINESIZE {199 rows }
REP MOVSB
MOV DS,DX {now make new row visible: }
MOV SI,BX {DS:SI = ^row to move }
MOV CX,LINESIZE {1 row }
REP MOVSB
SUB BX,LINESIZE {increase source pointer }
{--- insertion to realize timing:}
PUSH AX {save all registers needed later }
PUSH BX
PUSH DX
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
CLD {just to be sure! }
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP DX
POP BX
POP AX
{--- end of insertion}
DEC AX {all rows done? }
JNS oneLine2 {no, next row }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END
ELSE BEGIN {scroll downwards }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES := Segment_Adr[1-PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX := Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,0*LINESIZE {DX:BX = ^source data}
MOV AX,YMAX {AX = row counter }
oneLine1:
MOV SI,ES {first scroll old contents upwards: }
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,1*LINESIZE {from graphic row 1}
MOV DI,0*LINESIZE {to graphic row 0 }
MOV CX,YMAX*LINESIZE {199 rows }
REP MOVSB
MOV DS,DX {now make new row visible: }
MOV SI,BX {DS:SI = ^row to move }
MOV CX,LINESIZE {1 row }
REP MOVSB
ADD BX,LINESIZE {increase source pointer }
{--- insertion to realize timing:}
PUSH AX {save all registers needed later }
PUSH BX
PUSH DX
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP DX
POP BX
POP AX
{--- end of insertion}
DEC AX {all rows done? }
JNS oneLine1 {no, next row }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END;
END
ELSE BEGIN {pa = BACKGNDPAGE, thus copy from RAM to VRAM}
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
IF up
THEN BEGIN {scroll upwards }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES := Segment_Adr[1 - PAGE] = ^visible page }
PUSH DS
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX := Segment_Adr[pa] = ^source address }
{(for pa=BACKGNDPAGE id. to BACKGNADR)}
PUSH BP
MOV BP,AX
MOV BX,YMAX*LINESIZE+(LINESIZE-1)-1 {BP:BX = ^source data}
MOV AX,YMAX {AX = row counter }
oneLine4:
MOV SI,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV AX,SI
STD {move backwards! }
MOV SI,ES {first scroll old contents upwards: }
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,(YMAX-1)*LINESIZE+(LINESIZE-1) {from last but one graphic row}
MOV DI,YMAX*LINESIZE+(LINESIZE-1) {to last graphic row }
MOV CX,YMAX*LINESIZE {199 rows }
REP MOVSB
PUSH AX
MOV DX,3CEh {select write mode 0}
MOV AX,4005h
OUT DX,AX
MOV DX,3C4h
MOV AX,0102h {select write plane 0 }
OUT DX,AX
MOV DS,BP {now make new row visible: }
MOV SI,BX {DS:SI = ^row to move }
DEC DI {decrement DI by 1 (word accesses!) }
MOV CX,LINESIZE / 2 {1 row }
REP MOVSW
SHL AH,1 {select write plane 1 }
OUT DX,AX
ADD SI,PAGESIZE+LINESIZE
MOV CX,LINESIZE / 2 {1 row }
ADD DI,LINESIZE
REP MOVSW
SHL AH,1 {select write plane 2 }
OUT DX,AX
ADD SI,PAGESIZE+LINESIZE
MOV CX,LINESIZE / 2 {1 row }
ADD DI,LINESIZE
REP MOVSW
SHL AH,1 {select write plane 3 }
OUT DX,AX
ADD SI,PAGESIZE+LINESIZE
MOV CX,LINESIZE / 2 {1 row }
ADD DI,LINESIZE
REP MOVSW
MOV AH,$F {select all 4 planes }
OUT DX,AX
SUB BX,LINESIZE {increase source pointer }
POP AX
{--- insertion to realize timing:}
MOV SI,BP {temporary BP }
POP BP {TP's old BP }
PUSH AX {save all registers needed later }
PUSH BX
PUSH SI
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP SI
POP BX
POP AX
PUSH BP
MOV BP,SI
{--- end of insertion}
DEC AX {all rows done? }
JNS oneLine4 {no, next row }
POP BP
POP DS
END;
END
ELSE BEGIN {scroll downwards }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES := Segment_Adr[1-PAGE] = ^visible page }
PUSH DS
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX := Segment_Adr[pa] = ^source address }
{(for pa=BACKGNDPAGE id. to BACKGNADR)}
PUSH BP
MOV BP,AX
MOV DX,AX
MOV BX,0*LINESIZE {DX:BX = ^source data}
MOV AX,YMAX {AX = row counter }
oneLine3:
MOV SI,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV AX,SI
MOV SI,ES {first scroll old contents upwards: }
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,1*LINESIZE {from graphic row 1}
MOV DI,0*LINESIZE {to graphic row 0 }
MOV CX,YMAX*LINESIZE {199 rows }
REP MOVSB
PUSH AX
MOV DX,3CEh {select write mode 0}
MOV AX,4005h
OUT DX,AX
MOV DX,3C4h
MOV AX,0102h {select write plane 0 }
OUT DX,AX
MOV DS,BP {now make new row visible: }
MOV SI,BX {DS:SI = ^row to move }
MOV CX,LINESIZE / 2 {1 row }
REP MOVSW
SHL AH,1 {select write plane 1 }
OUT DX,AX
ADD SI,PAGESIZE-LINESIZE
MOV CX,LINESIZE / 2 {1 row }
SUB DI,LINESIZE
REP MOVSW
SHL AH,1 {select write plane 2 }
OUT DX,AX
ADD SI,PAGESIZE-LINESIZE
MOV CX,LINESIZE / 2 {1 row }
SUB DI,LINESIZE
REP MOVSW
SHL AH,1 {select write plane 3 }
OUT DX,AX
ADD SI,PAGESIZE-LINESIZE
MOV CX,LINESIZE / 2 {1 row }
SUB DI,LINESIZE
REP MOVSW
MOV AH,$F {select all 4 planes }
OUT DX,AX
ADD BX,LINESIZE {increase source pointer }
POP AX
{--- insertion to realize timing:}
MOV SI,BP {temporary BP }
POP BP {TP's old BP }
PUSH AX {save all registers needed later }
PUSH BX
PUSH SI
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP SI
POP BX
POP AX
PUSH BP
MOV BP,SI
{--- end of insertion}
DEC AX {all rows done? }
JNS oneLine3 {no, next row }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP BP
POP DS
END;
END;
END;
END;
PROCEDURE ScrollHorizontal(pa,time:WORD; left:BOOLEAN);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{ left = TRUE/FALSE for: from left to right/vice versa }
{ 1-PAGE= (visible) graphic page on which to draw }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
LABEL oneColumn1,oneColumn2,oneColumn3,oneColumn4;
CONST n=Pred(LINESIZE); {number of executions of the delay loop }
VAR counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
temp:REAL;
BEGIN
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
IF pa<>BACKGNDPAGE
THEN BEGIN
IF left
THEN BEGIN {scroll to the left }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES := Segment_Adr[1 - PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX := Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
MOV AX,LINESIZE-1 {AX = column counter}
oneColumn2: {scroll old screen contents to the right:}
MOV SI,ES
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,PAGESIZE-2
MOV DI,PAGESIZE-1
MOV CX,PAGESIZE-1
STD
REP MOVSB
CLD
MOV CX,SEG @DATA
MOV DS,CX {DS = ^TP's data}
MOV CX,YMAX+1 {CX = row counter }
MOV SI,AX
XOR DI,DI
MOV BX,LINESIZE-1
MOV DS,DX {DS = ^source data}
@oneRow: {update first column: }
MOVSB
ADD SI,BX {position at next row: }
ADD DI,BX {works, because BX + 1 = LINESIZE}
LOOP @oneRow
{--- insertion to realize timing:}
PUSH AX {save all registers needed later }
PUSH DX
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP DX
POP AX
{--- end of insertion}
DEC AX {all columns done? }
JNS oneColumn2 {no, next column }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END
ELSE BEGIN {scroll to the right }
ASM
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES := Segment_Adr[1 - PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX := Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
MOV AX,0 {AX = column counter}
oneColumn1: {scroll old screen contents to the left:}
MOV SI,ES
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,1
XOR DI,DI
MOV CX,PAGESIZE-1
REP MOVSB
MOV CX,SEG @DATA
MOV DS,CX {DS = ^TP's data}
MOV CX,YMAX+1 {CX = row counter }
MOV SI,AX
MOV DI,LINESIZE-1
MOV BX,LINESIZE-1
MOV DS,DX {DS = ^source data}
@oneRow: {update last column: }
MOVSB
ADD SI,BX {position at next row: }
ADD DI,BX {works, because BX + 1 = LINESIZE}
LOOP @oneRow
{--- insertion to realize timing:}
PUSH AX {save all registers needed later }
PUSH DX
PUSH ES
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
POP DX
POP AX
{--- end of insertion}
INC AX {all columns done? }
CMP AX,LINESIZE
JB oneColumn1 {no, next column }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END;
END
ELSE BEGIN {pa = BACKGNDPAGE, thus copy from RAM to VRAM}
IF EMSused
THEN EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
IF left
THEN BEGIN {scroll to the left }
ASM
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES := Segment_Adr[1 - PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX := Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
MOV AX,LINESIZE-1 {column counter}
oneColumn4: {scroll old screen contents to the right:}
MOV SI,DX
MOV DI,AX
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV DX,SI
MOV AX,DI
MOV SI,ES
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,PAGESIZE-2
MOV DI,PAGESIZE-1
MOV CX,PAGESIZE-1
STD
REP MOVSB
CLD
MOV CX,SEG @DATA
MOV DS,CX {DS = ^TP's data}
MOV CX,YMAX+1 {CX = row counter }
MOV SI,AX {column counter}
XOR DI,DI
MOV BX,LINESIZE-1
MOV DS,DX {DS = ^source data}
PUSH AX
PUSH DX
MOV DX,3CEh {select write mode 0}
MOV AX,4005h
OUT DX,AX
MOV DX,3C4h
@oneRow0: {update first column: }
MOV AX,0802h {select write plane 3 }
OUT DX,AX
MOV AL,[SI +3*PAGESIZE]
MOV ES:[DI],AL
MOV AX,0402h {select write plane 2 }
OUT DX,AX
MOV AL,[SI +2*PAGESIZE]
MOV ES:[DI],AL
MOV AX,0202h {select write plane 1 }
OUT DX,AX
MOV AL,[SI +1*PAGESIZE]
MOV ES:[DI],AL
MOV AX,0102h {select write plane 0 }
OUT DX,AX
MOVSB
ADD SI,BX {position at next row: }
ADD DI,BX {works, because BX + 1 = LINESIZE}
LOOP @oneRow0
{--- insertion to realize timing:}
PUSH ES {save all registers needed later }
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
{--- end of insertion}
POP DX
POP AX
DEC AX {all columns done? }
JNS oneColumn4 {no, next column }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END
ELSE BEGIN {scroll to the right }
ASM
MOV SI,1
SUB SI,PAGE
AND SI,1
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW
MOV ES,AX {ES := Segment_Adr[1 - PAGE] = ^visible page }
MOV SI,pa
AND SI,3
SHL SI,1
ADD SI,OFFSET Segment_Adr-StartIndex*2
LODSW {AX := Segment_Adr[pa] = ^source address}
PUSH DS
MOV DX,AX
MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
MOV AX,0 {AX = column counter}
oneColumn3: {scroll old screen contents to the left:}
MOV SI,DX
MOV DI,AX
MOV DX,3C4h
MOV AX,0F02h {access all 4 planes at once }
OUT DX,AX
MOV DX,3CEh
MOV AX,4105h {set write mode 1 }
OUT DX,AX
MOV DX,SI
MOV AX,DI
MOV SI,ES
MOV DS,SI {DS = ES = visible graphic page }
MOV SI,1
XOR DI,DI
MOV CX,PAGESIZE-1
REP MOVSB
MOV CX,SEG @DATA
MOV DS,CX {DS = ^TP's data}
MOV CX,YMAX+1 {CX = row counter }
MOV SI,AX
MOV DI,LINESIZE-1
MOV BX,LINESIZE-1
MOV DS,DX {DS = ^source data}
PUSH AX
PUSH DX
MOV DX,3CEh {select write mode 0}
MOV AX,4005h
OUT DX,AX
MOV DX,3C4h
@oneRow: {update last column: }
MOV AX,0802h {select write plane 3 }
OUT DX,AX
MOV AL,[SI +3*PAGESIZE]
MOV ES:[DI],AL
MOV AX,0402h {select write plane 2 }
OUT DX,AX
MOV AL,[SI +2*PAGESIZE]
MOV ES:[DI],AL
MOV AX,0202h {select write plane 1 }
OUT DX,AX
MOV AL,[SI +1*PAGESIZE]
MOV ES:[DI],AL
MOV AX,0102h {select write plane 0 }
OUT DX,AX
MOVSB
ADD SI,BX {position at next row: }
ADD DI,BX {works, because BX + 1 = LINESIZE}
LOOP @oneRow
{--- insertion to realize timing:}
PUSH ES {save all registers needed later }
MOV AX,SEG @DATA {restore TP's data segment }
MOV DS,AX
END;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
ASM;
POP ES
{--- end of insertion}
POP DX
POP AX
INC AX {all columns done? }
CMP AX,LINESIZE
JB oneColumn3 {no, next column }
MOV DX,3CEh {restore write mode 0 }
MOV AX,4005h
OUT DX,AX
POP DS
END;
END;
END
END;
PROCEDURE CircleIn(pa,time:WORD);
{ in: pa = page, which contents will be made visible }
{ time = time (in millisceconds) for this action (approx.) }
{out: - }
{rem: the contents of page "pa" has been copied to page 1-PAGE }
CONST centerX=XMAX DIV 2; {middle of screen}
centerY=YMAX DIV 2;
k=189; {number of circles := sqrt(centerX² + centerY²), rounded up}
adjust=0.707106781; {compensation in diagonal direction = 1/sqrt(2) }
n=TRUNC(k/adjust); {number of executions of the delay loop }
VAR radqu,x,y,x0,y0,u1,u2,u3,u4,v1,v2,v3,v4:WORD;
counter:WORD;
ClockTicks:LONGINT ABSOLUTE $40:$6C;
t:LONGINT;
radius,temp:REAL;
BEGIN
t:=ClockTicks;
counter:=0;
temp:=0.0182*time/n;
x0:=centerX + StartVirtualX;
y0:=centerY + StartVirtualY;
{unfortunately, FOR true_radius:=1 TO k STEP 1/adjust isn't possible in TP}
radius:=0.0;
REPEAT
radqu:=TRUNC(sqr(radius));
FOR x:=0 TO TRUNC(radius/sqrt(2)) DO {compute octant }
BEGIN
y:=TRUNC(sqrt(radqu-sqr(x))); {Pythagorean proposition}
u1:=x0-x; v1:=y0-y; {use axial- and point symmetrie }
u2:=x0+x; v2:=y0+y;
u3:=x0-y; v3:=y0-x;
u4:=x0+y; v4:=y0+x;
PutPixel(u1,v1,PageGetPixel(u1,v1,pa));
PutPixel(u1,v2,PageGetPixel(u1,v2,pa));
PutPixel(u2,v1,PageGetPixel(u2,v1,pa));
PutPixel(u2,v2,PageGetPixel(u2,v2,pa));
PutPixel(u3,v3,PageGetPixel(u3,v3,pa));
PutPixel(u3,v4,PageGetPixel(u3,v4,pa));
PutPixel(u4,v3,PageGetPixel(u4,v3,pa));
PutPixel(u4,v4,PageGetPixel(u4,v4,pa));
END;
radius:=radius+adjust;
INC(counter);
WHILE ClockTicks<t+counter*temp DO BEGIN END;
UNTIL radius>=k;
END;
BEGIN {of FadeIn}
IF (pa<0) OR (pa>SCROLLPAGE)
THEN Error:=Err_InvalidPageNumber
ELSE CASE style OF
Fade_Squares :WipeIn(pa,ti);
Fade_Moiree1..Fade_Moiree14:Chaos(pa,ti,style+1-Fade_Moiree1);
Fade_SweepInFromTop: SweepVertical(pa,ti,TRUE);
Fade_SweepInFromBottom: SweepVertical(pa,ti,FALSE);
Fade_SweepInFromLeft: SweepHorizontal(pa,ti,TRUE);
Fade_SweepInFromRight: SweepHorizontal(pa,ti,FALSE);
Fade_ScrollInFromTop: ScrollVertical(pa,ti,TRUE);
Fade_ScrollInFromBottom:ScrollVertical(pa,ti,FALSE);
Fade_ScrollInFromLeft: ScrollHorizontal(pa,ti,TRUE);
Fade_ScrollInFromRight: ScrollHorizontal(pa,ti,FALSE);
Fade_Circles : CircleIn(pa,ti);
Fade_Moiree15:Chaos2(pa,ti,style+1-Fade_Moiree15);
else Error:=Err_InvalidFade
END;
END;
PROCEDURE IntroScroll(n,wait:WORD);
{ in: n = number of lines to be scrolled up }
{ wait = time (in ms) the program will wait after each line }
{rem: scrolling always starts at page 0 (=$A000:0000) }
{ After the scrolling, the command "Screen(1-page)" must be issued!}
BEGIN
Screen(0); {position at $A000:0000 }
ASM
XOR SI,SI {compute address of page 0 = $A000:0000 }
AND SI,3 {page value *2 (word-sized entries!)}
SHL SI,1 {add start address of array to that }
ADD SI,OFFSET Offset_Adr-StartIndex*2 {evtl. correct displacement }
LODSW {and fetch value}
MOV BX,AX
MOV CX,n
MOV SI,wait
@oneline:
ADD BX,LINESIZE
CLI {May not be interrupted! }
MOV DX,StatusReg
@WaitNotHSyncLoop:
in al,dx
and al,1
jz @WaitNotHSyncLoop
@WaitHSyncLoop:
in al,dx
and al,1
jz @WaitHSyncLoop
MOV DX,CRTAddress {CRT-Controller}
MOV AL,$0D {LB-startaddress-register}
OUT DX,AL
INC DX
MOV AL,BL
OUT DX,AL {set LB of new starting address }
DEC DX
MOV AL,$0C
OUT DX,AL
INC DX
MOV AL,BH {set HB of new starting address }
OUT DX,AL
STI
PUSH BX
PUSH CX
PUSH SI
PUSH SI
CALL CRT.Delay
POP SI
POP CX
POP BX
LOOP @oneline
END;
END;
PROCEDURE CopyVRAMtoVRAM(source,dest:POINTER; len:WORD); ASSEMBLER;
{ in: source = starting address}
{ dest = destination address }
{ len = length of the area to copy }
{out: - }
{rem: The both areas may not overlap each other }
{ WriteMode1 will be used; therefore, the length "len" counts }
{ for 4 bytes each: for example, a call like }
{ CopyVRAMtoVRAM(Ptr($A000,0),Ptr($A000,PAGESIZE),PAGESIZE) }
{ would copy a complete page (4*PAGESIZE = 64000 bytes)}
ASM
MOV AX,4105h {enable write mode 1 }
MOV DX,3CEh
OUT DX,AX
MOV AX,0F02h {access all 4 planes at once }
MOV DX,3C4h
OUT DX,AX
MOV BX,DS
LES DI,dest
LDS SI,source
MOV CX,len
CLD
REP MOVSB
MOV DS,BX
MOV AX,4005h
MOV DX,3CEh
OUT DX,AX
END;
PROCEDURE InitRoutines;
{ in: USEEMS = TRUE for: use EMS-memory for BACKGNDPAGE contents}
{out: SpriteN[],SPRITEAD[],SPRITEPTR[],SPRITESIZE[],BackTile[] have been }
{ initialised to "completely empty"}
{ NextSprite[] has been set so that each sprite is its own }
{ successor }
{ PAGE, PAGEADR have been set to graphic page 0 }
{ BACKGNDADR has been set to the background page }
{ SCROLLADR has been set to the scrollable background page }
{ BACKGROUNDMODE has been set to STATIC }
{ The default tile for the scolling background has been set to #0 }
{ StartVirtualX,StartVirtualY =0 (that is: virtual = absolute coord.)}
{ oldMode = old graphicmode }
{ Error has been set, if no VGA-card could be found in the system }
{ CycleTime = 0, that is: no min. time for animation cycle }
{ Color = 15, that is: white }
{ CurrentFont = pointer to internal font}
{ FontHeight, FontWidth = sizes of internal font }
{ FontType = its type }
{ ActualColors = default color palette of mode $13 }
{ CRTAddress = port address of CRT-address register}
{ StatusReg = port address of state register }
{ EMSused = TRUE, if EMS-memory has been allocated for BACKGNDPAGE }
{ BackgroundEMSHandle = handle to allocated EMS-block (if EMSused=TRUE) }
{ buf = pointer to allocated heap-block (if EMSused=FALSE) }
{ Win* coordinates have been set to the complete animation window }
{ SplitIndex has been set so that all sprites will be clipped to }
{ the animation window }
{rem: This procedure should be called only once - namely in the very }
{ beginning - for initializing the whole package properly }
TYPE rec=RECORD lw,hw:WORD END;
VAR i,adj:WORD;
FUNCTION IsVGA:BOOLEAN;
BEGIN
WITH Regs DO
BEGIN
AX:=$1A00;
Intr($10,Regs);
IsVGA:=(AL=$1A) AND {VGA's identify-adapter-function supported?}
( (BL=7) OR (BL=8) ) {monochrome or color VGA - adapter}
END;
END;
BEGIN
IF IsVGA THEN Error:=Err_None
ELSE BEGIN
Error:=Err_NoVGA;
exit
END;
SetCycleTime(0);
FillChar(SpriteN,SizeOf(SpriteN),0);
FillChar(SPRITEAD,SizeOf(SPRITEAD),0);
FillChar(SPRITESIZE,SizeOf(SPRITESIZE),0);
FillChar(BackTile,SizeOf(BackTile),0);
FOR i:=0 TO LoadMAX DO
BEGIN
NextSprite[i]:=i;
SPRITEPTR[i]:=NIL
END;
BACKGNDADR:=Segment_Adr[BACKGNDPAGE]; {segment address of background page }
PAGE:=0; {page to be drawn upon }
PAGEADR:=Segment_Adr[PAGE];
SCROLLADR:=Segment_Adr[SCROLLPAGE];
SetBackgroundMode(STATIC);
SetOffscreenTile(0);
StartVirtualX:=0; StartVirtualY:=0; {virtual = absolute coordinates }
Color:=white; {set actual drawing color to white }
regs.ah:=$f; intr($10,regs); oldMode:=regs.al;
ActualColors:=DefaultColors;
{SetShadowTab(Schatten) isn't needed, as the default values are set already}
ASM {see if we are using color-/monochorme display}
MOV DX,3CCh {use output register: }
IN AL,DX
TEST AL,1 {is it a color display? }
MOV DX,3D4h
JNZ @L1 {yes }
MOV DX,3B4h {no }
@L1: {DX = 3B4h / 3D4h = CRTAddress-register for monochrome/color}
MOV CRTAddress,DX
ADD DX,6 {DX = 3BAh / 3DAh = state register for monochrome/color}
MOV StatusReg,DX
END; {of ASM}
LoadFont(''); {load internal font }
SetAnimateWindow(0,0,319,199);
EMSused:=FALSE;
IF EmsInstalled(BACKGNDADR) AND
EMSIsHardWareEMS AND
(EMSPagesAvailable>=4) AND
(EMSError=0)
THEN BEGIN {use EMS }
BackgroundEMSHandle:=EMSAllocate(4); {allocate 64K }
If EmsError<>0
THEN BEGIN {don't do it}
WriteLn ('EMS-Allozierungsfehler!' );
EMSRelease(BackgroundEMSHandle);
END
ELSE BEGIN
EMSused:=TRUE;
buf:=Ptr(BACKGNDADR,0)
END;
END;
IF NOT EMSused
THEN BEGIN {no, not enough or "wrong" EMS-memory, use heap instead: }
New(buf)
END
ELSE EMSFillFrame(BackgroundEMSHandle); {prepare EMS access }
FillChar(buf^,SizeOf(buf^),0);
adj:=rec(buf).lw DIV 16;
IF (rec(buf).lw MOD 16)<>0
THEN inc(adj); {round towards increasing addresses }
inc(rec(buf).hw,adj);
rec(buf).lw:=0;
IF rec(buf).lw<>0
THEN BEGIN
WRITELN('Fehler: buf^ liegt nicht auf Segmentgrenze');
Halt
END
ELSE BEGIN
Segment_Adr[BACKGNDPAGE]:=rec(buf).hw;
Offset_Adr[BACKGNDPAGE]:=0;
BACKGNDADR:=rec(buf).hw
END;
SetAnimateWindow(0,0,XMAX,YMAX);
SetSplitIndex(-1); {=clip all }
END;
PROCEDURE CloseRoutines;
{ in: oldMode = old graphicmode, to which we'll switch back }
{ EMSused = did we use EMS memory for BACKGNDPAGE? }
{ BackgroundEMSHandle = if yes, then this is the handle to it }
{ buf = if no, then this is the pointer to the normal heap area}
{out: - }
BEGIN
regs.al:=oldMode; regs.ah:=0; intr($10,regs);
IF EMSused
THEN EMSRelease(BackgroundEMSHandle)
ELSE Release(buf);
END;
FUNCTION GetErrorMessage:STRING;
{ in: Error = number of the occurred error }
{out: the error described in words}
BEGIN
CASE Error OF
Err_None:GetErrorMessage:='No Error';
Err_NotEnoughMemory:GetErrorMessage:='Not enough memory available on heap';
Err_FileIO:GetErrorMessage:='I/O-error with file';
Err_InvalidSpriteNumber:GetErrorMessage:='Invalid sprite number used';
Err_NoSprite:GetErrorMessage:='No (or corrupted) sprite file';
Err_InvalidPageNumber:GetErrorMessage:='Invalid page number used';
Err_NoVGA:GetErrorMessage:='No VGA-card found';
Err_NoPicture:GetErrorMessage:='No (or corrupted) picture file';
Err_InvalidPercentage:GetErrorMessage:='Percentage value must be 0..100';
Err_NoTile:GetErrorMessage:='No (or corrupted) tile/sprite file';
Err_InvalidTileNumber:GetErrorMessage:='Invalid tile number used';
Err_InvalidCoordinates:GetErrorMessage:='Invalid coordinates used';
Err_BackgroundToBig:GetErrorMessage:='Background to big for tile-buffer';
Err_InvalidMode:GetErrorMessage:='Only STATIC or SCROLLING allowed here';
Err_InvalidSpriteLoadNumber:GetErrorMessage:='Invalid spriteload number used';
Err_NoPalette:GetErrorMessage:='No (or corrupted) palette file';
Err_PaletteWontFit:GetErrorMessage:='Attempt to write beyond palette end';
Err_InvalidFade:GetErrorMessage:='Invalid fade style used';
Err_NoFont:GetErrorMessage:='No (or corrupted) font file';
Err_EMSError:GetErrorMessage:='Problems with EMS memory';
ELSE GetErrorMessage:='Unknown error';
END;
END;
FUNCTION FindFile(P:PathStr):PathStr;
{ in: P = file to search, incl. starting path}
{out: complete pathname to file }
{rem: If the file is not found in the specified directory, then all }
{ subdirectories will be searched through recursively.}
{ If the search still doesn't find the file, '' will be returned }
VAR D: DirStr;
N: NameStr;
E: ExtStr;
DateiName:STRING[12];
temp:PathStr;
FUNCTION SearchFile(Pfad:PathStr):PathStr;
{ in: DateiName = file to be searched}
{ Pfad = path to start the search from }
{out: complete pathname to file or '', if the file is not found }
VAR Datei,Dir:SearchRec;
BEGIN
FindFirst(Pfad+DateiName,AnyFile,Datei);
WHILE DosError=0 DO
BEGIN
IF (Datei.Attr AND Directory)<>Directory
THEN BEGIN
SearchFile:=Pfad+Datei.Name;
Exit
END;
FindNext(Datei)
END;
{here: file has not be found in current directory }
FindFirst(Pfad+'*.*',Directory,Dir);
WHILE DosError=0 DO
BEGIN
IF ((Dir.Attr AND Directory)=Directory) AND (Dir.Name[1]<>'.')
THEN BEGIN {search through next directory}
temp:=SearchFile(Pfad+Dir.Name+'\');
IF temp<>''
THEN BEGIN {found recursively!}
SearchFile:=temp;
Exit
END;
END;
FindNext(Dir);
END;
SearchFile:='';
END;
BEGIN
FSplit(P,D,N,E);
DateiName:=N+E;
FindFile:=SearchFile(D)
END;
BEGIN
InitRoutines;
END.