1 /* Keen Dreams Source Code
\r
2 * Copyright (C) 2014 Javier M. Chavez
\r
4 * This program is free software; you can redistribute it and/or modify
\r
5 * it under the terms of the GNU General Public License as published by
\r
6 * the Free Software Foundation; either version 2 of the License, or
\r
7 * (at your option) any later version.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License along
\r
15 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
22 =============================================================================
\r
27 scrolling more than one tile / refresh forces a total redraw
\r
29 two overlapping sprites of equal priority can change drawing order when
\r
32 =============================================================================
\r
35 #include "src/lib/16_rf.h"
\r
39 =============================================================================
\r
43 =============================================================================
\r
46 #define SCREENTILESWIDE 20
\r
47 #define SCREENTILESHIGH 13
\r
49 #define SCREENSPACE (SCREENWIDTH*240)
\r
50 #define FREEEGAMEM (0x10000l-3l*SCREENSPACE)
\r
53 // the update array must have enough space for two screens that can float
\r
54 // up two two tiles each way
\r
56 // (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared
\r
57 // by word width instructions
\r
59 #define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)
\r
60 #define UPDATESPARESIZE (UPDATEWIDE*2+4)
\r
61 #define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)
\r
63 #define G_EGASX_SHIFT 7 // global >> ?? = screen x
\r
64 #define G_CGASX_SHIFT 6 // global >> ?? = screen x
\r
65 #define G_SY_SHIFT 4 // global >> ?? = screen y
\r
67 unsigned SX_T_SHIFT; // screen x >> ?? = tile EGA = 1, CGA = 2;
\r
68 #define SY_T_SHIFT 4 // screen y >> ?? = tile
\r
71 #define EGAPORTSCREENWIDE 42
\r
72 #define CGAPORTSCREENWIDE 84
\r
73 #define PORTSCREENHIGH 224
\r
75 #define UPDATESCREENSIZE (UPDATEWIDE*PORTTILESHIGH+2)
\r
76 #define UPDATESPARESIZE (UPDATEWIDE*2+4)
\r
77 #define UPDATESIZE (UPDATESCREENSIZE+2*UPDATESPARESIZE)
\r
80 =============================================================================
\r
84 =============================================================================
\r
87 typedef struct spriteliststruct
\r
89 int screenx,screeny;
\r
92 unsigned grseg,sourceofs,planesize;
\r
94 unsigned tilex,tiley,tilewide,tilehigh;
\r
95 int priority,updatecount;
\r
96 struct spriteliststruct **prevptr,*nextsprite;
\r
102 int screenx,screeny;
\r
109 unsigned current; // foreground tiles have high bit set
\r
114 typedef struct animtilestruct
\r
118 unsigned far *mapplane;
\r
119 struct animtilestruct **prevptr,*nexttile;
\r
123 =============================================================================
\r
127 =============================================================================
\r
131 long lasttimecount;
\r
133 boolean compatability; // crippled refresh for wierdo SVGAs
\r
135 unsigned mapwidth,mapheight,mapbyteswide,mapwordswide
\r
136 ,mapbytesextra,mapwordsextra;
\r
137 unsigned mapbwidthtable[MAXMAPHEIGHT];
\r
140 // Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow
\r
141 // for fractional movement and acceleration.
\r
143 // Tiles : Tile offsets from the upper left corner of the current map.
\r
145 // Screen : Graphics level offsets from map origin, x in bytes, y in pixels.
\r
146 // originxscreen is the same spot as originxtile, just with extra precision
\r
147 // so graphics don't need to be done in tile boundaries.
\r
150 unsigned originxglobal,originyglobal;
\r
151 unsigned originxtile,originytile;
\r
152 unsigned originxscreen,originyscreen;
\r
153 unsigned originmap;
\r
154 unsigned originxmin,originxmax,originymin,originymax;
\r
155 unsigned originxtile,originytile;
\r
157 unsigned masterofs;
\r
160 // Table of the offsets from bufferofs of each tile spot in the
\r
161 // view port. The extra wide tile should never be drawn, but the space
\r
162 // is needed to account for the extra 0 in the update arrays. Built by
\r
166 unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];
\r
167 unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH];
\r
169 unsigned uwidthtable[PORTTILESHIGH]; // lookup instead of multiply
\r
171 byte update[2][UPDATESIZE];
\r
172 byte *updateptr,*baseupdateptr, // current start of update window
\r
174 *baseupdatestart[2];
\r
177 cardtype videocard; // set by VW_Startup
\r
178 grtype grmode; // CGAgr, EGAgr, VGAgr
\r
180 unsigned bufferofs; // hidden area to draw to before displaying
\r
181 unsigned displayofs; // origin of the visable screen
\r
185 =============================================================================
\r
189 =============================================================================
\r
192 static char scratch[20],str[20];
\r
195 tiletype allanims[MAXANIMTYPES];
\r
196 unsigned numanimchains;
\r
198 void (*refreshvector) (void);
\r
200 unsigned screenstart[3] =
\r
201 {0,SCREENSPACE,SCREENSPACE*2};
\r
203 unsigned xpanmask; // prevent panning to odd pixels
\r
205 unsigned screenpage; // screen currently being displayed
\r
206 unsigned otherpage;
\r
208 #if GRMODE == EGAGR
\r
209 unsigned tilecache[NUMTILE16];
\r
212 spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],
\r
215 animtiletype animarray[MAXANIMTILES],*animhead,*animfreeptr;
\r
219 eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];
\r
222 =============================================================================
\r
226 =============================================================================
\r
229 void RFL_NewTile (unsigned updateoffset);
\r
230 void RFL_MaskForegroundTiles (void);
\r
231 void RFL_UpdateTiles (void);
\r
233 void RFL_CalcOriginStuff (long x, long y);
\r
234 void RFL_InitSpriteList (void);
\r
235 void RFL_InitAnimList (void);
\r
236 void RFL_CheckForAnimTile (unsigned x, unsigned y);
\r
237 void RFL_AnimateTiles (void);
\r
238 void RFL_RemoveAnimsOnX (unsigned x);
\r
239 void RFL_RemoveAnimsOnY (unsigned y);
\r
240 void RFL_EraseBlocks (void);
\r
241 void RFL_UpdateSprites (void);
\r
245 =============================================================================
\r
247 GRMODE INDEPENDANT ROUTINES
\r
249 =============================================================================
\r
254 =====================
\r
258 =====================
\r
261 static char *ParmStrings[] = {"comp",""};
\r
263 void RF_Startup (void)
\r
266 unsigned *blockstart;
\r
268 if (grmode == EGAGR)
\r
269 for (i = 1;i < _argc;i++)
\r
270 if (US_CheckParm(_argv[i],ParmStrings) == 0)
\r
272 compatability = true;
\r
276 for (i=0;i<PORTTILESHIGH;i++)
\r
277 uwidthtable[i] = UPDATEWIDE*i;
\r
279 originxmin = originymin = MAPBORDER*TILEGLOBAL;
\r
281 eraselistptr[0] = &eraselist[0][0];
\r
282 eraselistptr[1] = &eraselist[1][0];
\r
286 if (grmode == EGAGR)
\r
290 baseupdatestart[0] = &update[0][UPDATESPARESIZE];
\r
291 baseupdatestart[1] = &update[1][UPDATESPARESIZE];
\r
295 displayofs = screenstart[screenpage];
\r
296 bufferofs = screenstart[otherpage];
\r
297 masterofs = screenstart[2];
\r
299 updateptr = baseupdatestart[otherpage];
\r
301 blockstart = &blockstarts[0];
\r
302 for (y=0;y<UPDATEHIGH;y++)
\r
303 for (x=0;x<UPDATEWIDE;x++)
\r
304 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
306 xpanmask = 6; // dont pan to odd pixels
\r
309 else if (grmode == CGAGR)
\r
313 updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];
\r
316 masterofs = 0x8000;
\r
318 blockstart = &blockstarts[0];
\r
319 for (y=0;y<UPDATEHIGH;y++)
\r
320 for (x=0;x<UPDATEWIDE;x++)
\r
321 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
329 =====================
\r
333 =====================
\r
336 void RF_Shutdown (void)
\r
341 //===========================================================================
\r
344 =====================
\r
348 = Makes some convienient calculations based on maphead->
\r
350 =====================
\r
353 void RF_NewMap (void)
\r
356 unsigned spot,*table;
\r
358 mapwidth = mapheaderseg[mapon]->width;
\r
359 mapbyteswide = 2*mapwidth;
\r
360 mapheight = mapheaderseg[mapon]->height;
\r
361 mapwordsextra = mapwidth-PORTTILESWIDE;
\r
362 mapbytesextra = 2*mapwordsextra;
\r
365 // make a lookup table for the maps left edge
\r
368 for (i=0;i<mapheight;i++)
\r
370 mapbwidthtable[i] = spot;
\r
371 spot += mapbyteswide;
\r
375 // fill in updatemapofs with the new width info
\r
377 table = &updatemapofs[0];
\r
378 for (y=0;y<PORTTILESHIGH;y++)
\r
379 for (x=0;x<UPDATEWIDE;x++)
\r
380 *table++ = mapbwidthtable[y]+x*2;
\r
384 // the y max value clips off the bottom half of a tile so a map that is
\r
385 // 13 + MAPBORDER*2 tile high will not scroll at all vertically
\r
387 originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;
\r
388 originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;
\r
389 if (originxmax<originxmin) // for very small maps
\r
390 originxmax=originxmin;
\r
391 if (originymax<originymin)
\r
392 originymax=originymin;
\r
395 // clear out the lists
\r
397 RFL_InitSpriteList ();
\r
398 RFL_InitAnimList ();
\r
401 lasttimecount = TimeCount; // setup for adaptive timing
\r
405 //===========================================================================
\r
408 ==========================
\r
410 = RF_MarkTileGraphics
\r
412 = Goes through mapplane[0/1] and marks all background/foreground tiles
\r
413 = needed, then follows all animation sequences to make sure animated
\r
414 = tiles get all the stages. Every unique animating tile is given an
\r
415 = entry in allanims[], so every instance of that tile will animate at the
\r
416 = same rate. The info plane for each animating tile will hold a pointer
\r
417 = into allanims[], therefore you can't have both an animating foreground
\r
418 = and background tile in the same spot!
\r
420 ==========================
\r
423 void RF_MarkTileGraphics (void)
\r
426 int tile,next,anims;
\r
427 unsigned far *start,far *end,far *info;
\r
428 unsigned i,tilehigh;
\r
430 memset (allanims,0,sizeof(allanims));
\r
433 size = mapwidth*mapheight;
\r
436 // background plane
\r
438 start = mapsegs[0];
\r
444 if (tile>=0) // <0 is a tile that is never drawn
\r
446 CA_MarkGrChunk(STARTTILE16+tile);
\r
447 if (tinf[ANIM+tile])
\r
449 // this tile will animated
\r
451 for (i=0;i<numanimchains;i++)
\r
452 if (allanims[i].current == tile)
\r
454 *info = (unsigned)&allanims[i];
\r
458 // new chain of animating tiles
\r
460 if (i>=MAXANIMTYPES)
\r
461 //Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");
\r
462 allanims[i].current = tile;
\r
463 allanims[i].count = tinf[SPEED+tile];
\r
465 *info = (unsigned)&allanims[i];
\r
469 next = tile+(signed char)(tinf[ANIM+tile]);
\r
470 while (next != tile)
\r
472 CA_MarkGrChunk(STARTTILE16+next);
\r
473 next += (signed char)(tinf[ANIM+next]);
\r
475 //Quit ("MarkTileGraphics: Unending animation!");
\r
482 } while (start<end);
\r
485 // foreground plane
\r
487 start = mapsegs[1];
\r
493 if (tile>=0) // <0 is a tile that is never drawn
\r
495 CA_MarkGrChunk(STARTTILE16M+tile);
\r
496 if (tinf[MANIM+tile])
\r
498 // this tile will animated
\r
500 tilehigh = tile | 0x8000; // foreground tiles have high bit
\r
501 for (i=0;i<numanimchains;i++)
\r
502 if (allanims[i].current == tilehigh)
\r
504 *info = (unsigned)&allanims[i];
\r
508 // new chain of animating tiles
\r
510 if (i>=MAXANIMTYPES)
\r
511 //Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");
\r
512 allanims[i].current = tilehigh;
\r
513 allanims[i].count = tinf[MSPEED+tile];
\r
515 *info = (unsigned)&allanims[i];
\r
519 next = tile+(signed char)(tinf[MANIM+tile]);
\r
520 while (next != tile)
\r
522 CA_MarkGrChunk(STARTTILE16M+next);
\r
523 next += (signed char)(tinf[MANIM+next]);
\r
525 //Quit ("MarkTileGraphics: Unending animation!");
\r
532 } while (start<end);
\r
536 //===========================================================================
\r
540 =========================
\r
544 = Call to clear out the entire animating tile list and return all of them to
\r
547 =========================
\r
550 void RFL_InitAnimList (void)
\r
554 animfreeptr = &animarray[0];
\r
556 for (i=0;i<MAXANIMTILES-1;i++)
\r
557 animarray[i].nexttile = &animarray[i+1];
\r
559 animarray[i].nexttile = NULL;
\r
561 animhead = NULL; // nothing in list
\r
566 ====================
\r
568 = RFL_CheckForAnimTile
\r
570 ====================
\r
573 void RFL_CheckForAnimTile (unsigned x, unsigned y)
\r
575 unsigned tile,offset,speed,lasttime,thistime,timemissed;
\r
577 animtiletype *anim,*next;
\r
579 // the info plane of each animating tile has a near pointer into allanims[]
\r
580 // which gives the current state of all concurrently animating tiles
\r
582 offset = mapbwidthtable[y]/2+x;
\r
587 map = mapsegs[0]+offset;
\r
589 if (tinf[ANIM+tile])
\r
592 //Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
\r
593 anim = animfreeptr;
\r
594 animfreeptr = animfreeptr->nexttile;
\r
595 next = animhead; // stick it at the start of the list
\r
598 next->prevptr = &anim->nexttile;
\r
599 anim->nexttile = next;
\r
600 anim->prevptr = &animhead;
\r
605 anim->mapplane = map;
\r
606 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
612 map = mapsegs[1]+offset;
\r
614 if (tinf[MANIM+tile])
\r
617 //Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
\r
618 anim = animfreeptr;
\r
619 animfreeptr = animfreeptr->nexttile;
\r
620 next = animhead; // stick it at the start of the list
\r
623 next->prevptr = &anim->nexttile;
\r
624 anim->nexttile = next;
\r
625 anim->prevptr = &animhead;
\r
630 anim->mapplane = map;
\r
631 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
638 ====================
\r
640 = RFL_RemoveAnimsOnX
\r
642 ====================
\r
645 void RFL_RemoveAnimsOnX (unsigned x)
\r
647 animtiletype *current,*next;
\r
649 current = animhead;
\r
652 if (current->x == x)
\r
654 *(void **)current->prevptr = current->nexttile;
\r
655 if (current->nexttile)
\r
656 current->nexttile->prevptr = current->prevptr;
\r
657 next = current->nexttile;
\r
658 current->nexttile = animfreeptr;
\r
659 animfreeptr = current;
\r
663 current = current->nexttile;
\r
669 ====================
\r
671 = RFL_RemoveAnimsOnY
\r
673 ====================
\r
676 void RFL_RemoveAnimsOnY (unsigned y)
\r
678 animtiletype *current,*next;
\r
680 current = animhead;
\r
683 if (current->y == y)
\r
685 *(void **)current->prevptr = current->nexttile;
\r
686 if (current->nexttile)
\r
687 current->nexttile->prevptr = current->prevptr;
\r
688 next = current->nexttile;
\r
689 current->nexttile = animfreeptr;
\r
690 animfreeptr = current;
\r
694 current = current->nexttile;
\r
700 ====================
\r
704 ====================
\r
707 void RFL_AnimateTiles (void)
\r
709 animtiletype *current;
\r
710 unsigned updateofs,tile,x,y;
\r
714 // animate the lists of tiles
\r
716 anim = &allanims[0];
\r
717 while (anim->current)
\r
720 while ( anim->count < 1)
\r
722 if (anim->current & 0x8000)
\r
724 tile = anim->current & 0x7fff;
\r
725 tile += (signed char)tinf[MANIM+tile];
\r
726 anim->count += tinf[MSPEED+tile];
\r
731 tile = anim->current;
\r
732 tile += (signed char)tinf[ANIM+tile];
\r
733 anim->count += tinf[SPEED+tile];
\r
735 anim->current = tile;
\r
742 // traverse the list of animating tiles
\r
744 current = animhead;
\r
747 tile =current->chain->current;
\r
748 if ( tile != current->tile)
\r
750 // tile has animated
\r
752 // remove tile from master screen cache,
\r
753 // change a tile to its next state, set the structure up for
\r
754 // next animation, and post an update region to both update pages
\r
756 current->tile = tile;
\r
758 *(current->mapplane) = tile & 0x7fff; // change in map
\r
760 #if GRMODE == EGAGR
\r
761 if (tile<0x8000) // background
\r
762 tilecache[tile] = 0;
\r
765 x = current->x-originxtile;
\r
766 y = current->y-originytile;
\r
768 if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)
\r
769 //Quit ("RFL_AnimateTiles: Out of bounds!");
\r
771 updateofs = uwidthtable[y] + x;
\r
772 RFL_NewTile(updateofs); // puts "1"s in both pages
\r
774 current = current->nexttile;
\r
779 //===========================================================================
\r
782 =========================
\r
784 = RFL_InitSpriteList
\r
786 = Call to clear out the entire sprite list and return all of them to
\r
789 =========================
\r
792 void RFL_InitSpriteList (void)
\r
796 spritefreeptr = &spritearray[0];
\r
797 for (i=0;i<MAXSPRITES-1;i++)
\r
798 spritearray[i].nextsprite = &spritearray[i+1];
\r
800 spritearray[i].nextsprite = NULL;
\r
802 // NULL in all priority levels
\r
804 memset (prioritystart,0,sizeof(prioritystart));
\r
807 //===========================================================================
\r
812 = RFL_CalcOriginStuff
\r
814 = Calculate all the global variables for a new position
\r
815 = Long parms so position can be clipped to a maximum near 64k
\r
820 void RFL_CalcOriginStuff (long x, long y)
\r
824 else if (x>originxmax)
\r
829 else if (y>originymax)
\r
834 originxtile = originxglobal>>G_T_SHIFT;
\r
835 originytile = originyglobal>>G_T_SHIFT;
\r
836 originxscreen = originxtile<<SX_T_SHIFT;
\r
837 originyscreen = originytile<<SY_T_SHIFT;
\r
838 originmap = mapbwidthtable[originytile] + originxtile*2;
\r
840 #if GRMODE == EGAGR
\r
841 panx = (originxglobal>>G_P_SHIFT) & 15;
\r
843 pany = pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
844 panadjust = panx/8 + ylookup[pany];
\r
847 #if GRMODE == CGAGR
\r
848 panx = (originxglobal>>G_P_SHIFT) & 15;
\r
850 pany = pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
851 panadjust = pansx/4 + ylookup[pansy];
\r
856 //===========================================================================
\r
859 =====================
\r
861 = RF_SetRefreshHook
\r
863 =====================
\r
866 void RF_SetRefreshHook (void (*func) (void) )
\r
868 refreshvector = func;
\r
878 = Bring a new row of tiles onto the port, spawning animating tiles
\r
883 void RFL_NewRow (int dir)
\r
885 unsigned count,updatespot,updatestep;
\r
886 int x,y,xstep,ystep;
\r
897 count = PORTTILESWIDE;
\r
900 case 1: // right row
\r
901 updatespot = PORTTILESWIDE-1;
\r
902 updatestep = UPDATEWIDE;
\r
903 x = originxtile + PORTTILESWIDE-1;
\r
907 count = PORTTILESHIGH;
\r
910 case 2: // bottom row
\r
911 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
914 y = originytile + PORTTILESHIGH-1;
\r
917 count = PORTTILESWIDE;
\r
920 case 3: // left row
\r
922 updatestep = UPDATEWIDE;
\r
927 count = PORTTILESHIGH;
\r
930 //Quit ("RFL_NewRow: Bad dir!");
\r
935 RFL_NewTile(updatespot);
\r
936 RFL_CheckForAnimTile (x,y);
\r
937 updatespot+=updatestep;
\r
943 //===========================================================================
\r
946 =====================
\r
950 =====================
\r
953 void RF_ForceRefresh (void)
\r
955 RF_NewPosition (originxglobal,originyglobal);
\r
963 =============================================================================
\r
965 EGA specific routines
\r
967 =============================================================================
\r
970 #if GRMODE == EGAGR
\r
974 =====================
\r
976 = RF_NewPosition EGA
\r
978 =====================
\r
981 void RF_NewPosition (unsigned x, unsigned y)
\r
984 byte *page0ptr,*page1ptr;
\r
985 unsigned updatenum;
\r
988 // calculate new origin related globals
\r
990 RFL_CalcOriginStuff (x,y);
\r
993 // clear out all animating tiles
\r
995 RFL_InitAnimList ();
\r
998 // set up the new update arrays at base position
\r
1000 memset (tilecache,0,sizeof(tilecache)); // old cache is invalid
\r
1002 updatestart[0] = baseupdatestart[0];
\r
1003 updatestart[1] = baseupdatestart[1];
\r
1005 page0ptr = updatestart[0]+PORTTILESWIDE; // used to stick "0"s after rows
\r
1006 page1ptr = updatestart[1]+PORTTILESWIDE;
\r
1008 updatenum = 0; // start at first visable tile
\r
1010 for (my=0;my<PORTTILESHIGH;my++)
\r
1012 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
1014 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
1015 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
1019 *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles
\r
1020 page0ptr+=(PORTTILESWIDE+1);
\r
1021 page1ptr+=(PORTTILESWIDE+1);
\r
1023 *(word *)(page0ptr-PORTTILESWIDE)
\r
1024 = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
1027 //===========================================================================
\r
1034 = Uncache the trailing row of tiles
\r
1039 void RFL_OldRow (unsigned updatespot,unsigned count,unsigned step)
\r
1042 asm mov si,[updatespot] // pointer inside each map plane
\r
1043 asm mov cx,[count] // number of tiles to clear
\r
1044 asm mov dx,[step] // move to next tile
\r
1045 asm mov es,[WORD PTR mapsegs] // background plane
\r
1046 asm mov ds,[WORD PTR mapsegs+2] // foreground plane
\r
1051 asm jnz blockok // if a foreground tile, block wasn't cached
\r
1052 asm mov bx,[es:si]
\r
1054 asm mov [WORD PTR ss:tilecache+bx],0 //tile is no longer in master screen cache
\r
1057 asm loop clearcache
\r
1066 =====================
\r
1070 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
1071 = scroll if needed. If the scroll distance is greater than one tile, the
\r
1072 = entire screen will be redrawn (this could be generalized, but scrolling
\r
1073 = more than one tile per refresh is a bad idea!).
\r
1075 =====================
\r
1078 void RF_Scroll (int x, int y)
\r
1080 long neworgx,neworgy;
\r
1081 int i,deltax,deltay,absdx,absdy;
\r
1082 int oldxt,oldyt,move,yy;
\r
1083 unsigned updatespot;
\r
1084 byte *update0,*update1;
\r
1085 unsigned oldpanx,oldpanadjust,oldoriginmap,oldscreen,newscreen,screencopy;
\r
1088 oldxt = originxtile;
\r
1089 oldyt = originytile;
\r
1090 oldoriginmap = originmap;
\r
1091 oldpanadjust = panadjust;
\r
1094 RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);
\r
1096 deltax = originxtile - oldxt;
\r
1097 absdx = abs(deltax);
\r
1098 deltay = originytile - oldyt;
\r
1099 absdy = abs(deltay);
\r
1101 if (absdx>1 || absdy>1)
\r
1104 // scrolled more than one tile, so start from scratch
\r
1106 RF_NewPosition(originxglobal,originyglobal);
\r
1110 if (!absdx && !absdy)
\r
1111 return; // the screen has not scrolled an entire tile
\r
1115 // adjust screens and handle SVGA crippled compatability mode
\r
1117 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
1120 screenstart[i]+= screenmove;
\r
1121 if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )
\r
1124 // move the screen to the opposite end of the buffer
\r
1126 screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;
\r
1127 oldscreen = screenstart[i] - screenmove;
\r
1128 newscreen = oldscreen + screencopy;
\r
1129 screenstart[i] = newscreen + screenmove;
\r
1130 VW_ScreenToScreen (oldscreen,newscreen,
\r
1131 PORTTILESWIDE*2,PORTTILESHIGH*16);
\r
1133 if (i==screenpage)
\r
1134 VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);
\r
1137 bufferofs = screenstart[otherpage];
\r
1138 displayofs = screenstart[screenpage];
\r
1139 masterofs = screenstart[2];
\r
1143 // float the update regions
\r
1147 move += UPDATEWIDE;
\r
1148 else if (deltay==-1)
\r
1149 move -= UPDATEWIDE;
\r
1151 updatestart[0]+=move;
\r
1152 updatestart[1]+=move;
\r
1155 // draw the new tiles just scrolled on to the master screen, and
\r
1156 // mark them as needing to be copied to each screen next refreshes
\r
1157 // Make sure a zero is at the end of each row in update
\r
1164 RFL_NewRow (1); // new right row
\r
1165 RFL_OldRow (oldoriginmap,PORTTILESHIGH,mapbyteswide);
\r
1166 RFL_RemoveAnimsOnX (originxtile-1);
\r
1170 RFL_NewRow (3); // new left row
\r
1171 RFL_OldRow (oldoriginmap+(PORTTILESWIDE-1)*2,PORTTILESHIGH
\r
1173 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
1176 update0 = updatestart[0]+PORTTILESWIDE;
\r
1177 update1 = updatestart[1]+PORTTILESWIDE;
\r
1178 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
1180 *update0 = *update1 = 0; // drop a 0 at end of each row
\r
1181 update0+=UPDATEWIDE;
\r
1182 update1+=UPDATEWIDE;
\r
1186 //----------------
\r
1192 RFL_NewRow (2); // new bottom row
\r
1193 RFL_OldRow (oldoriginmap,PORTTILESWIDE,2);
\r
1194 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1195 RFL_RemoveAnimsOnY (originytile-1);
\r
1199 RFL_NewRow (0); // new top row
\r
1200 RFL_OldRow (oldoriginmap+mapbwidthtable[PORTTILESHIGH-1]
\r
1201 ,PORTTILESWIDE,2);
\r
1203 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
1206 *(updatestart[0]+updatespot+PORTTILESWIDE) =
\r
1207 *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;
\r
1210 //----------------
\r
1213 // place a new terminator
\r
1215 update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1216 update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1217 *update0++ = *update1++ = 0;
\r
1218 *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;
\r
1221 //===========================================================================
\r
1224 =====================
\r
1226 = RF_PlaceSprite EGA
\r
1228 =====================
\r
1231 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
1232 unsigned spritenumber, drawtype draw, int priority)
\r
1234 spritelisttype register *sprite,*next;
\r
1235 spritetabletype far *spr;
\r
1236 spritetype /*_seg*/ *block;
\r
1237 unsigned shift,pixx;
\r
1239 if (!spritenumber)
\r
1241 RF_RemoveSprite (user);
\r
1245 sprite = (spritelisttype *)*user;
\r
1249 // sprite allready exists in the list, so we can use it's block
\r
1252 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1253 // both pages may not need to be erased if the sprite just changed last frame
\r
1255 if (sprite->updatecount<2)
\r
1257 if (!sprite->updatecount)
\r
1258 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1259 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1262 if (priority != sprite->priority)
\r
1264 // sprite mvoed to another priority, so unlink the old one and
\r
1265 // relink it in the new priority
\r
1267 next = sprite->nextsprite; // cut old links
\r
1269 next->prevptr = sprite->prevptr;
\r
1270 *sprite->prevptr = next;
\r
1276 // this is a brand new sprite, so allocate a block from the array
\r
1278 if (!spritefreeptr)
\r
1279 //Quit ("RF_PlaceSprite: No free spots in spritearray!");
\r
1281 sprite = spritefreeptr;
\r
1282 spritefreeptr = spritefreeptr->nextsprite;
\r
1285 next = prioritystart[priority]; // stick it in new spot
\r
1287 next->prevptr = &sprite->nextsprite;
\r
1288 sprite->nextsprite = next;
\r
1289 prioritystart[priority] = sprite;
\r
1290 sprite->prevptr = &prioritystart[priority];
\r
1294 // write the new info to the sprite
\r
1296 spr = &spritetable[spritenumber-STARTSPRITES];
\r
1297 block = (spritetype /*_seg*/ *)grsegs[spritenumber];
\r
1299 globaly+=spr->orgy;
\r
1300 globalx+=spr->orgx;
\r
1302 pixx = globalx >> G_SY_SHIFT;
\r
1303 shift = (pixx&7)/2;
\r
1305 sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);
\r
1306 sprite->screeny = globaly >> G_SY_SHIFT;
\r
1307 sprite->width = block->width[shift];
\r
1308 sprite->height = spr->height;
\r
1309 sprite->grseg = spritenumber;
\r
1310 sprite->sourceofs = block->sourceoffset[shift];
\r
1311 sprite->planesize = block->planesize[shift];
\r
1312 sprite->draw = draw;
\r
1313 sprite->priority = priority;
\r
1314 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
1315 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
1316 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
1317 - sprite->tilex + 1;
\r
1318 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
1319 - sprite->tiley + 1;
\r
1321 sprite->updatecount = 2; // draw on next two refreshes
\r
1323 // save the sprite pointer off in the user's pointer so it can be moved
\r
1329 //===========================================================================
\r
1332 =====================
\r
1334 = RF_RemoveSprite EGA
\r
1336 =====================
\r
1339 void RF_RemoveSprite (void **user)
\r
1341 spritelisttype *sprite,*next;
\r
1343 sprite = (spritelisttype *)*user;
\r
1348 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1349 // both pages may not need to be erased if the sprite just changed last frame
\r
1351 if (sprite->updatecount<2)
\r
1353 if (!sprite->updatecount)
\r
1354 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1355 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1359 // unlink the sprite node
\r
1361 next = sprite->nextsprite;
\r
1362 if (next) // if (!next), sprite is last in chain
\r
1363 next->prevptr = sprite->prevptr;
\r
1364 *sprite->prevptr = next;
\r
1367 // add it back to the free list
\r
1369 sprite->nextsprite = spritefreeptr;
\r
1370 spritefreeptr = sprite;
\r
1373 // null the users pointer, so next time that actor gets placed, it will
\r
1374 // allocate a new block
\r
1381 //===========================================================================
\r
1385 ====================
\r
1387 = RFL_EraseBlocks EGA
\r
1389 = Write mode 1 should be set
\r
1391 ====================
\r
1394 void RFL_EraseBlocks (void)
\r
1396 eraseblocktype *block,*done;
\r
1397 int screenxh,screenyh;
\r
1398 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
1400 unsigned updatedelta;
\r
1401 unsigned erasecount;
\r
1407 block = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
1409 done = eraselistptr[otherpage];
\r
1411 while (block != done)
\r
1415 // clip the block to the current screen view
\r
1417 block->screenx -= originxscreen;
\r
1418 block->screeny -= originyscreen;
\r
1420 if (block->screenx < 0)
\r
1422 block->width += block->screenx;
\r
1423 if (block->width<1)
\r
1425 block->screenx = 0;
\r
1428 if (block->screeny < 0)
\r
1430 block->height += block->screeny;
\r
1431 if (block->height<1)
\r
1433 block->screeny = 0;
\r
1436 screenxh = block->screenx + block->width;
\r
1437 screenyh = block->screeny + block->height;
\r
1439 if (screenxh > EGAPORTSCREENWIDE)
\r
1441 block->width = EGAPORTSCREENWIDE-block->screenx;
\r
1442 screenxh = block->screenx + block->width;
\r
1445 if (screenyh > PORTSCREENHIGH)
\r
1447 block->height = PORTSCREENHIGH-block->screeny;
\r
1448 screenyh = block->screeny + block->height;
\r
1451 if (block->width<1 || block->height<1)
\r
1455 // erase the block by copying from the master screen
\r
1457 pos = ylookup[block->screeny]+block->screenx;
\r
1458 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
1459 block->width,block->height);
\r
1462 // put 2s in update where the block was, to force sprites to update
\r
1464 xtl = block->screenx >> SX_T_SHIFT;
\r
1465 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
1466 ytl = block->screeny >> SY_T_SHIFT;
\r
1467 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
1469 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
1470 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
1472 for (y=ytl;y<=yth;y++)
\r
1474 for (x=xtl;x<=xth;x++)
\r
1475 *updatespot++ = 2;
\r
1476 updatespot += updatedelta; // down to next line
\r
1485 eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
1487 strcpy (scratch,"\tErase:");
\r
1488 itoa (erasecount,str,10);
\r
1489 strcat (scratch,str);
\r
1490 write (profilehandle,scratch,strlen(scratch));
\r
1497 ====================
\r
1499 = RFL_UpdateSprites EGA
\r
1501 = NOTE: Implement vertical clipping!
\r
1503 ====================
\r
1506 void RFL_UpdateSprites (void)
\r
1508 spritelisttype *sprite;
\r
1509 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
1512 byte *updatespot,*baseupdatespot;
\r
1513 unsigned updatedelta;
\r
1514 unsigned updatecount;
\r
1515 unsigned height,sourceofs;
\r
1521 for (priority=0;priority<PRIORITIES;priority++)
\r
1523 if (priority==MASKEDTILEPRIORITY)
\r
1524 RFL_MaskForegroundTiles ();
\r
1526 for (sprite = prioritystart[priority]; sprite ;
\r
1527 sprite = (spritelisttype *)sprite->nextsprite)
\r
1530 // see if the sprite has any visable area in the port
\r
1533 portx = sprite->screenx - originxscreen;
\r
1534 porty = sprite->screeny - originyscreen;
\r
1535 xtl = portx >> SX_T_SHIFT;
\r
1536 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
1537 ytl = porty >> SY_T_SHIFT;
\r
1538 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
1542 if (xth>=PORTTILESWIDE)
\r
1543 xth = PORTTILESWIDE-1;
\r
1546 if (yth>=PORTTILESHIGH)
\r
1547 yth = PORTTILESHIGH-1;
\r
1549 if (xtl>xth || ytl>yth)
\r
1553 // see if it's visable area covers any non 0 update tiles
\r
1555 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
1556 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
1558 if (sprite->updatecount)
\r
1560 sprite->updatecount--; // the sprite was just placed,
\r
1561 goto redraw; // so draw it for sure
\r
1564 for (y=ytl;y<=yth;y++)
\r
1566 for (x=xtl;x<=xth;x++)
\r
1567 if (*updatespot++)
\r
1569 updatespot += updatedelta; // down to next line
\r
1571 continue; // no need to update
\r
1575 // set the tiles it covers to 3, because those tiles are being
\r
1578 updatespot = baseupdatespot;
\r
1579 for (y=ytl;y<=yth;y++)
\r
1581 for (x=xtl;x<=xth;x++)
\r
1582 *updatespot++ = 3;
\r
1583 updatespot += updatedelta; // down to next line
\r
1588 height = sprite->height;
\r
1589 sourceofs = sprite->sourceofs;
\r
1592 height += porty; // clip top off
\r
1593 sourceofs -= porty*sprite->width;
\r
1596 else if (porty+height>PORTSCREENHIGH)
\r
1598 height = PORTSCREENHIGH - porty; // clip bottom off
\r
1601 dest = bufferofs + ylookup[porty] + portx;
\r
1603 switch (sprite->draw)
\r
1606 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
1607 dest,sprite->width,height,sprite->planesize);
\r
1622 strcpy (scratch,"\tSprites:");
\r
1623 itoa (updatecount,str,10);
\r
1624 strcat (scratch,str);
\r
1625 write (profilehandle,scratch,strlen(scratch));
\r
1632 =====================
\r
1636 = All routines will draw at the port at bufferofs, possibly copying from
\r
1637 = the port at masterofs. The EGA version then page flips, while the
\r
1638 = CGA version updates the screen from the buffer port.
\r
1640 = Screenpage is the currently displayed page, not the one being drawn
\r
1641 = Otherpage is the page to be worked with now
\r
1643 =====================
\r
1646 void RF_Refresh (void)
\r
1651 updateptr = updatestart[otherpage];
\r
1653 RFL_AnimateTiles (); // DEBUG
\r
1656 // update newly scrolled on tiles and animated tiles from the master screen
\r
1660 RFL_UpdateTiles ();
\r
1661 RFL_EraseBlocks ();
\r
1664 // Update is all 0 except where sprites have changed or new area has
\r
1665 // been scrolled on. Go through all sprites and update the ones that cover
\r
1666 // a non 0 update tile
\r
1669 RFL_UpdateSprites ();
\r
1672 // if the main program has a refresh hook set, call their function before
\r
1673 // displaying the new page
\r
1675 if (refreshvector)
\r
1679 // display the changed screen
\r
1681 VW_SetScreen(bufferofs+panadjust,panx & xpanmask);
\r
1684 // prepare for next refresh
\r
1686 // Set the update array to the middle position and clear it out to all "0"s
\r
1687 // with an UPDATETERMINATE at the end
\r
1689 updatestart[otherpage] = newupdate = baseupdatestart[otherpage];
\r
1693 asm mov cx,(UPDATESCREENSIZE-2)/2
\r
1694 asm mov di,[newupdate]
\r
1696 asm mov [WORD PTR es:di],UPDATETERMINATE
\r
1700 bufferofs = screenstart[otherpage];
\r
1701 displayofs = screenstart[screenpage];
\r
1704 // calculate tics since last refresh for adaptive timing
\r
1706 if (lasttimecount > TimeCount)
\r
1707 lasttimecount = TimeCount; // if the game was paused a LONG time
\r
1710 newtime = TimeCount;
\r
1711 tics = newtime-lasttimecount;
\r
1712 } while (tics<MINTICS);
\r
1713 lasttimecount = newtime;
\r
1716 strcpy (scratch,"\tTics:");
\r
1717 itoa (tics,str,10);
\r
1718 strcat (scratch,str);
\r
1719 strcat (scratch,"\n");
\r
1720 write (profilehandle,scratch,strlen(scratch));
\r
1725 TimeCount -= (tics-MAXTICS);
\r
1730 #endif // GRMODE == EGAGR
\r
1733 =============================================================================
\r
1735 CGA specific routines
\r
1737 =============================================================================
\r
1740 #if GRMODE == CGAGR
\r
1744 =====================
\r
1746 = RF_NewPosition CGA
\r
1748 =====================
\r
1751 void RF_NewPosition (unsigned x, unsigned y)
\r
1755 unsigned updatenum;
\r
1758 // calculate new origin related globals
\r
1760 RFL_CalcOriginStuff (x,y);
\r
1763 // clear out all animating tiles
\r
1765 RFL_InitAnimList ();
\r
1768 // set up the new update arrays at base position
\r
1770 updateptr = baseupdateptr;
\r
1772 spotptr = updateptr + PORTTILESWIDE; // used to stick "0"s after rows
\r
1774 updatenum = 0; // start at first visable tile
\r
1776 for (my=0;my<PORTTILESHIGH;my++)
\r
1778 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
1780 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
1781 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
1785 *spotptr = 0; // set a 0 at end of a line of tiles
\r
1786 spotptr +=(PORTTILESWIDE+1);
\r
1788 *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
1793 =====================
\r
1797 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
1798 = scroll if needed. If the scroll distance is greater than one tile, the
\r
1799 = entire screen will be redrawn (this could be generalized, but scrolling
\r
1800 = more than one tile per refresh is a bad idea!).
\r
1802 =====================
\r
1805 void RF_Scroll (int x, int y)
\r
1807 long neworgx,neworgy;
\r
1808 int i,deltax,deltay,absdx,absdy;
\r
1809 int oldxt,oldyt,move,yy;
\r
1810 unsigned updatespot;
\r
1812 unsigned oldoriginmap,oldscreen,newscreen,screencopy;
\r
1815 oldxt = originxtile;
\r
1816 oldyt = originytile;
\r
1818 RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);
\r
1820 deltax = originxtile - oldxt;
\r
1821 absdx = abs(deltax);
\r
1822 deltay = originytile - oldyt;
\r
1823 absdy = abs(deltay);
\r
1825 if (absdx>1 || absdy>1)
\r
1828 // scrolled more than one tile, so start from scratch
\r
1830 RF_NewPosition(originxglobal,originyglobal);
\r
1834 if (!absdx && !absdy)
\r
1835 return; // the screen has not scrolled an entire tile
\r
1841 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
1842 bufferofs += screenmove;
\r
1843 masterofs += screenmove;
\r
1847 // float the update regions
\r
1851 move += UPDATEWIDE;
\r
1852 else if (deltay==-1)
\r
1853 move -= UPDATEWIDE;
\r
1858 // draw the new tiles just scrolled on to the master screen, and
\r
1859 // mark them as needing to be copied to each screen next refreshes
\r
1860 // Make sure a zero is at the end of each row in update
\r
1867 RFL_NewRow (1); // new right row
\r
1868 RFL_RemoveAnimsOnX (originxtile-1);
\r
1872 RFL_NewRow (3); // new left row
\r
1873 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
1876 spotptr = updateptr+PORTTILESWIDE;
\r
1877 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
1879 *spotptr = 0; // drop a 0 at end of each row
\r
1880 spotptr+=UPDATEWIDE;
\r
1884 //----------------
\r
1890 RFL_NewRow (2); // new bottom row
\r
1891 *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;
\r
1892 RFL_RemoveAnimsOnY (originytile-1);
\r
1896 RFL_NewRow (0); // new top row
\r
1897 *(updateptr+PORTTILESWIDE) = 0;
\r
1898 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
1902 //----------------
\r
1905 // place a new terminator
\r
1907 spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;
\r
1909 *(unsigned *)spotptr = UPDATETERMINATE;
\r
1913 =====================
\r
1915 = RF_PlaceSprite CGA
\r
1917 =====================
\r
1920 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
1921 unsigned spritenumber, drawtype draw, int priority)
\r
1923 spritelisttype register *sprite,*next;
\r
1924 spritetabletype far *spr;
\r
1925 spritetype /*_seg*/ *block;
\r
1926 unsigned shift,pixx;
\r
1928 if (!spritenumber)
\r
1930 RF_RemoveSprite (user);
\r
1934 sprite = (spritelisttype *)*user;
\r
1938 // sprite allready exists in the list, so we can use it's block
\r
1941 // post an erase block to erase the old position by copying
\r
1942 // screenx,screeny,width,height
\r
1944 if (!sprite->updatecount) // may not have been drawn at all yet
\r
1945 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
1947 if (priority != sprite->priority)
\r
1949 // sprite moved to another priority, so unlink the old one and
\r
1950 // relink it in the new priority
\r
1952 next = sprite->nextsprite; // cut old links
\r
1954 next->prevptr = sprite->prevptr;
\r
1955 *sprite->prevptr = next;
\r
1961 // this is a brand new sprite, so allocate a block from the array
\r
1963 if (!spritefreeptr)
\r
1964 //Quit ("RF_PlaceSprite: No free spots in spritearray!");
\r
1966 sprite = spritefreeptr;
\r
1967 spritefreeptr = spritefreeptr->nextsprite;
\r
1970 next = prioritystart[priority]; // stick it in new spot
\r
1972 next->prevptr = &sprite->nextsprite;
\r
1973 sprite->nextsprite = next;
\r
1974 prioritystart[priority] = sprite;
\r
1975 sprite->prevptr = &prioritystart[priority];
\r
1979 // write the new info to the sprite
\r
1981 spr = &spritetable[spritenumber-STARTSPRITES];
\r
1982 block = (spritetype /*_seg*/ *)grsegs[spritenumber];
\r
1984 globaly+=spr->orgy;
\r
1985 globalx+=spr->orgx;
\r
1987 sprite->screenx = globalx >> G_CGASX_SHIFT;
\r
1988 sprite->screeny = globaly >> G_SY_SHIFT;
\r
1989 sprite->width = block->width[0];
\r
1990 sprite->height = spr->height;
\r
1991 sprite->grseg = spritenumber;
\r
1992 sprite->sourceofs = block->sourceoffset[0];
\r
1993 sprite->planesize = block->planesize[0];
\r
1994 sprite->draw = draw;
\r
1995 sprite->priority = priority;
\r
1996 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
1997 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
1998 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
1999 - sprite->tilex + 1;
\r
2000 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
2001 - sprite->tiley + 1;
\r
2003 sprite->updatecount = 1; // draw on next refresh
\r
2005 // save the sprite pointer off in the user's pointer so it can be moved
\r
2011 //===========================================================================
\r
2014 =====================
\r
2016 = RF_RemoveSprite CGA
\r
2018 =====================
\r
2021 void RF_RemoveSprite (void **user)
\r
2023 spritelisttype *sprite,*next;
\r
2025 sprite = (spritelisttype *)*user;
\r
2030 // post an erase block to erase the old position by copying
\r
2031 // screenx,screeny,width,height
\r
2033 if (!sprite->updatecount)
\r
2035 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2039 // unlink the sprite node
\r
2041 next = sprite->nextsprite;
\r
2042 if (next) // if (!next), sprite is last in chain
\r
2043 next->prevptr = sprite->prevptr;
\r
2044 *sprite->prevptr = next;
\r
2047 // add it back to the free list
\r
2049 sprite->nextsprite = spritefreeptr;
\r
2050 spritefreeptr = sprite;
\r
2053 // null the users pointer, so next time that actor gets placed, it will
\r
2054 // allocate a new block
\r
2062 ====================
\r
2064 = RFL_EraseBlocks CGA
\r
2066 = Write mode 1 should be set
\r
2068 ====================
\r
2071 void RFL_EraseBlocks (void)
\r
2073 eraseblocktype *block,*done;
\r
2074 int screenxh,screenyh;
\r
2075 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
2077 unsigned updatedelta;
\r
2079 block = &eraselist[0][0];
\r
2081 done = eraselistptr[0];
\r
2083 while (block != done)
\r
2087 // clip the block to the current screen view
\r
2089 block->screenx -= originxscreen;
\r
2090 block->screeny -= originyscreen;
\r
2092 if (block->screenx < 0)
\r
2094 block->width += block->screenx;
\r
2095 if (block->width<1)
\r
2097 block->screenx = 0;
\r
2100 if (block->screeny < 0)
\r
2102 block->height += block->screeny;
\r
2103 if (block->height<1)
\r
2105 block->screeny = 0;
\r
2108 screenxh = block->screenx + block->width;
\r
2109 screenyh = block->screeny + block->height;
\r
2111 if (screenxh > CGAPORTSCREENWIDE)
\r
2113 block->width = CGAPORTSCREENWIDE-block->screenx;
\r
2114 screenxh = block->screenx + block->width;
\r
2117 if (screenyh > PORTSCREENHIGH)
\r
2119 block->height = PORTSCREENHIGH-block->screeny;
\r
2120 screenyh = block->screeny + block->height;
\r
2123 if (block->width<1 || block->height<1)
\r
2127 // erase the block by copying from the master screen
\r
2129 pos = ylookup[block->screeny]+block->screenx;
\r
2130 block->width = (block->width + (pos&1) + 1)& ~1;
\r
2131 pos &= ~1; // make sure a word copy gets used
\r
2132 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2133 block->width,block->height);
\r
2136 // put 2s in update where the block was, to force sprites to update
\r
2138 xtl = block->screenx >> SX_T_SHIFT;
\r
2139 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2140 ytl = block->screeny >> SY_T_SHIFT;
\r
2141 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2143 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2144 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2146 for (y=ytl;y<=yth;y++)
\r
2148 for (x=xtl;x<=xth;x++)
\r
2149 *updatespot++ = 2;
\r
2150 updatespot += updatedelta; // down to next line
\r
2156 eraselistptr[0] = &eraselist[0][0];
\r
2161 ====================
\r
2163 = RFL_UpdateSprites CGA
\r
2165 = NOTE: Implement vertical clipping!
\r
2167 ====================
\r
2170 void RFL_UpdateSprites (void)
\r
2172 spritelisttype *sprite;
\r
2173 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2176 byte *updatespot,*baseupdatespot;
\r
2177 unsigned updatedelta;
\r
2179 unsigned updatecount;
\r
2180 unsigned height,sourceofs;
\r
2187 for (priority=0;priority<PRIORITIES;priority++)
\r
2189 if (priority==MASKEDTILEPRIORITY)
\r
2190 RFL_MaskForegroundTiles ();
\r
2192 for (sprite = prioritystart[priority]; sprite ;
\r
2193 sprite = (spritelisttype *)sprite->nextsprite)
\r
2196 // see if the sprite has any visable area in the port
\r
2199 portx = sprite->screenx - originxscreen;
\r
2200 porty = sprite->screeny - originyscreen;
\r
2201 xtl = portx >> SX_T_SHIFT;
\r
2202 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2203 ytl = porty >> SY_T_SHIFT;
\r
2204 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2208 if (xth>=PORTTILESWIDE)
\r
2209 xth = PORTTILESWIDE-1;
\r
2212 if (yth>=PORTTILESHIGH)
\r
2213 yth = PORTTILESHIGH-1;
\r
2215 if (xtl>xth || ytl>yth)
\r
2219 // see if it's visable area covers any non 0 update tiles
\r
2221 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2222 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2224 if (sprite->updatecount)
\r
2226 sprite->updatecount--; // the sprite was just placed,
\r
2227 goto redraw; // so draw it for sure
\r
2230 for (y=ytl;y<=yth;y++)
\r
2232 for (x=xtl;x<=xth;x++)
\r
2233 if (*updatespot++)
\r
2235 updatespot += updatedelta; // down to next line
\r
2237 continue; // no need to update
\r
2241 // set the tiles it covers to 3, because those tiles are being
\r
2244 updatespot = baseupdatespot;
\r
2245 for (y=ytl;y<=yth;y++)
\r
2247 for (x=xtl;x<=xth;x++)
\r
2248 *updatespot++ = 3;
\r
2249 updatespot += updatedelta; // down to next line
\r
2254 height = sprite->height;
\r
2255 sourceofs = sprite->sourceofs;
\r
2258 height += porty; // clip top off
\r
2259 sourceofs -= porty*sprite->width;
\r
2262 else if (porty+height>PORTSCREENHIGH)
\r
2264 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2267 dest = bufferofs + ylookup[porty] + portx;
\r
2269 switch (sprite->draw)
\r
2272 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2273 dest,sprite->width,height,sprite->planesize);
\r
2291 =====================
\r
2295 = All routines will draw at the port at bufferofs, possibly copying from
\r
2296 = the port at masterofs. The EGA version then page flips, while the
\r
2297 = CGA version updates the screen from the buffer port.
\r
2299 = Screenpage is the currently displayed page, not the one being drawn
\r
2300 = Otherpage is the page to be worked with now
\r
2302 =====================
\r
2305 void RF_Refresh (void)
\r
2309 RFL_AnimateTiles ();
\r
2312 // update newly scrolled on tiles and animated tiles from the master screen
\r
2314 RFL_UpdateTiles ();
\r
2315 RFL_EraseBlocks ();
\r
2318 // Update is all 0 except where sprites have changed or new area has
\r
2319 // been scrolled on. Go through all sprites and update the ones that cover
\r
2320 // a non 0 update tile
\r
2322 RFL_UpdateSprites ();
\r
2325 // if the main program has a refresh hook set, call their function before
\r
2326 // displaying the new page
\r
2328 if (refreshvector)
\r
2332 // update everything to the screen
\r
2334 VW_CGAFullUpdate ();
\r
2337 // calculate tics since last refresh for adaptive timing
\r
2339 if (lasttimecount > TimeCount)
\r
2340 lasttimecount = TimeCount; // if the game was paused a LONG time
\r
2343 newtime = TimeCount;
\r
2344 tics = newtime-lasttimecount;
\r
2345 } while (tics<MINTICS);
\r
2346 lasttimecount = newtime;
\r
2349 itoa (tics,str,10);
\r
2350 strcat (str,"\t");
\r
2351 ltoa (TimeCount,str2,10);
\r
2352 strcat (str,str2);
\r
2353 strcat (str,"\t");
\r
2354 ltoa (LocalTime,str2,10);
\r
2355 strcat (str,str2);
\r
2356 strcat (str,"\n");
\r
2357 write (profile,str,strlen(str));
\r
2364 #endif // GRMODE == CGAGR
\r
2365 //===============================
\r
2367 ; Keen Dreams Source Code
\r
2368 ; Copyright (C) 2014 Javier M. Chavez
\r
2370 ; This program is free software; you can redistribute it and/or modify
\r
2371 ; it under the terms of the GNU General Public License as published by
\r
2372 ; the Free Software Foundation; either version 2 of the License, or
\r
2373 ; (at your option) any later version.
\r
2375 ; This program is distributed in the hope that it will be useful,
\r
2376 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
2377 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
2378 ; GNU General Public License for more details.
\r
2380 ; You should have received a copy of the GNU General Public License along
\r
2381 ; with this program; if not, write to the Free Software Foundation, Inc.,
\r
2382 ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
2389 INCLUDE "ID_ASM.EQU"
\r
2391 CACHETILES = 1 ;enable master screen tile caching
\r
2393 ;============================================================================
\r
2398 UPDATESIZE = (TILESWIDE+1)*TILESHIGH+1
\r
2402 EXTRN screenseg:WORD
\r
2403 EXTRN updateptr:WORD
\r
2404 EXTRN updatestart:WORD
\r
2405 EXTRN masterofs:WORD ;start of master tile port
\r
2406 EXTRN bufferofs:WORD ;start of current buffer port
\r
2407 EXTRN screenstart:WORD ;starts of three screens (0/1/master) in EGA mem
\r
2409 EXTRN mapsegs:WORD
\r
2410 EXTRN originmap:WORD
\r
2411 EXTRN updatemapofs:WORD
\r
2412 EXTRN tilecache:WORD
\r
2413 EXTRN tinf:WORD ;seg pointer to map header and tile info
\r
2414 EXTRN blockstarts:WORD ;offsets from bufferofs for each update block
\r
2421 screenstartcs dw ? ;in code segment for accesability
\r
2427 ;============================================================================
\r
2429 ; CGA refresh routines
\r
2431 ;============================================================================
\r
2435 ;=================
\r
2439 ; Draws a composit two plane tile to the master screen and sets the update
\r
2440 ; spot to 1 in both update pages, forcing the tile to be copied to the
\r
2441 ; view pages the next two refreshes
\r
2443 ; Called to draw newlly scrolled on strips and animating tiles
\r
2445 ;=================
\r
2447 PROC RFL_NewTile updateoffset:WORD
\r
2448 PUBLIC RFL_NewTile
\r
2452 ; mark both update lists at this spot
\r
2454 mov di,[updateoffset]
\r
2456 mov bx,[updateptr] ;start of update matrix
\r
2457 mov [BYTE bx+di],1
\r
2459 mov dx,SCREENWIDTH-TILEWIDTH ;add to get to start of next line
\r
2462 ; set di to the location in screenseg to draw the tile
\r
2465 mov si,[updatemapofs+di] ;offset in map from origin
\r
2466 add si,[originmap]
\r
2467 mov di,[blockstarts+di] ;screen location for tile
\r
2468 add di,[masterofs]
\r
2471 ; set BX to the foreground tile number and SI to the background number
\r
2472 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together
\r
2473 ; as one of the planes totally eclipses the other
\r
2475 mov es,[mapsegs+2] ;foreground plane
\r
2477 mov es,[mapsegs] ;background plane
\r
2480 mov es,[screenseg]
\r
2484 jmp @@maskeddraw ;draw both together
\r
2488 ; Draw single background tile from main memory
\r
2494 mov ds,[grsegs+STARTTILE16*2+si]
\r
2496 xor si,si ;block is segment aligned
\r
2507 mov ds,ax ;restore turbo's data segment
\r
2513 ; Draw a masked tile combo
\r
2514 ; Interupts are disabled and the stack segment is reassigned
\r
2518 cli ; don't allow ints when SS is set
\r
2520 mov ss,[grsegs+STARTTILE16M*2+bx]
\r
2522 mov ds,[grsegs+STARTTILE16*2+si]
\r
2524 xor si,si ;first word of tile data
\r
2527 mov ax,[si] ;background tile
\r
2528 and ax,[ss:si] ;mask
\r
2529 or ax,[ss:si+64] ;masked data
\r
2531 mov ax,[si+2] ;background tile
\r
2532 and ax,[ss:si+2] ;mask
\r
2533 or ax,[ss:si+66] ;masked data
\r
2551 ;===========================================================================
\r
2553 ; EGA refresh routines
\r
2555 ;===========================================================================
\r
2559 ;=================
\r
2563 ; Draws a composit two plane tile to the master screen and sets the update
\r
2564 ; spot to 1 in both update pages, forcing the tile to be copied to the
\r
2565 ; view pages the next two refreshes
\r
2567 ; Called to draw newlly scrolled on strips and animating tiles
\r
2569 ; Assumes write mode 0
\r
2571 ;=================
\r
2573 PROC RFL_NewTile updateoffset:WORD
\r
2574 PUBLIC RFL_NewTile
\r
2578 ; mark both update lists at this spot
\r
2580 mov di,[updateoffset]
\r
2582 mov bx,[updatestart] ;page 0 pointer
\r
2583 mov [BYTE bx+di],1
\r
2584 mov bx,[updatestart+2] ;page 1 pointer
\r
2585 mov [BYTE bx+di],1
\r
2588 ; set screenstartcs to the location in screenseg to draw the tile
\r
2591 mov si,[updatemapofs+di] ;offset in map from origin
\r
2592 add si,[originmap]
\r
2593 mov di,[blockstarts+di] ;screen location for tile
\r
2594 add di,[masterofs]
\r
2595 mov [cs:screenstartcs],di
\r
2598 ; set BX to the foreground tile number and SI to the background number
\r
2599 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together
\r
2600 ; as one of the planes totally eclipses the other
\r
2602 mov es,[mapsegs+2] ;foreground plane
\r
2604 mov es,[mapsegs] ;background plane
\r
2607 mov es,[screenseg]
\r
2608 mov dx,SC_INDEX ;for stepping through map mask planes
\r
2612 jmp @@maskeddraw ;draw both together
\r
2616 ; No foreground tile, so draw a single background tile.
\r
2617 ; Use the master screen cache if possible
\r
2622 mov bx,SCREENWIDTH-2 ;add to get to start of next line
\r
2629 mov ax,[tilecache+si]
\r
2634 ; Draw single tile from cache
\r
2640 mov ax,SC_MAPMASK + 15*256 ;all planes
\r
2644 mov ax,GC_MODE + 1*256 ;write mode 1
\r
2647 mov di,[cs:screenstartcs]
\r
2648 mov ds,[screenseg]
\r
2659 xor ah,ah ;write mode 0
\r
2663 mov ds,ax ;restore turbo's data segment
\r
2668 ; Draw single tile from main memory
\r
2673 mov ax,[cs:screenstartcs]
\r
2674 mov [tilecache+si],ax ;next time it can be drawn from here with latch
\r
2675 mov ds,[grsegs+STARTTILE16*2+si]
\r
2677 xor si,si ;block is segment aligned
\r
2679 mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0
\r
2681 mov cx,4 ;draw four planes
\r
2686 mov di,[cs:screenstartcs] ;start at same place in all planes
\r
2694 shl ah,1 ;shift plane mask over for next plane
\r
2698 mov ds,ax ;restore turbo's data segment
\r
2704 ; Draw a masked tile combo
\r
2705 ; Interupts are disabled and the stack segment is reassigned
\r
2709 cli ; don't allow ints when SS is set
\r
2711 mov ss,[grsegs+STARTTILE16M*2+bx]
\r
2713 mov ds,[grsegs+STARTTILE16*2+si]
\r
2715 xor si,si ;first word of tile data
\r
2717 mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0
\r
2719 mov di,[cs:screenstartcs]
\r
2725 mov bx,[si+tileofs] ;background tile
\r
2726 and bx,[ss:tileofs] ;mask
\r
2727 or bx,[ss:si+tileofs+32] ;masked data
\r
2728 mov [es:di+lineoffset],bx
\r
2729 tileofs = tileofs + 2
\r
2730 lineoffset = lineoffset + SCREENWIDTH
\r
2733 shl ah,1 ;shift plane mask over for next plane
\r
2735 je @@done ;drawn all four planes
\r
2749 ;============================================================================
\r
2751 ; VGA refresh routines
\r
2753 ;============================================================================
\r
2759 ;============================================================================
\r
2761 ; reasonably common refresh routines
\r
2763 ;============================================================================
\r
2766 ;=================
\r
2770 ; Scans through the update matrix pointed to by updateptr, looking for 1s.
\r
2771 ; A 1 represents a tile that needs to be copied from the master screen to the
\r
2772 ; current screen (a new row or an animated tiled). If more than one adjacent
\r
2773 ; tile in a horizontal row needs to be copied, they will be copied as a group.
\r
2775 ; Assumes write mode 1
\r
2777 ;=================
\r
2780 ; AX 0/1 for scasb, temp for segment register transfers
\r
2781 ; BX width for block copies
\r
2783 ; DX line width deltas
\r
2784 ; SI source for copies
\r
2785 ; DI scas dest / movsb dest
\r
2786 ; BP pointer to UPDATETERMINATE
\r
2792 PROC RFL_UpdateTiles
\r
2793 PUBLIC RFL_UpdateTiles
\r
2796 jmp SHORT @@realstart
\r
2799 ; all tiles have been scanned
\r
2804 mov di,[updateptr]
\r
2805 mov bp,(TILESWIDE+1)*TILESHIGH+1
\r
2806 add bp,di ; when di = bx, all tiles have been scanned
\r
2808 mov cx,-1 ; definately scan the entire thing
\r
2811 ; scan for a 1 in the update list, meaning a tile needs to be copied
\r
2812 ; from the master screen to the current screen
\r
2815 pop di ; place to continue scaning from
\r
2817 mov es,ax ; search in the data segment
\r
2830 ; copy a single tile
\r
2835 inc di ; we know the next tile is nothing
\r
2836 push di ; save off the spot being scanned
\r
2837 sub di,[updateptr]
\r
2839 mov di,[blockstarts-4+di] ; start of tile location on screen
\r
2841 add di,[bufferofs] ; dest in current screen
\r
2842 add si,[masterofs] ; source in master screen
\r
2844 mov dx,SCREENWIDTH-TILEWIDTH
\r
2845 mov ax,[screenseg]
\r
2849 ;--------------------------
\r
2864 ;--------------------------
\r
2879 ;--------------------------
\r
2885 ; more than one tile in a row needs to be updated, so do it as a group
\r
2890 mov dx,di ; hold starting position + 1 in dx
\r
2891 inc di ; we know the next tile also gets updated
\r
2892 repe scasb ; see how many more in a row
\r
2893 push di ; save off the spot being scanned
\r
2896 sub bx,dx ; number of tiles in a row
\r
2897 shl bx,1 ; number of bytes / row
\r
2899 mov di,dx ; lookup position of start tile
\r
2900 sub di,[updateptr]
\r
2902 mov di,[blockstarts-2+di] ; start of tile location
\r
2904 add di,[bufferofs] ; dest in current screen
\r
2905 add si,[masterofs] ; source in master screen
\r
2907 mov dx,SCREENWIDTH
\r
2908 sub dx,bx ; offset to next line on screen
\r
2910 sub dx,bx ; bx is words wide in CGA tiles
\r
2913 mov ax,[screenseg]
\r
2936 dec cx ; was 0 from last rep movsb, now $ffff for scasb
\r
2942 ;============================================================================
\r
2945 ;=================
\r
2947 ; RFL_MaskForegroundTiles
\r
2949 ; Scan through update looking for 3's. If the foreground tile there is a
\r
2950 ; masked foreground tile, draw it to the screen
\r
2952 ;=================
\r
2954 PROC RFL_MaskForegroundTiles
\r
2955 PUBLIC RFL_MaskForegroundTiles
\r
2957 jmp SHORT @@realstart
\r
2960 ; all tiles have been scanned
\r
2965 mov di,[updateptr]
\r
2966 mov bp,(TILESWIDE+1)*TILESHIGH+2
\r
2967 add bp,di ; when di = bx, all tiles have been scanned
\r
2969 mov cx,-1 ; definately scan the entire thing
\r
2971 ; scan for a 3 in the update list
\r
2975 mov es,ax ; scan in the data segment
\r
2977 pop di ; place to continue scaning from
\r
2984 ; found a tile, see if it needs to be masked on
\r
2990 sub di,[updateptr]
\r
2992 mov si,[updatemapofs-2+di] ; offset from originmap
\r
2993 add si,[originmap]
\r
2995 mov es,[mapsegs+2] ; foreground map plane segment
\r
2996 mov si,[es:si] ; foreground tile number
\r
2999 jz @@findtile ; 0 = no foreground tile
\r
3002 add bx,INTILE ;INTILE tile info table
\r
3004 test [BYTE PTR es:bx],80h ;high bit = masked tile
\r
3007 ;-------------------
\r
3010 ;=================
\r
3012 ; mask the tile CGA
\r
3014 ;=================
\r
3016 mov di,[blockstarts-2+di]
\r
3017 add di,[bufferofs]
\r
3018 mov es,[screenseg]
\r
3020 mov ds,[grsegs+STARTTILE16M*2+si]
\r
3022 mov bx,64 ;data starts 64 bytes after mask
\r
3028 mov ax,[es:di+lineoffset] ;background
\r
3030 or ax,[si+bx] ;masked data
\r
3031 mov [es:di+lineoffset],ax ;background
\r
3034 mov ax,[es:di+lineoffset+2] ;background
\r
3036 or ax,[si+bx] ;masked data
\r
3037 mov [es:di+lineoffset+2],ax ;background
\r
3040 lineoffset = lineoffset + SCREENWIDTH
\r
3044 ;-------------------
\r
3047 ;=================
\r
3051 ;=================
\r
3053 mov [BYTE planemask],1
\r
3054 mov [BYTE planenum],0
\r
3056 mov di,[blockstarts-2+di]
\r
3057 add di,[bufferofs]
\r
3058 mov [cs:screenstartcs],di
\r
3059 mov es,[screenseg]
\r
3061 mov ds,[grsegs+STARTTILE16M*2+si]
\r
3063 mov bx,32 ;data starts 32 bytes after mask
\r
3068 mov ah,[ss:planemask]
\r
3072 mov ah,[ss:planenum]
\r
3076 mov di,[cs:screenstartcs]
\r
3079 mov cx,[es:di+lineoffset] ;background
\r
3081 or cx,[si+bx] ;masked data
\r
3084 mov [es:di+lineoffset],cx
\r
3085 lineoffset = lineoffset + SCREENWIDTH
\r
3087 add bx,32 ;the mask is now further away
\r
3089 shl [ss:planemask],1 ;shift plane mask over for next plane
\r
3090 cmp [ss:planemask],10000b ;done all four planes?
\r
3091 je @@drawn ;drawn all four planes
\r
3097 ;-------------------
\r
3101 mov cx,-1 ;definately scan the entire thing
\r