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
79 #define MAXSCROLLEDGES 6
\r
82 =============================================================================
\r
86 =============================================================================
\r
89 typedef struct spriteliststruct
\r
91 int screenx,screeny;
\r
94 unsigned grseg,sourceofs,planesize;
\r
96 unsigned tilex,tiley,tilewide,tilehigh;
\r
97 int priority,updatecount;
\r
98 struct spriteliststruct **prevptr,*nextsprite;
\r
104 int screenx,screeny;
\r
111 unsigned current; // foreground tiles have high bit set
\r
116 typedef struct animtilestruct
\r
120 unsigned far *mapplane;
\r
121 struct animtilestruct **prevptr,*nexttile;
\r
125 =============================================================================
\r
129 =============================================================================
\r
133 long lasttimecount;
\r
135 boolean compatability; // crippled refresh for wierdo SVGAs
\r
137 unsigned mapwidth,mapheight,mapbyteswide,mapwordswide
\r
138 ,mapbytesextra,mapwordsextra;
\r
139 unsigned mapbwidthtable[MAXMAPHEIGHT];
\r
142 // Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow
\r
143 // for fractional movement and acceleration.
\r
145 // Tiles : Tile offsets from the upper left corner of the current map.
\r
147 // Screen : Graphics level offsets from map origin, x in bytes, y in pixels.
\r
148 // originxscreen is the same spot as originxtile, just with extra precision
\r
149 // so graphics don't need to be done in tile boundaries.
\r
152 unsigned originxglobal,originyglobal;
\r
153 unsigned originxtile,originytile;
\r
154 unsigned originxscreen,originyscreen;
\r
155 unsigned originmap;
\r
156 unsigned originxmin,originxmax,originymin,originymax;
\r
157 unsigned originxtile,originytile;
\r
159 unsigned masterofs;
\r
162 // Table of the offsets from bufferofs of each tile spot in the
\r
163 // view port. The extra wide tile should never be drawn, but the space
\r
164 // is needed to account for the extra 0 in the update arrays. Built by
\r
168 unsigned blockstarts[UPDATEWIDE*UPDATEHIGH];
\r
169 unsigned updatemapofs[UPDATEWIDE*UPDATEHIGH];
\r
171 unsigned uwidthtable[PORTTILESHIGH]; // lookup instead of multiply
\r
173 byte update[2][UPDATESIZE];
\r
174 byte *updateptr,*baseupdateptr, // current start of update window
\r
176 *baseupdatestart[2];
\r
179 cardtype videocard; // set by VW_Startup
\r
180 grtype grmode; // CGAgr, EGAgr, VGAgr
\r
182 unsigned bufferofs; // hidden area to draw to before displaying
\r
183 unsigned displayofs; // origin of the visable screen
\r
187 =============================================================================
\r
191 =============================================================================
\r
194 static char scratch[20],str[20];
\r
197 tiletype allanims[MAXANIMTYPES];
\r
198 unsigned numanimchains;
\r
200 void (*refreshvector) (void);
\r
202 unsigned screenstart[3] =
\r
203 {0,SCREENSPACE,SCREENSPACE*2};
\r
205 unsigned xpanmask; // prevent panning to odd pixels
\r
207 unsigned screenpage; // screen currently being displayed
\r
208 unsigned otherpage;
\r
210 #if GRMODE == EGAGR
\r
211 unsigned tilecache[NUMTILE16];
\r
214 spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],
\r
217 animtiletype animarray[MAXANIMTILES],*animhead,*animfreeptr;
\r
221 eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];
\r
224 =============================================================================
\r
228 =============================================================================
\r
231 void RFL_NewTile (unsigned updateoffset);
\r
232 void RFL_MaskForegroundTiles (void);
\r
233 void RFL_UpdateTiles (void);
\r
235 void RFL_BoundScroll (int x, int y);//++++??
\r
236 void RFL_CalcOriginStuff (long x, long y);
\r
237 void RFL_ClearScrollBlocks (void);//++++??
\r
238 void RFL_InitSpriteList (void);
\r
239 void RFL_InitAnimList (void);
\r
240 void RFL_CheckForAnimTile (unsigned x, unsigned y);
\r
241 void RFL_AnimateTiles (void);
\r
242 void RFL_RemoveAnimsOnX (unsigned x);
\r
243 void RFL_RemoveAnimsOnY (unsigned y);
\r
244 void RFL_EraseBlocks (void);
\r
245 void RFL_UpdateSprites (void);
\r
249 =============================================================================
\r
251 GRMODE INDEPENDANT ROUTINES
\r
253 =============================================================================
\r
258 =====================
\r
262 =====================
\r
265 static char *ParmStrings[] = {"comp",""};
\r
267 void RF_Startup (void)
\r
270 unsigned *blockstart;
\r
272 if (grmode == EGAGR)
\r
273 for (i = 1;i < _argc;i++)
\r
274 if (US_CheckParm(_argv[i],ParmStrings) == 0)
\r
276 compatability = true;
\r
280 for (i=0;i<PORTTILESHIGH;i++)
\r
281 uwidthtable[i] = UPDATEWIDE*i;
\r
283 originxmin = originymin = MAPBORDER*TILEGLOBAL;
\r
285 eraselistptr[0] = &eraselist[0][0];
\r
286 eraselistptr[1] = &eraselist[1][0];
\r
290 if (grmode == EGAGR)
\r
294 baseupdatestart[0] = &update[0][UPDATESPARESIZE];
\r
295 baseupdatestart[1] = &update[1][UPDATESPARESIZE];
\r
299 displayofs = screenstart[screenpage];
\r
300 bufferofs = screenstart[otherpage];
\r
301 masterofs = screenstart[2];
\r
303 updateptr = baseupdatestart[otherpage];
\r
305 blockstart = &blockstarts[0];
\r
306 for (y=0;y<UPDATEHIGH;y++)
\r
307 for (x=0;x<UPDATEWIDE;x++)
\r
308 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
310 xpanmask = 6; // dont pan to odd pixels
\r
313 else if (grmode == CGAGR)
\r
317 updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];
\r
320 masterofs = 0x8000;
\r
322 blockstart = &blockstarts[0];
\r
323 for (y=0;y<UPDATEHIGH;y++)
\r
324 for (x=0;x<UPDATEWIDE;x++)
\r
325 *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;
\r
333 =====================
\r
337 =====================
\r
340 void RF_Shutdown (void)
\r
345 //===========================================================================
\r
349 =====================
\r
353 = Sets bufferofs,displayofs, and masterofs to regular values, for the
\r
354 = occasions when you have moved them around manually
\r
356 =====================
\r
359 void RF_FixOfs (void)
\r
361 if (grmode == EGAGR)
\r
365 panx = pany = pansx = pansy = panadjust = 0;
\r
366 displayofs = screenstart[screenpage];
\r
367 bufferofs = screenstart[otherpage];
\r
368 masterofs = screenstart[2];
\r
369 VW_SetScreen (displayofs,0);
\r
374 masterofs = 0x8000;
\r
379 //===========================================================================
\r
382 =====================
\r
386 = Makes some convienient calculations based on maphead->
\r
388 =====================
\r
391 void RF_NewMap (void)
\r
394 unsigned spot,*table;
\r
396 mapwidth = mapheaderseg[mapon]->width;
\r
397 mapbyteswide = 2*mapwidth;
\r
398 mapheight = mapheaderseg[mapon]->height;
\r
399 mapwordsextra = mapwidth-PORTTILESWIDE;
\r
400 mapbytesextra = 2*mapwordsextra;
\r
403 // make a lookup table for the maps left edge
\r
405 if (mapheight > MAXMAPHEIGHT)
\r
406 Quit ("RF_NewMap: Map too tall!");
\r
408 for (i=0;i<mapheight;i++)
\r
410 mapbwidthtable[i] = spot;
\r
411 spot += mapbyteswide;
\r
415 // fill in updatemapofs with the new width info
\r
417 table = &updatemapofs[0];
\r
418 for (y=0;y<PORTTILESHIGH;y++)
\r
419 for (x=0;x<UPDATEWIDE;x++)
\r
420 *table++ = mapbwidthtable[y]+x*2;
\r
423 // the y max value clips off the bottom half of a tile so a map that is
\r
424 // 13 + MAPBORDER*2 tile high will not scroll at all vertically
\r
426 originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;
\r
427 originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;
\r
428 if (originxmax<originxmin) // for very small maps
\r
429 originxmax=originxmin;
\r
430 if (originymax<originymin)
\r
431 originymax=originymin;
\r
434 // clear out the lists
\r
436 RFL_InitSpriteList ();
\r
437 RFL_InitAnimList ();
\r
438 RFL_ClearScrollBlocks ();
\r
439 RF_SetScrollBlock (0,MAPBORDER-1,true);
\r
440 RF_SetScrollBlock (0,mapheight-MAPBORDER,true);
\r
441 RF_SetScrollBlock (MAPBORDER-1,0,false);
\r
442 RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);
\r
445 lasttimecount = TimeCount; // setup for adaptive timing
\r
449 //===========================================================================
\r
452 ==========================
\r
454 = RF_MarkTileGraphics
\r
456 = Goes through mapplane[0/1] and marks all background/foreground tiles
\r
457 = needed, then follows all animation sequences to make sure animated
\r
458 = tiles get all the stages. Every unique animating tile is given an
\r
459 = entry in allanims[], so every instance of that tile will animate at the
\r
460 = same rate. The info plane for each animating tile will hold a pointer
\r
461 = into allanims[], therefore you can't have both an animating foreground
\r
462 = and background tile in the same spot!
\r
464 ==========================
\r
467 void RF_MarkTileGraphics (void)
\r
470 int tile,next,anims;
\r
471 unsigned far *start,far *end,far *info;
\r
472 unsigned i,tilehigh;
\r
474 memset (allanims,0,sizeof(allanims));
\r
477 size = mapwidth*mapheight;
\r
480 // background plane
\r
482 start = mapsegs[0];
\r
488 if (tile>=0) // <0 is a tile that is never drawn
\r
490 CA_MarkGrChunk(STARTTILE16+tile);
\r
491 if (tinf[ANIM+tile])
\r
493 // this tile will animated
\r
495 for (i=0;i<numanimchains;i++)
\r
496 if (allanims[i].current == tile)
\r
498 *info = (unsigned)&allanims[i];
\r
502 // new chain of animating tiles
\r
504 if (i>=MAXANIMTYPES)
\r
505 Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");
\r
506 allanims[i].current = tile;
\r
507 allanims[i].count = tinf[SPEED+tile];
\r
509 *info = (unsigned)&allanims[i];
\r
513 next = tile+(signed char)(tinf[ANIM+tile]);
\r
514 while (next != tile)
\r
516 CA_MarkGrChunk(STARTTILE16+next);
\r
517 next += (signed char)(tinf[ANIM+next]);
\r
519 Quit ("MarkTileGraphics: Unending animation!");
\r
526 } while (start<end);
\r
529 // foreground plane
\r
531 start = mapsegs[1];
\r
537 if (tile>=0) // <0 is a tile that is never drawn
\r
539 CA_MarkGrChunk(STARTTILE16M+tile);
\r
540 if (tinf[MANIM+tile])
\r
542 // this tile will animated
\r
544 tilehigh = tile | 0x8000; // foreground tiles have high bit
\r
545 for (i=0;i<numanimchains;i++)
\r
546 if (allanims[i].current == tilehigh)
\r
548 *info = (unsigned)&allanims[i];
\r
552 // new chain of animating tiles
\r
554 if (i>=MAXANIMTYPES)
\r
555 Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");
\r
556 allanims[i].current = tilehigh;
\r
557 allanims[i].count = tinf[MSPEED+tile];
\r
559 *info = (unsigned)&allanims[i];
\r
563 next = tile+(signed char)(tinf[MANIM+tile]);
\r
564 while (next != tile)
\r
566 CA_MarkGrChunk(STARTTILE16M+next);
\r
567 next += (signed char)(tinf[MANIM+next]);
\r
569 Quit ("MarkTileGraphics: Unending animation!");
\r
576 } while (start<end);
\r
580 //===========================================================================
\r
584 =========================
\r
588 = Call to clear out the entire animating tile list and return all of them to
\r
591 =========================
\r
594 void RFL_InitAnimList (void)
\r
598 animfreeptr = &animarray[0];
\r
600 for (i=0;i<MAXANIMTILES-1;i++)
\r
601 animarray[i].nexttile = &animarray[i+1];
\r
603 animarray[i].nexttile = NULL;
\r
605 animhead = NULL; // nothing in list
\r
610 ====================
\r
612 = RFL_CheckForAnimTile
\r
614 ====================
\r
617 void RFL_CheckForAnimTile (unsigned x, unsigned y)
\r
619 unsigned tile,offset,speed,lasttime,thistime,timemissed;
\r
621 animtiletype *anim,*next;
\r
623 // the info plane of each animating tile has a near pointer into allanims[]
\r
624 // which gives the current state of all concurrently animating tiles
\r
626 offset = mapbwidthtable[y]/2+x;
\r
631 map = mapsegs[0]+offset;
\r
633 if (tinf[ANIM+tile] && tinf[SPEED+tile])
\r
636 Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
\r
637 anim = animfreeptr;
\r
638 animfreeptr = animfreeptr->nexttile;
\r
639 next = animhead; // stick it at the start of the list
\r
642 next->prevptr = &anim->nexttile;
\r
643 anim->nexttile = next;
\r
644 anim->prevptr = &animhead;
\r
649 anim->mapplane = map;
\r
650 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
656 map = mapsegs[1]+offset;
\r
658 if (tinf[MANIM+tile] && tinf[MSPEED+tile])
\r
661 Quit ("RF_CheckForAnimTile: No free spots in tilearray!");
\r
662 anim = animfreeptr;
\r
663 animfreeptr = animfreeptr->nexttile;
\r
664 next = animhead; // stick it at the start of the list
\r
667 next->prevptr = &anim->nexttile;
\r
668 anim->nexttile = next;
\r
669 anim->prevptr = &animhead;
\r
674 anim->mapplane = map;
\r
675 anim->chain = (tiletype *)*(mapsegs[2]+offset);
\r
682 ====================
\r
684 = RFL_RemoveAnimsOnX
\r
686 ====================
\r
689 void RFL_RemoveAnimsOnX (unsigned x)
\r
691 animtiletype *current,*next;
\r
693 current = animhead;
\r
696 if (current->x == x)
\r
698 *(void **)current->prevptr = current->nexttile;
\r
699 if (current->nexttile)
\r
700 current->nexttile->prevptr = current->prevptr;
\r
701 next = current->nexttile;
\r
702 current->nexttile = animfreeptr;
\r
703 animfreeptr = current;
\r
707 current = current->nexttile;
\r
713 ====================
\r
715 = RFL_RemoveAnimsOnY
\r
717 ====================
\r
720 void RFL_RemoveAnimsOnY (unsigned y)
\r
722 animtiletype *current,*next;
\r
724 current = animhead;
\r
727 if (current->y == y)
\r
729 *(void **)current->prevptr = current->nexttile;
\r
730 if (current->nexttile)
\r
731 current->nexttile->prevptr = current->prevptr;
\r
732 next = current->nexttile;
\r
733 current->nexttile = animfreeptr;
\r
734 animfreeptr = current;
\r
738 current = current->nexttile;
\r
744 ====================
\r
746 = RFL_RemoveAnimsInBlock
\r
748 ====================
\r
751 void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)
\r
753 animtiletype *current,*next;
\r
755 current = animhead;
\r
758 if (current->x - x < width && current->y - y < height)
\r
760 *(void **)current->prevptr = current->nexttile;
\r
761 if (current->nexttile)
\r
762 current->nexttile->prevptr = current->prevptr;
\r
763 next = current->nexttile;
\r
764 current->nexttile = animfreeptr;
\r
765 animfreeptr = current;
\r
769 current = current->nexttile;
\r
775 ====================
\r
779 ====================
\r
782 void RFL_AnimateTiles (void)
\r
784 animtiletype *current;
\r
785 unsigned updateofs,tile,x,y;
\r
789 // animate the lists of tiles
\r
791 anim = &allanims[0];
\r
792 while (anim->current)
\r
795 while ( anim->count < 1)
\r
797 if (anim->current & 0x8000)
\r
799 tile = anim->current & 0x7fff;
\r
800 tile += (signed char)tinf[MANIM+tile];
\r
801 anim->count += tinf[MSPEED+tile];
\r
806 tile = anim->current;
\r
807 tile += (signed char)tinf[ANIM+tile];
\r
808 anim->count += tinf[SPEED+tile];
\r
810 anim->current = tile;
\r
817 // traverse the list of animating tiles
\r
819 current = animhead;
\r
822 tile =current->chain->current;
\r
823 if ( tile != current->tile)
\r
825 // tile has animated
\r
827 // remove tile from master screen cache,
\r
828 // change a tile to its next state, set the structure up for
\r
829 // next animation, and post an update region to both update pages
\r
831 current->tile = tile;
\r
833 *(current->mapplane) = tile & 0x7fff; // change in map
\r
835 #if GRMODE == EGAGR
\r
836 if (tile<0x8000) // background
\r
837 tilecache[tile] = 0;
\r
840 x = current->x-originxtile;
\r
841 y = current->y-originytile;
\r
843 if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)
\r
844 Quit ("RFL_AnimateTiles: Out of bounds!");
\r
846 updateofs = uwidthtable[y] + x;
\r
847 RFL_NewTile(updateofs); // puts "1"s in both pages
\r
849 current = current->nexttile;
\r
854 //===========================================================================
\r
857 =========================
\r
859 = RFL_InitSpriteList
\r
861 = Call to clear out the entire sprite list and return all of them to
\r
864 =========================
\r
867 void RFL_InitSpriteList (void)
\r
871 spritefreeptr = &spritearray[0];
\r
872 for (i=0;i<MAXSPRITES-1;i++)
\r
873 spritearray[i].nextsprite = &spritearray[i+1];
\r
875 spritearray[i].nextsprite = NULL;
\r
877 // NULL in all priority levels
\r
879 memset (prioritystart,0,sizeof(prioritystart));
\r
882 //===========================================================================
\r
887 = RFL_CalcOriginStuff
\r
889 = Calculate all the global variables for a new position
\r
890 = Long parms so position can be clipped to a maximum near 64k
\r
895 void RFL_CalcOriginStuff (long x, long y)
\r
899 else if (x>originxmax)
\r
904 else if (y>originymax)
\r
909 originxtile = originxglobal>>G_T_SHIFT;
\r
910 originytile = originyglobal>>G_T_SHIFT;
\r
911 originxscreen = originxtile<<SX_T_SHIFT;
\r
912 originyscreen = originytile<<SY_T_SHIFT;
\r
913 originmap = mapbwidthtable[originytile] + originxtile*2;
\r
915 #if GRMODE == EGAGR
\r
916 panx = (originxglobal>>G_P_SHIFT) & 15;
\r
918 pany = pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
919 panadjust = panx/8 + ylookup[pany];
\r
922 #if GRMODE == CGAGR
\r
923 panx = (originxglobal>>G_P_SHIFT) & 15;
\r
925 pany = pansy = (originyglobal>>G_P_SHIFT) & 15;
\r
926 panadjust = pansx/4 + ylookup[pansy];
\r
935 = RFL_ClearScrollBlocks
\r
940 void RFL_ClearScrollBlocks (void)
\r
942 hscrollblocks = vscrollblocks = 0;
\r
949 = RF_SetScrollBlock
\r
951 = Sets a horizontal or vertical scroll block
\r
952 = a horizontal block is ----, meaning it blocks up/down movement
\r
957 void RF_SetScrollBlock (int x, int y, boolean horizontal)
\r
961 hscrolledge[hscrollblocks] = y;
\r
962 if (hscrollblocks++ == MAXSCROLLEDGES)
\r
963 Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks");
\r
967 vscrolledge[vscrollblocks] = x;
\r
968 if (vscrollblocks++ == MAXSCROLLEDGES)
\r
969 Quit ("RF_SetScrollBlock: Too many vertical scroll blocks");
\r
979 = Bound a given x/y movement to scroll blocks
\r
984 void RFL_BoundScroll (int x, int y)
\r
986 int check,newxtile,newytile;
\r
988 originxglobal += x;
\r
989 originyglobal += y;
\r
991 newxtile= originxglobal >> G_T_SHIFT;
\r
992 newytile = originyglobal >> G_T_SHIFT;
\r
996 newxtile+=SCREENTILESWIDE;
\r
997 for (check=0;check<vscrollblocks;check++)
\r
998 if (vscrolledge[check] == newxtile)
\r
1000 originxglobal = originxglobal&0xff00;
\r
1006 for (check=0;check<vscrollblocks;check++)
\r
1007 if (vscrolledge[check] == newxtile)
\r
1009 originxglobal = (originxglobal&0xff00)+0x100;
\r
1017 newytile+=SCREENTILESHIGH;
\r
1018 for (check=0;check<hscrollblocks;check++)
\r
1019 if (hscrolledge[check] == newytile)
\r
1021 originyglobal = originyglobal&0xff00;
\r
1027 for (check=0;check<hscrollblocks;check++)
\r
1028 if (hscrolledge[check] == newytile)
\r
1030 originyglobal = (originyglobal&0xff00)+0x100;
\r
1036 RFL_CalcOriginStuff (originxglobal, originyglobal);
\r
1040 //===========================================================================
\r
1043 =====================
\r
1045 = RF_SetRefreshHook
\r
1047 =====================
\r
1050 void RF_SetRefreshHook (void (*func) (void) )
\r
1052 refreshvector = func;
\r
1056 //===========================================================================
\r
1063 = Bring a new row of tiles onto the port, spawning animating tiles
\r
1068 void RFL_NewRow (int dir)
\r
1070 unsigned count,updatespot,updatestep;
\r
1071 int x,y,xstep,ystep;
\r
1075 case 0: // top row
\r
1082 count = PORTTILESWIDE;
\r
1085 case 1: // right row
\r
1086 updatespot = PORTTILESWIDE-1;
\r
1087 updatestep = UPDATEWIDE;
\r
1088 x = originxtile + PORTTILESWIDE-1;
\r
1092 count = PORTTILESHIGH;
\r
1095 case 2: // bottom row
\r
1096 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1099 y = originytile + PORTTILESHIGH-1;
\r
1102 count = PORTTILESWIDE;
\r
1105 case 3: // left row
\r
1107 updatestep = UPDATEWIDE;
\r
1112 count = PORTTILESHIGH;
\r
1115 Quit ("RFL_NewRow: Bad dir!");
\r
1120 RFL_NewTile(updatespot);
\r
1121 RFL_CheckForAnimTile (x,y);
\r
1122 updatespot+=updatestep;
\r
1128 //===========================================================================
\r
1131 =====================
\r
1135 =====================
\r
1138 void RF_ForceRefresh (void)
\r
1140 RF_NewPosition (originxglobal,originyglobal);
\r
1145 //===========================================================================
\r
1148 =====================
\r
1152 = Copies a block of tiles (all three planes) from one point
\r
1153 = in the map to another, accounting for animating tiles
\r
1155 =====================
\r
1158 void RF_MapToMap (unsigned srcx, unsigned srcy,
\r
1159 unsigned destx, unsigned desty,
\r
1160 unsigned width, unsigned height)
\r
1163 unsigned source,destofs,xspot,yspot;
\r
1164 unsigned linedelta,p0,p1,p2,updatespot;
\r
1165 unsigned far *source0, far *source1, far *source2;
\r
1166 unsigned far *dest0, far *dest1, far *dest2;
\r
1169 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1171 source = mapbwidthtable[srcy]/2 + srcx;
\r
1173 source0 = mapsegs[0]+source;
\r
1174 source1 = mapsegs[1]+source;
\r
1175 source2 = mapsegs[2]+source;
\r
1177 destofs = mapbwidthtable[desty]/2 + destx;
\r
1178 destofs -= source;
\r
1180 linedelta = mapwidth - width;
\r
1182 for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)
\r
1183 for (x=0;x<width;x++,source0++,source1++,source2++)
\r
1189 dest0 = source0 + destofs;
\r
1190 dest1 = source1 + destofs;
\r
1191 dest2 = source2 + destofs;
\r
1194 // only make a new tile if it is different
\r
1196 if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)
\r
1207 // if tile is on the view port
\r
1209 xspot = destx+x-originxtile;
\r
1210 yspot = desty+y-originytile;
\r
1211 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1215 updatespot = uwidthtable[yspot]+xspot;
\r
1216 RFL_NewTile(updatespot);
\r
1218 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1223 //===========================================================================
\r
1227 =====================
\r
1231 = Copies a string of tiles from main memory to the map,
\r
1232 = accounting for animating tiles
\r
1234 =====================
\r
1237 void RF_MemToMap (unsigned far *source, unsigned plane,
\r
1238 unsigned destx, unsigned desty,
\r
1239 unsigned width, unsigned height)
\r
1242 unsigned xspot,yspot;
\r
1243 unsigned linedelta,updatespot;
\r
1244 unsigned far *dest,old,new;
\r
1247 RFL_RemoveAnimsInBlock (destx,desty,width,height);
\r
1249 dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;
\r
1251 linedelta = mapwidth - width;
\r
1253 for (y=0;y<height;y++,dest+=linedelta)
\r
1254 for (x=0;x<width;x++)
\r
1267 xspot = destx+x-originxtile;
\r
1268 yspot = desty+y-originytile;
\r
1269 if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)
\r
1273 updatespot = uwidthtable[yspot]+xspot;
\r
1274 RFL_NewTile(updatespot);
\r
1276 RFL_CheckForAnimTile (destx+x,desty+y);
\r
1281 //===========================================================================
\r
1285 =====================
\r
1287 = RFL_BoundNewOrigin
\r
1289 = Copies a string of tiles from main memory to the map,
\r
1290 = accounting for animating tiles
\r
1292 =====================
\r
1295 void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)
\r
1300 // calculate new origin related globals
\r
1302 if (orgx<originxmin)
\r
1304 else if (orgx>originxmax)
\r
1307 if (orgy<originymin)
\r
1309 else if (orgy>originymax)
\r
1312 originxtile = orgx>>G_T_SHIFT;
\r
1313 originytile = orgy>>G_T_SHIFT;
\r
1315 for (check=0;check<vscrollblocks;check++)
\r
1317 edge = vscrolledge[check];
\r
1318 if (edge>=originxtile && edge <=originxtile+10)
\r
1320 orgx = (edge+1)*TILEGLOBAL;
\r
1323 if (edge>=originxtile+11 && edge <=originxtile+20)
\r
1325 orgx = (edge-20)*TILEGLOBAL;
\r
1330 for (check=0;check<hscrollblocks;check++)
\r
1332 edge = hscrolledge[check];
\r
1333 if (edge>=originytile && edge <=originytile+6)
\r
1335 orgy = (edge+1)*TILEGLOBAL;
\r
1338 if (edge>=originytile+7 && edge <=originytile+13)
\r
1340 orgy = (edge-13)*TILEGLOBAL;
\r
1346 RFL_CalcOriginStuff (orgx,orgy);
\r
1350 //===========================================================================
\r
1353 =====================
\r
1357 = Posts erase blocks to clear a certain area of the screen to the master
\r
1358 = screen, to erase text or something draw directly to the screen
\r
1360 = Parameters in pixels, but erasure is byte bounded
\r
1362 =====================
\r
1365 void RF_ClearBlock (int x, int y, int width, int height)
\r
1367 eraseblocktype block;
\r
1369 #if GRMODE == EGAGR
\r
1370 block.screenx = x/8+originxscreen;
\r
1371 block.screeny = y+originyscreen;
\r
1372 block.width = (width+(x&7)+7)/8;
\r
1373 block.height = height;
\r
1374 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1375 memcpy (eraselistptr[1]++,&block,sizeof(block));
\r
1378 #if GRMODE == CGAGR
\r
1379 block.screenx = x/4+originxscreen;
\r
1380 block.screeny = y+originyscreen;
\r
1381 block.width = (width+(x&3)+3)/4;
\r
1382 block.height = height;
\r
1383 memcpy (eraselistptr[0]++,&block,sizeof(block));
\r
1388 //===========================================================================
\r
1391 =====================
\r
1395 = Causes a number of tiles to be redrawn to the master screen and updated
\r
1397 = Parameters in pixels, but erasure is tile bounded
\r
1399 =====================
\r
1402 void RF_RedrawBlock (int x, int y, int width, int height)
\r
1404 int xx,yy,xl,xh,yl,yh;
\r
1407 xh=(x+panx+width+15)/16;
\r
1409 yh=(y+pany+height+15)/16;
\r
1410 for (yy=yl;yy<=yh;yy++)
\r
1411 for (xx=xl;xx<=xh;xx++)
\r
1412 RFL_NewTile (yy*UPDATEWIDE+xx);
\r
1416 //===========================================================================
\r
1419 =====================
\r
1423 =====================
\r
1426 void RF_CalcTics (void)
\r
1428 long newtime,oldtimecount;
\r
1431 // calculate tics since last refresh for adaptive timing
\r
1433 if (lasttimecount > TimeCount)
\r
1434 TimeCount = lasttimecount; // if the game was paused a LONG time
\r
1436 if (DemoMode) // demo recording and playback needs
\r
1437 { // to be constant
\r
1439 // take DEMOTICS or more tics, and modify Timecount to reflect time taken
\r
1441 oldtimecount = lasttimecount;
\r
1442 while (TimeCount<oldtimecount+DEMOTICS*2)
\r
1444 lasttimecount = oldtimecount + DEMOTICS;
\r
1445 TimeCount = lasttimecount + DEMOTICS;
\r
1451 // non demo, so report actual time
\r
1455 newtime = TimeCount;
\r
1456 tics = newtime-lasttimecount;
\r
1457 } while (tics<MINTICS);
\r
1458 lasttimecount = newtime;
\r
1461 strcpy (scratch,"\tTics:");
\r
1462 itoa (tics,str,10);
\r
1463 strcat (scratch,str);
\r
1464 strcat (scratch,"\n");
\r
1465 write (profilehandle,scratch,strlen(scratch));
\r
1470 TimeCount -= (tics-MAXTICS);
\r
1477 =============================================================================
\r
1479 EGA specific routines
\r
1481 =============================================================================
\r
1484 #if GRMODE == EGAGR
\r
1487 =====================
\r
1489 = RF_FindFreeBuffer
\r
1491 = Finds the start of unused, non visable buffer space
\r
1493 =====================
\r
1496 unsigned RF_FindFreeBuffer (void)
\r
1498 unsigned spot,i,j;
\r
1503 spot = screenstart[i]+SCREENSPACE;
\r
1506 if (spot == screenstart[j])
\r
1515 return 0; // never get here...
\r
1518 //===========================================================================
\r
1521 =====================
\r
1523 = RF_NewPosition EGA
\r
1525 =====================
\r
1528 void RF_NewPosition (unsigned x, unsigned y)
\r
1531 byte *page0ptr,*page1ptr;
\r
1532 unsigned updatenum;
\r
1534 RFL_BoundNewOrigin (x,y);
\r
1536 // calculate new origin related globals
\r
1538 RFL_CalcOriginStuff (x,y);*/
\r
1541 // clear out all animating tiles
\r
1543 RFL_InitAnimList ();
\r
1546 // set up the new update arrays at base position
\r
1548 //?? memset (tilecache,0,sizeof(tilecache)); // old cache is invalid
\r
1550 updatestart[0] = baseupdatestart[0];
\r
1551 updatestart[1] = baseupdatestart[1];
\r
1552 updateptr = updatestart[otherpage];
\r
1554 page0ptr = updatestart[0]+PORTTILESWIDE; // used to stick "0"s after rows
\r
1555 page1ptr = updatestart[1]+PORTTILESWIDE;
\r
1557 updatenum = 0; // start at first visable tile
\r
1559 for (my=0;my<PORTTILESHIGH;my++)
\r
1561 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
1563 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
1564 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
1568 *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles
\r
1569 page0ptr+=(PORTTILESWIDE+1);
\r
1570 page1ptr+=(PORTTILESWIDE+1);
\r
1572 *(word *)(page0ptr-PORTTILESWIDE)
\r
1573 = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
1576 //===========================================================================
\r
1583 = Uncache the trailing row of tiles
\r
1588 void RFL_OldRow (unsigned updatespot,unsigned count,unsigned step)
\r
1591 asm mov si,[updatespot] // pointer inside each map plane
\r
1592 asm mov cx,[count] // number of tiles to clear
\r
1593 asm mov dx,[step] // move to next tile
\r
1594 asm mov es,[WORD PTR mapsegs] // background plane
\r
1595 asm mov ds,[WORD PTR mapsegs+2] // foreground plane
\r
1600 asm jnz blockok // if a foreground tile, block wasn't cached
\r
1601 asm mov bx,[es:si]
\r
1603 asm mov [WORD PTR ss:tilecache+bx],0 //tile is no longer in master screen cache
\r
1606 asm loop clearcache
\r
1615 =====================
\r
1619 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
1620 = scroll if needed. If the scroll distance is greater than one tile, the
\r
1621 = entire screen will be redrawn (this could be generalized, but scrolling
\r
1622 = more than one tile per refresh is a bad idea!).
\r
1624 =====================
\r
1627 void RF_Scroll (int x, int y)
\r
1629 long neworgx,neworgy;
\r
1630 int i,deltax,deltay,absdx,absdy;
\r
1631 int oldxt,oldyt,move,yy;
\r
1632 unsigned updatespot;
\r
1633 byte *update0,*update1;
\r
1634 unsigned oldpanx,oldpanadjust,oldoriginmap,oldscreen,newscreen,screencopy;
\r
1637 oldxt = originxtile;
\r
1638 oldyt = originytile;
\r
1639 oldoriginmap = originmap;
\r
1640 oldpanadjust = panadjust;
\r
1643 RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);
\r
1645 deltax = originxtile - oldxt;
\r
1646 absdx = abs(deltax);
\r
1647 deltay = originytile - oldyt;
\r
1648 absdy = abs(deltay);
\r
1650 if (absdx>1 || absdy>1)
\r
1653 // scrolled more than one tile, so start from scratch
\r
1655 RF_NewPosition(originxglobal,originyglobal);
\r
1659 if (!absdx && !absdy)
\r
1660 return; // the screen has not scrolled an entire tile
\r
1664 // adjust screens and handle SVGA crippled compatability mode
\r
1666 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
1669 screenstart[i]+= screenmove;
\r
1670 if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )
\r
1673 // move the screen to the opposite end of the buffer
\r
1675 screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;
\r
1676 oldscreen = screenstart[i] - screenmove;
\r
1677 newscreen = oldscreen + screencopy;
\r
1678 screenstart[i] = newscreen + screenmove;
\r
1679 VW_ScreenToScreen (oldscreen,newscreen,
\r
1680 PORTTILESWIDE*2,PORTTILESHIGH*16);
\r
1682 if (i==screenpage)
\r
1683 VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);
\r
1686 bufferofs = screenstart[otherpage];
\r
1687 displayofs = screenstart[screenpage];
\r
1688 masterofs = screenstart[2];
\r
1692 // float the update regions
\r
1696 move += UPDATEWIDE;
\r
1697 else if (deltay==-1)
\r
1698 move -= UPDATEWIDE;
\r
1700 updatestart[0]+=move;
\r
1701 updatestart[1]+=move;
\r
1704 // draw the new tiles just scrolled on to the master screen, and
\r
1705 // mark them as needing to be copied to each screen next refreshes
\r
1706 // Make sure a zero is at the end of each row in update
\r
1713 RFL_NewRow (1); // new right row
\r
1714 RFL_OldRow (oldoriginmap,PORTTILESHIGH,mapbyteswide);
\r
1715 RFL_RemoveAnimsOnX (originxtile-1);
\r
1719 RFL_NewRow (3); // new left row
\r
1720 RFL_OldRow (oldoriginmap+(PORTTILESWIDE-1)*2,PORTTILESHIGH
\r
1722 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
1725 update0 = updatestart[0]+PORTTILESWIDE;
\r
1726 update1 = updatestart[1]+PORTTILESWIDE;
\r
1727 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
1729 *update0 = *update1 = 0; // drop a 0 at end of each row
\r
1730 update0+=UPDATEWIDE;
\r
1731 update1+=UPDATEWIDE;
\r
1735 //----------------
\r
1741 RFL_NewRow (2); // new bottom row
\r
1742 RFL_OldRow (oldoriginmap,PORTTILESWIDE,2);
\r
1743 updatespot = UPDATEWIDE*(PORTTILESHIGH-1);
\r
1744 RFL_RemoveAnimsOnY (originytile-1);
\r
1748 RFL_NewRow (0); // new top row
\r
1749 RFL_OldRow (oldoriginmap+mapbwidthtable[PORTTILESHIGH-1]
\r
1750 ,PORTTILESWIDE,2);
\r
1752 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
1755 *(updatestart[0]+updatespot+PORTTILESWIDE) =
\r
1756 *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;
\r
1759 //----------------
\r
1762 // place a new terminator
\r
1764 update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1765 update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;
\r
1766 *update0++ = *update1++ = 0;
\r
1767 *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;
\r
1770 //===========================================================================
\r
1773 =====================
\r
1775 = RF_PlaceSprite EGA
\r
1777 =====================
\r
1780 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
1781 unsigned spritenumber, drawtype draw, int priority)
\r
1783 spritelisttype register *sprite,*next;
\r
1784 spritetabletype far *spr;
\r
1785 spritetype _seg *block;
\r
1786 unsigned shift,pixx;
\r
1787 char str[80],str2[10];
\r
1789 if (!spritenumber || spritenumber == (unsigned)-1)
\r
1791 RF_RemoveSprite (user);
\r
1795 sprite = (spritelisttype *)*user;
\r
1799 // sprite allready exists in the list, so we can use it's block
\r
1802 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1803 // both pages may not need to be erased if the sprite just changed last frame
\r
1805 if (sprite->updatecount<2)
\r
1807 if (!sprite->updatecount)
\r
1808 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1809 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1812 if (priority != sprite->priority)
\r
1814 // sprite mvoed to another priority, so unlink the old one and
\r
1815 // relink it in the new priority
\r
1817 next = sprite->nextsprite; // cut old links
\r
1819 next->prevptr = sprite->prevptr;
\r
1820 *sprite->prevptr = next;
\r
1826 // this is a brand new sprite, so allocate a block from the array
\r
1828 if (!spritefreeptr)
\r
1829 Quit ("RF_PlaceSprite: No free spots in spritearray!");
\r
1831 sprite = spritefreeptr;
\r
1832 spritefreeptr = spritefreeptr->nextsprite;
\r
1835 next = prioritystart[priority]; // stick it in new spot
\r
1837 next->prevptr = &sprite->nextsprite;
\r
1838 sprite->nextsprite = next;
\r
1839 prioritystart[priority] = sprite;
\r
1840 sprite->prevptr = &prioritystart[priority];
\r
1844 // write the new info to the sprite
\r
1846 spr = &spritetable[spritenumber-STARTSPRITES];
\r
1847 block = (spritetype _seg *)grsegs[spritenumber];
\r
1851 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");
\r
1852 itoa (spritenumber,str2,10);
\r
1853 strcat (str,str2);
\r
1857 globaly+=spr->orgy;
\r
1858 globalx+=spr->orgx;
\r
1860 pixx = globalx >> G_SY_SHIFT;
\r
1861 shift = (pixx&7)/2;
\r
1863 sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);
\r
1864 sprite->screeny = globaly >> G_SY_SHIFT;
\r
1865 sprite->width = block->width[shift];
\r
1866 sprite->height = spr->height;
\r
1867 sprite->grseg = spritenumber;
\r
1868 sprite->sourceofs = block->sourceoffset[shift];
\r
1869 sprite->planesize = block->planesize[shift];
\r
1870 sprite->draw = draw;
\r
1871 sprite->priority = priority;
\r
1872 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
1873 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
1874 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
1875 - sprite->tilex + 1;
\r
1876 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
1877 - sprite->tiley + 1;
\r
1879 sprite->updatecount = 2; // draw on next two refreshes
\r
1881 // save the sprite pointer off in the user's pointer so it can be moved
\r
1887 //===========================================================================
\r
1890 =====================
\r
1892 = RF_RemoveSprite EGA
\r
1894 =====================
\r
1897 void RF_RemoveSprite (void **user)
\r
1899 spritelisttype *sprite,*next;
\r
1901 sprite = (spritelisttype *)*user;
\r
1906 // post an erase block to both pages by copying screenx,screeny,width,height
\r
1907 // both pages may not need to be erased if the sprite just changed last frame
\r
1909 if (sprite->updatecount<2)
\r
1911 if (!sprite->updatecount)
\r
1912 memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));
\r
1913 memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));
\r
1917 // unlink the sprite node
\r
1919 next = sprite->nextsprite;
\r
1920 if (next) // if (!next), sprite is last in chain
\r
1921 next->prevptr = sprite->prevptr;
\r
1922 *sprite->prevptr = next;
\r
1925 // add it back to the free list
\r
1927 sprite->nextsprite = spritefreeptr;
\r
1928 spritefreeptr = sprite;
\r
1931 // null the users pointer, so next time that actor gets placed, it will
\r
1932 // allocate a new block
\r
1939 //===========================================================================
\r
1943 ====================
\r
1945 = RFL_EraseBlocks EGA
\r
1947 = Write mode 1 should be set
\r
1949 ====================
\r
1952 void RFL_EraseBlocks (void)
\r
1954 eraseblocktype *block,*done;
\r
1955 int screenxh,screenyh;
\r
1956 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
1958 unsigned updatedelta;
\r
1959 unsigned erasecount;
\r
1965 block = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
1967 done = eraselistptr[otherpage];
\r
1969 while (block != done)
\r
1973 // clip the block to the current screen view
\r
1975 block->screenx -= originxscreen;
\r
1976 block->screeny -= originyscreen;
\r
1978 if (block->screenx < 0)
\r
1980 block->width += block->screenx;
\r
1981 if (block->width<1)
\r
1983 block->screenx = 0;
\r
1986 if (block->screeny < 0)
\r
1988 block->height += block->screeny;
\r
1989 if (block->height<1)
\r
1991 block->screeny = 0;
\r
1994 screenxh = block->screenx + block->width;
\r
1995 screenyh = block->screeny + block->height;
\r
1997 if (screenxh > EGAPORTSCREENWIDE)
\r
1999 block->width = EGAPORTSCREENWIDE-block->screenx;
\r
2000 screenxh = block->screenx + block->width;
\r
2003 if (screenyh > PORTSCREENHIGH)
\r
2005 block->height = PORTSCREENHIGH-block->screeny;
\r
2006 screenyh = block->screeny + block->height;
\r
2009 if (block->width<1 || block->height<1)
\r
2013 // erase the block by copying from the master screen
\r
2015 pos = ylookup[block->screeny]+block->screenx;
\r
2016 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2017 block->width,block->height);
\r
2020 // put 2s in update where the block was, to force sprites to update
\r
2022 xtl = block->screenx >> SX_T_SHIFT;
\r
2023 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2024 ytl = block->screeny >> SY_T_SHIFT;
\r
2025 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2027 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2028 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2030 for (y=ytl;y<=yth;y++)
\r
2032 for (x=xtl;x<=xth;x++)
\r
2033 *updatespot++ = 2;
\r
2034 updatespot += updatedelta; // down to next line
\r
2043 eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];
\r
2045 strcpy (scratch,"\tErase:");
\r
2046 itoa (erasecount,str,10);
\r
2047 strcat (scratch,str);
\r
2048 write (profilehandle,scratch,strlen(scratch));
\r
2055 ====================
\r
2057 = RFL_UpdateSprites EGA
\r
2059 = NOTE: Implement vertical clipping!
\r
2061 ====================
\r
2064 void RFL_UpdateSprites (void)
\r
2066 spritelisttype *sprite;
\r
2067 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2070 byte *updatespot,*baseupdatespot;
\r
2071 unsigned updatedelta;
\r
2072 unsigned updatecount;
\r
2073 unsigned height,sourceofs;
\r
2079 for (priority=0;priority<PRIORITIES;priority++)
\r
2081 if (priority==MASKEDTILEPRIORITY)
\r
2082 RFL_MaskForegroundTiles ();
\r
2084 for (sprite = prioritystart[priority]; sprite ;
\r
2085 sprite = (spritelisttype *)sprite->nextsprite)
\r
2088 // see if the sprite has any visable area in the port
\r
2091 portx = sprite->screenx - originxscreen;
\r
2092 porty = sprite->screeny - originyscreen;
\r
2093 xtl = portx >> SX_T_SHIFT;
\r
2094 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2095 ytl = porty >> SY_T_SHIFT;
\r
2096 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2100 if (xth>=PORTTILESWIDE)
\r
2101 xth = PORTTILESWIDE-1;
\r
2104 if (yth>=PORTTILESHIGH)
\r
2105 yth = PORTTILESHIGH-1;
\r
2107 if (xtl>xth || ytl>yth)
\r
2111 // see if it's visable area covers any non 0 update tiles
\r
2113 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2114 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2116 if (sprite->updatecount)
\r
2118 sprite->updatecount--; // the sprite was just placed,
\r
2119 goto redraw; // so draw it for sure
\r
2122 for (y=ytl;y<=yth;y++)
\r
2124 for (x=xtl;x<=xth;x++)
\r
2125 if (*updatespot++)
\r
2127 updatespot += updatedelta; // down to next line
\r
2129 continue; // no need to update
\r
2133 // set the tiles it covers to 3, because those tiles are being
\r
2136 updatespot = baseupdatespot;
\r
2137 for (y=ytl;y<=yth;y++)
\r
2139 for (x=xtl;x<=xth;x++)
\r
2140 *updatespot++ = 3;
\r
2141 updatespot += updatedelta; // down to next line
\r
2146 height = sprite->height;
\r
2147 sourceofs = sprite->sourceofs;
\r
2150 height += porty; // clip top off
\r
2151 sourceofs -= porty*sprite->width;
\r
2154 else if (porty+height>PORTSCREENHIGH)
\r
2156 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2159 dest = bufferofs + ylookup[porty] + portx;
\r
2161 switch (sprite->draw)
\r
2164 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2165 dest,sprite->width,height,sprite->planesize);
\r
2180 strcpy (scratch,"\tSprites:");
\r
2181 itoa (updatecount,str,10);
\r
2182 strcat (scratch,str);
\r
2183 write (profilehandle,scratch,strlen(scratch));
\r
2190 =====================
\r
2194 = All routines will draw at the port at bufferofs, possibly copying from
\r
2195 = the port at masterofs. The EGA version then page flips, while the
\r
2196 = CGA version updates the screen from the buffer port.
\r
2198 = Screenpage is the currently displayed page, not the one being drawn
\r
2199 = Otherpage is the page to be worked with now
\r
2201 =====================
\r
2204 void RF_Refresh (void)
\r
2208 updateptr = updatestart[otherpage];
\r
2210 RFL_AnimateTiles (); // DEBUG
\r
2213 // update newly scrolled on tiles and animated tiles from the master screen
\r
2217 RFL_UpdateTiles ();
\r
2218 RFL_EraseBlocks ();
\r
2221 // Update is all 0 except where sprites have changed or new area has
\r
2222 // been scrolled on. Go through all sprites and update the ones that cover
\r
2223 // a non 0 update tile
\r
2226 RFL_UpdateSprites ();
\r
2229 // if the main program has a refresh hook set, call their function before
\r
2230 // displaying the new page
\r
2232 if (refreshvector)
\r
2236 // display the changed screen
\r
2238 VW_SetScreen(bufferofs+panadjust,panx & xpanmask);
\r
2241 // prepare for next refresh
\r
2243 // Set the update array to the middle position and clear it out to all "0"s
\r
2244 // with an UPDATETERMINATE at the end
\r
2246 updatestart[otherpage] = newupdate = baseupdatestart[otherpage];
\r
2250 asm mov cx,(UPDATESCREENSIZE-2)/2
\r
2251 asm mov di,[newupdate]
\r
2253 asm mov [WORD PTR es:di],UPDATETERMINATE
\r
2257 bufferofs = screenstart[otherpage];
\r
2258 displayofs = screenstart[screenpage];
\r
2261 // calculate tics since last refresh for adaptive timing
\r
2266 #endif // GRMODE == EGAGR
\r
2269 =============================================================================
\r
2271 CGA specific routines
\r
2273 =============================================================================
\r
2276 #if GRMODE == CGAGR
\r
2280 =====================
\r
2282 = RF_NewPosition CGA
\r
2284 =====================
\r
2287 void RF_NewPosition (unsigned x, unsigned y)
\r
2291 unsigned updatenum;
\r
2293 RFL_BoundNewOrigin (x,y);
\r
2295 // calculate new origin related globals
\r
2297 RFL_CalcOriginStuff (x,y);*/
\r
2300 // clear out all animating tiles
\r
2302 RFL_InitAnimList ();
\r
2305 // set up the new update arrays at base position
\r
2307 updateptr = baseupdateptr;
\r
2309 spotptr = updateptr + PORTTILESWIDE; // used to stick "0"s after rows
\r
2311 updatenum = 0; // start at first visable tile
\r
2313 for (my=0;my<PORTTILESHIGH;my++)
\r
2315 for (mx=0;mx<PORTTILESWIDE;mx++)
\r
2317 RFL_NewTile(updatenum); // puts "1"s in both pages
\r
2318 RFL_CheckForAnimTile(mx+originxtile,my+originytile);
\r
2322 *spotptr = 0; // set a 0 at end of a line of tiles
\r
2323 spotptr +=(PORTTILESWIDE+1);
\r
2325 *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;
\r
2330 =====================
\r
2334 = Move the origin x/y global coordinates, readjust the screen panning, and
\r
2335 = scroll if needed. If the scroll distance is greater than one tile, the
\r
2336 = entire screen will be redrawn (this could be generalized, but scrolling
\r
2337 = more than one tile per refresh is a bad idea!).
\r
2339 =====================
\r
2342 void RF_Scroll (int x, int y)
\r
2344 long neworgx,neworgy;
\r
2345 int i,deltax,deltay,absdx,absdy;
\r
2346 int oldxt,oldyt,move,yy;
\r
2347 unsigned updatespot;
\r
2349 unsigned oldoriginmap,oldscreen,newscreen,screencopy;
\r
2352 oldxt = originxtile;
\r
2353 oldyt = originytile;
\r
2355 RFL_CalcOriginStuff ((long)originxglobal + x,(long)originyglobal + y);
\r
2357 deltax = originxtile - oldxt;
\r
2358 absdx = abs(deltax);
\r
2359 deltay = originytile - oldyt;
\r
2360 absdy = abs(deltay);
\r
2362 if (absdx>1 || absdy>1)
\r
2365 // scrolled more than one tile, so start from scratch
\r
2367 RF_NewPosition(originxglobal,originyglobal);
\r
2371 if (!absdx && !absdy)
\r
2372 return; // the screen has not scrolled an entire tile
\r
2378 screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;
\r
2379 bufferofs += screenmove;
\r
2380 masterofs += screenmove;
\r
2384 // float the update regions
\r
2388 move += UPDATEWIDE;
\r
2389 else if (deltay==-1)
\r
2390 move -= UPDATEWIDE;
\r
2395 // draw the new tiles just scrolled on to the master screen, and
\r
2396 // mark them as needing to be copied to each screen next refreshes
\r
2397 // Make sure a zero is at the end of each row in update
\r
2404 RFL_NewRow (1); // new right row
\r
2405 RFL_RemoveAnimsOnX (originxtile-1);
\r
2409 RFL_NewRow (3); // new left row
\r
2410 RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);
\r
2413 spotptr = updateptr+PORTTILESWIDE;
\r
2414 for (yy=0;yy<PORTTILESHIGH;yy++)
\r
2416 *spotptr = 0; // drop a 0 at end of each row
\r
2417 spotptr+=UPDATEWIDE;
\r
2421 //----------------
\r
2427 RFL_NewRow (2); // new bottom row
\r
2428 *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;
\r
2429 RFL_RemoveAnimsOnY (originytile-1);
\r
2433 RFL_NewRow (0); // new top row
\r
2434 *(updateptr+PORTTILESWIDE) = 0;
\r
2435 RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);
\r
2439 //----------------
\r
2442 // place a new terminator
\r
2444 spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;
\r
2446 *(unsigned *)spotptr = UPDATETERMINATE;
\r
2450 =====================
\r
2452 = RF_PlaceSprite CGA
\r
2454 =====================
\r
2457 void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,
\r
2458 unsigned spritenumber, drawtype draw, int priority)
\r
2460 spritelisttype register *sprite,*next;
\r
2461 spritetabletype far *spr;
\r
2462 spritetype _seg *block;
\r
2463 unsigned shift,pixx;
\r
2464 char str[80],str2[10];
\r
2466 if (!spritenumber || spritenumber == (unsigned)-1)
\r
2468 RF_RemoveSprite (user);
\r
2472 sprite = (spritelisttype *)*user;
\r
2476 // sprite allready exists in the list, so we can use it's block
\r
2479 // post an erase block to erase the old position by copying
\r
2480 // screenx,screeny,width,height
\r
2482 if (!sprite->updatecount) // may not have been drawn at all yet
\r
2483 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2485 if (priority != sprite->priority)
\r
2487 // sprite moved to another priority, so unlink the old one and
\r
2488 // relink it in the new priority
\r
2490 next = sprite->nextsprite; // cut old links
\r
2492 next->prevptr = sprite->prevptr;
\r
2493 *sprite->prevptr = next;
\r
2499 // this is a brand new sprite, so allocate a block from the array
\r
2501 if (!spritefreeptr)
\r
2502 Quit ("RF_PlaceSprite: No free spots in spritearray!");
\r
2504 sprite = spritefreeptr;
\r
2505 spritefreeptr = spritefreeptr->nextsprite;
\r
2508 next = prioritystart[priority]; // stick it in new spot
\r
2510 next->prevptr = &sprite->nextsprite;
\r
2511 sprite->nextsprite = next;
\r
2512 prioritystart[priority] = sprite;
\r
2513 sprite->prevptr = &prioritystart[priority];
\r
2517 // write the new info to the sprite
\r
2519 spr = &spritetable[spritenumber-STARTSPRITES];
\r
2520 block = (spritetype _seg *)grsegs[spritenumber];
\r
2524 strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");
\r
2525 itoa (spritenumber,str2,10);
\r
2526 strcat (str,str2);
\r
2531 globaly+=spr->orgy;
\r
2532 globalx+=spr->orgx;
\r
2534 sprite->screenx = globalx >> G_CGASX_SHIFT;
\r
2535 sprite->screeny = globaly >> G_SY_SHIFT;
\r
2536 sprite->width = block->width[0];
\r
2537 sprite->height = spr->height;
\r
2538 sprite->grseg = spritenumber;
\r
2539 sprite->sourceofs = block->sourceoffset[0];
\r
2540 sprite->planesize = block->planesize[0];
\r
2541 sprite->draw = draw;
\r
2542 sprite->priority = priority;
\r
2543 sprite->tilex = sprite->screenx >> SX_T_SHIFT;
\r
2544 sprite->tiley = sprite->screeny >> SY_T_SHIFT;
\r
2545 sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )
\r
2546 - sprite->tilex + 1;
\r
2547 sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )
\r
2548 - sprite->tiley + 1;
\r
2550 sprite->updatecount = 1; // draw on next refresh
\r
2552 // save the sprite pointer off in the user's pointer so it can be moved
\r
2558 //===========================================================================
\r
2561 =====================
\r
2563 = RF_RemoveSprite CGA
\r
2565 =====================
\r
2568 void RF_RemoveSprite (void **user)
\r
2570 spritelisttype *sprite,*next;
\r
2572 sprite = (spritelisttype *)*user;
\r
2577 // post an erase block to erase the old position by copying
\r
2578 // screenx,screeny,width,height
\r
2580 if (!sprite->updatecount)
\r
2582 memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));
\r
2586 // unlink the sprite node
\r
2588 next = sprite->nextsprite;
\r
2589 if (next) // if (!next), sprite is last in chain
\r
2590 next->prevptr = sprite->prevptr;
\r
2591 *sprite->prevptr = next;
\r
2594 // add it back to the free list
\r
2596 sprite->nextsprite = spritefreeptr;
\r
2597 spritefreeptr = sprite;
\r
2600 // null the users pointer, so next time that actor gets placed, it will
\r
2601 // allocate a new block
\r
2609 ====================
\r
2611 = RFL_EraseBlocks CGA
\r
2613 = Write mode 1 should be set
\r
2615 ====================
\r
2618 void RFL_EraseBlocks (void)
\r
2620 eraseblocktype *block,*done;
\r
2621 int screenxh,screenyh;
\r
2622 unsigned pos,xtl,ytl,xth,yth,x,y;
\r
2624 unsigned updatedelta;
\r
2626 block = &eraselist[0][0];
\r
2628 done = eraselistptr[0];
\r
2630 while (block != done)
\r
2634 // clip the block to the current screen view
\r
2636 block->screenx -= originxscreen;
\r
2637 block->screeny -= originyscreen;
\r
2639 if (block->screenx < 0)
\r
2641 block->width += block->screenx;
\r
2642 if (block->width<1)
\r
2644 block->screenx = 0;
\r
2647 if (block->screeny < 0)
\r
2649 block->height += block->screeny;
\r
2650 if (block->height<1)
\r
2652 block->screeny = 0;
\r
2655 screenxh = block->screenx + block->width;
\r
2656 screenyh = block->screeny + block->height;
\r
2658 if (screenxh > CGAPORTSCREENWIDE)
\r
2660 block->width = CGAPORTSCREENWIDE-block->screenx;
\r
2661 screenxh = block->screenx + block->width;
\r
2664 if (screenyh > PORTSCREENHIGH)
\r
2666 block->height = PORTSCREENHIGH-block->screeny;
\r
2667 screenyh = block->screeny + block->height;
\r
2670 if (block->width<1 || block->height<1)
\r
2674 // erase the block by copying from the master screen
\r
2676 pos = ylookup[block->screeny]+block->screenx;
\r
2677 block->width = (block->width + (pos&1) + 1)& ~1;
\r
2678 pos &= ~1; // make sure a word copy gets used
\r
2679 VW_ScreenToScreen (masterofs+pos,bufferofs+pos,
\r
2680 block->width,block->height);
\r
2683 // put 2s in update where the block was, to force sprites to update
\r
2685 xtl = block->screenx >> SX_T_SHIFT;
\r
2686 xth = (block->screenx+block->width-1) >> SX_T_SHIFT;
\r
2687 ytl = block->screeny >> SY_T_SHIFT;
\r
2688 yth = (block->screeny+block->height-1) >> SY_T_SHIFT;
\r
2690 updatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2691 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2693 for (y=ytl;y<=yth;y++)
\r
2695 for (x=xtl;x<=xth;x++)
\r
2696 *updatespot++ = 2;
\r
2697 updatespot += updatedelta; // down to next line
\r
2703 eraselistptr[0] = &eraselist[0][0];
\r
2708 ====================
\r
2710 = RFL_UpdateSprites CGA
\r
2712 = NOTE: Implement vertical clipping!
\r
2714 ====================
\r
2717 void RFL_UpdateSprites (void)
\r
2719 spritelisttype *sprite;
\r
2720 int portx,porty,x,y,xtl,xth,ytl,yth;
\r
2723 byte *updatespot,*baseupdatespot;
\r
2724 unsigned updatedelta;
\r
2726 unsigned updatecount;
\r
2727 unsigned height,sourceofs;
\r
2734 for (priority=0;priority<PRIORITIES;priority++)
\r
2736 if (priority==MASKEDTILEPRIORITY)
\r
2737 RFL_MaskForegroundTiles ();
\r
2739 for (sprite = prioritystart[priority]; sprite ;
\r
2740 sprite = (spritelisttype *)sprite->nextsprite)
\r
2743 // see if the sprite has any visable area in the port
\r
2746 portx = sprite->screenx - originxscreen;
\r
2747 porty = sprite->screeny - originyscreen;
\r
2748 xtl = portx >> SX_T_SHIFT;
\r
2749 xth = (portx + sprite->width-1) >> SX_T_SHIFT;
\r
2750 ytl = porty >> SY_T_SHIFT;
\r
2751 yth = (porty + sprite->height-1) >> SY_T_SHIFT;
\r
2755 if (xth>=PORTTILESWIDE)
\r
2756 xth = PORTTILESWIDE-1;
\r
2759 if (yth>=PORTTILESHIGH)
\r
2760 yth = PORTTILESHIGH-1;
\r
2762 if (xtl>xth || ytl>yth)
\r
2766 // see if it's visable area covers any non 0 update tiles
\r
2768 updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;
\r
2769 updatedelta = UPDATEWIDE - (xth-xtl+1);
\r
2771 if (sprite->updatecount)
\r
2773 sprite->updatecount--; // the sprite was just placed,
\r
2774 goto redraw; // so draw it for sure
\r
2777 for (y=ytl;y<=yth;y++)
\r
2779 for (x=xtl;x<=xth;x++)
\r
2780 if (*updatespot++)
\r
2782 updatespot += updatedelta; // down to next line
\r
2784 continue; // no need to update
\r
2788 // set the tiles it covers to 3, because those tiles are being
\r
2791 updatespot = baseupdatespot;
\r
2792 for (y=ytl;y<=yth;y++)
\r
2794 for (x=xtl;x<=xth;x++)
\r
2795 *updatespot++ = 3;
\r
2796 updatespot += updatedelta; // down to next line
\r
2801 height = sprite->height;
\r
2802 sourceofs = sprite->sourceofs;
\r
2805 height += porty; // clip top off
\r
2806 sourceofs -= porty*sprite->width;
\r
2809 else if (porty+height>PORTSCREENHIGH)
\r
2811 height = PORTSCREENHIGH - porty; // clip bottom off
\r
2814 dest = bufferofs + ylookup[porty] + portx;
\r
2816 switch (sprite->draw)
\r
2819 VW_MaskBlock(grsegs[sprite->grseg], sourceofs,
\r
2820 dest,sprite->width,height,sprite->planesize);
\r
2838 =====================
\r
2842 = All routines will draw at the port at bufferofs, possibly copying from
\r
2843 = the port at masterofs. The EGA version then page flips, while the
\r
2844 = CGA version updates the screen from the buffer port.
\r
2846 = Screenpage is the currently displayed page, not the one being drawn
\r
2847 = Otherpage is the page to be worked with now
\r
2849 =====================
\r
2852 void RF_Refresh (void)
\r
2856 RFL_AnimateTiles ();
\r
2859 // update newly scrolled on tiles and animated tiles from the master screen
\r
2861 RFL_UpdateTiles ();
\r
2862 RFL_EraseBlocks ();
\r
2865 // Update is all 0 except where sprites have changed or new area has
\r
2866 // been scrolled on. Go through all sprites and update the ones that cover
\r
2867 // a non 0 update tile
\r
2869 RFL_UpdateSprites ();
\r
2872 // if the main program has a refresh hook set, call their function before
\r
2873 // displaying the new page
\r
2875 if (refreshvector)
\r
2879 // update everything to the screen
\r
2881 VW_CGAFullUpdate ();
\r
2884 // calculate tics since last refresh for adaptive timing
\r
2889 #endif // GRMODE == CGAGR
\r
2890 //===============================
\r
2892 ; Keen Dreams Source Code
\r
2893 ; Copyright (C) 2014 Javier M. Chavez
\r
2895 ; This program is free software; you can redistribute it and/or modify
\r
2896 ; it under the terms of the GNU General Public License as published by
\r
2897 ; the Free Software Foundation; either version 2 of the License, or
\r
2898 ; (at your option) any later version.
\r
2900 ; This program is distributed in the hope that it will be useful,
\r
2901 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
2902 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
2903 ; GNU General Public License for more details.
\r
2905 ; You should have received a copy of the GNU General Public License along
\r
2906 ; with this program; if not, write to the Free Software Foundation, Inc.,
\r
2907 ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
2914 INCLUDE "ID_ASM.EQU"
\r
2916 CACHETILES = 1 ;enable master screen tile caching
\r
2918 ;============================================================================
\r
2923 UPDATESIZE = (TILESWIDE+1)*TILESHIGH+1
\r
2927 EXTRN screenseg:WORD
\r
2928 EXTRN updateptr:WORD
\r
2929 EXTRN updatestart:WORD
\r
2930 EXTRN masterofs:WORD ;start of master tile port
\r
2931 EXTRN bufferofs:WORD ;start of current buffer port
\r
2932 EXTRN screenstart:WORD ;starts of three screens (0/1/master) in EGA mem
\r
2934 EXTRN mapsegs:WORD
\r
2935 EXTRN originmap:WORD
\r
2936 EXTRN updatemapofs:WORD
\r
2937 EXTRN tilecache:WORD
\r
2938 EXTRN tinf:WORD ;seg pointer to map header and tile info
\r
2939 EXTRN blockstarts:WORD ;offsets from bufferofs for each update block
\r
2946 screenstartcs dw ? ;in code segment for accesability
\r
2952 ;============================================================================
\r
2954 ; CGA refresh routines
\r
2956 ;============================================================================
\r
2960 ;=================
\r
2964 ; Draws a composit two plane tile to the master screen and sets the update
\r
2965 ; spot to 1 in both update pages, forcing the tile to be copied to the
\r
2966 ; view pages the next two refreshes
\r
2968 ; Called to draw newlly scrolled on strips and animating tiles
\r
2970 ;=================
\r
2972 PROC RFL_NewTile updateoffset:WORD
\r
2973 PUBLIC RFL_NewTile
\r
2977 ; mark both update lists at this spot
\r
2979 mov di,[updateoffset]
\r
2981 mov bx,[updateptr] ;start of update matrix
\r
2982 mov [BYTE bx+di],1
\r
2984 mov dx,SCREENWIDTH-TILEWIDTH ;add to get to start of next line
\r
2987 ; set di to the location in screenseg to draw the tile
\r
2990 mov si,[updatemapofs+di] ;offset in map from origin
\r
2991 add si,[originmap]
\r
2992 mov di,[blockstarts+di] ;screen location for tile
\r
2993 add di,[masterofs]
\r
2996 ; set BX to the foreground tile number and SI to the background number
\r
2997 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together
\r
2998 ; as one of the planes totally eclipses the other
\r
3000 mov es,[mapsegs+2] ;foreground plane
\r
3002 mov es,[mapsegs] ;background plane
\r
3005 mov es,[screenseg]
\r
3009 jmp @@maskeddraw ;draw both together
\r
3013 ; Draw single background tile from main memory
\r
3019 mov ds,[grsegs+STARTTILE16*2+si]
\r
3021 xor si,si ;block is segment aligned
\r
3032 mov ds,ax ;restore turbo's data segment
\r
3038 ; Draw a masked tile combo
\r
3039 ; Interupts are disabled and the stack segment is reassigned
\r
3043 cli ; don't allow ints when SS is set
\r
3045 mov ss,[grsegs+STARTTILE16M*2+bx]
\r
3047 mov ds,[grsegs+STARTTILE16*2+si]
\r
3049 xor si,si ;first word of tile data
\r
3052 mov ax,[si] ;background tile
\r
3053 and ax,[ss:si] ;mask
\r
3054 or ax,[ss:si+64] ;masked data
\r
3056 mov ax,[si+2] ;background tile
\r
3057 and ax,[ss:si+2] ;mask
\r
3058 or ax,[ss:si+66] ;masked data
\r
3076 ;===========================================================================
\r
3078 ; EGA refresh routines
\r
3080 ;===========================================================================
\r
3084 ;=================
\r
3088 ; Draws a composit two plane tile to the master screen and sets the update
\r
3089 ; spot to 1 in both update pages, forcing the tile to be copied to the
\r
3090 ; view pages the next two refreshes
\r
3092 ; Called to draw newlly scrolled on strips and animating tiles
\r
3094 ; Assumes write mode 0
\r
3096 ;=================
\r
3098 PROC RFL_NewTile updateoffset:WORD
\r
3099 PUBLIC RFL_NewTile
\r
3103 ; mark both update lists at this spot
\r
3105 mov di,[updateoffset]
\r
3107 mov bx,[updatestart] ;page 0 pointer
\r
3108 mov [BYTE bx+di],1
\r
3109 mov bx,[updatestart+2] ;page 1 pointer
\r
3110 mov [BYTE bx+di],1
\r
3113 ; set screenstartcs to the location in screenseg to draw the tile
\r
3116 mov si,[updatemapofs+di] ;offset in map from origin
\r
3117 add si,[originmap]
\r
3118 mov di,[blockstarts+di] ;screen location for tile
\r
3119 add di,[masterofs]
\r
3120 mov [cs:screenstartcs],di
\r
3123 ; set BX to the foreground tile number and SI to the background number
\r
3124 ; If either BX or SI = 0xFFFF, the tile does not need to be masked together
\r
3125 ; as one of the planes totally eclipses the other
\r
3127 mov es,[mapsegs+2] ;foreground plane
\r
3129 mov es,[mapsegs] ;background plane
\r
3132 mov es,[screenseg]
\r
3133 mov dx,SC_INDEX ;for stepping through map mask planes
\r
3137 jmp @@maskeddraw ;draw both together
\r
3141 ; No foreground tile, so draw a single background tile.
\r
3142 ; Use the master screen cache if possible
\r
3147 mov bx,SCREENWIDTH-2 ;add to get to start of next line
\r
3154 mov ax,[tilecache+si]
\r
3159 ; Draw single tile from cache
\r
3165 mov ax,SC_MAPMASK + 15*256 ;all planes
\r
3169 mov ax,GC_MODE + 1*256 ;write mode 1
\r
3172 mov di,[cs:screenstartcs]
\r
3173 mov ds,[screenseg]
\r
3184 xor ah,ah ;write mode 0
\r
3188 mov ds,ax ;restore turbo's data segment
\r
3193 ; Draw single tile from main memory
\r
3198 mov ax,[cs:screenstartcs]
\r
3199 mov [tilecache+si],ax ;next time it can be drawn from here with latch
\r
3200 mov ds,[grsegs+STARTTILE16*2+si]
\r
3202 xor si,si ;block is segment aligned
\r
3204 mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0
\r
3206 mov cx,4 ;draw four planes
\r
3211 mov di,[cs:screenstartcs] ;start at same place in all planes
\r
3219 shl ah,1 ;shift plane mask over for next plane
\r
3223 mov ds,ax ;restore turbo's data segment
\r
3229 ; Draw a masked tile combo
\r
3230 ; Interupts are disabled and the stack segment is reassigned
\r
3234 cli ; don't allow ints when SS is set
\r
3236 mov ss,[grsegs+STARTTILE16M*2+bx]
\r
3238 mov ds,[grsegs+STARTTILE16*2+si]
\r
3240 xor si,si ;first word of tile data
\r
3242 mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0
\r
3244 mov di,[cs:screenstartcs]
\r
3250 mov bx,[si+tileofs] ;background tile
\r
3251 and bx,[ss:tileofs] ;mask
\r
3252 or bx,[ss:si+tileofs+32] ;masked data
\r
3253 mov [es:di+lineoffset],bx
\r
3254 tileofs = tileofs + 2
\r
3255 lineoffset = lineoffset + SCREENWIDTH
\r
3258 shl ah,1 ;shift plane mask over for next plane
\r
3260 je @@done ;drawn all four planes
\r
3274 ;============================================================================
\r
3276 ; VGA refresh routines
\r
3278 ;============================================================================
\r
3284 ;============================================================================
\r
3286 ; reasonably common refresh routines
\r
3288 ;============================================================================
\r
3291 ;=================
\r
3295 ; Scans through the update matrix pointed to by updateptr, looking for 1s.
\r
3296 ; A 1 represents a tile that needs to be copied from the master screen to the
\r
3297 ; current screen (a new row or an animated tiled). If more than one adjacent
\r
3298 ; tile in a horizontal row needs to be copied, they will be copied as a group.
\r
3300 ; Assumes write mode 1
\r
3302 ;=================
\r
3305 ; AX 0/1 for scasb, temp for segment register transfers
\r
3306 ; BX width for block copies
\r
3308 ; DX line width deltas
\r
3309 ; SI source for copies
\r
3310 ; DI scas dest / movsb dest
\r
3311 ; BP pointer to UPDATETERMINATE
\r
3317 PROC RFL_UpdateTiles
\r
3318 PUBLIC RFL_UpdateTiles
\r
3321 jmp SHORT @@realstart
\r
3324 ; all tiles have been scanned
\r
3329 mov di,[updateptr]
\r
3330 mov bp,(TILESWIDE+1)*TILESHIGH+1
\r
3331 add bp,di ; when di = bx, all tiles have been scanned
\r
3333 mov cx,-1 ; definately scan the entire thing
\r
3336 ; scan for a 1 in the update list, meaning a tile needs to be copied
\r
3337 ; from the master screen to the current screen
\r
3340 pop di ; place to continue scaning from
\r
3342 mov es,ax ; search in the data segment
\r
3355 ; copy a single tile
\r
3360 inc di ; we know the next tile is nothing
\r
3361 push di ; save off the spot being scanned
\r
3362 sub di,[updateptr]
\r
3364 mov di,[blockstarts-4+di] ; start of tile location on screen
\r
3366 add di,[bufferofs] ; dest in current screen
\r
3367 add si,[masterofs] ; source in master screen
\r
3369 mov dx,SCREENWIDTH-TILEWIDTH
\r
3370 mov ax,[screenseg]
\r
3374 ;--------------------------
\r
3389 ;--------------------------
\r
3404 ;--------------------------
\r
3410 ; more than one tile in a row needs to be updated, so do it as a group
\r
3415 mov dx,di ; hold starting position + 1 in dx
\r
3416 inc di ; we know the next tile also gets updated
\r
3417 repe scasb ; see how many more in a row
\r
3418 push di ; save off the spot being scanned
\r
3421 sub bx,dx ; number of tiles in a row
\r
3422 shl bx,1 ; number of bytes / row
\r
3424 mov di,dx ; lookup position of start tile
\r
3425 sub di,[updateptr]
\r
3427 mov di,[blockstarts-2+di] ; start of tile location
\r
3429 add di,[bufferofs] ; dest in current screen
\r
3430 add si,[masterofs] ; source in master screen
\r
3432 mov dx,SCREENWIDTH
\r
3433 sub dx,bx ; offset to next line on screen
\r
3435 sub dx,bx ; bx is words wide in CGA tiles
\r
3438 mov ax,[screenseg]
\r
3461 dec cx ; was 0 from last rep movsb, now $ffff for scasb
\r
3467 ;============================================================================
\r
3470 ;=================
\r
3472 ; RFL_MaskForegroundTiles
\r
3474 ; Scan through update looking for 3's. If the foreground tile there is a
\r
3475 ; masked foreground tile, draw it to the screen
\r
3477 ;=================
\r
3479 PROC RFL_MaskForegroundTiles
\r
3480 PUBLIC RFL_MaskForegroundTiles
\r
3482 jmp SHORT @@realstart
\r
3485 ; all tiles have been scanned
\r
3490 mov di,[updateptr]
\r
3491 mov bp,(TILESWIDE+1)*TILESHIGH+2
\r
3492 add bp,di ; when di = bx, all tiles have been scanned
\r
3494 mov cx,-1 ; definately scan the entire thing
\r
3496 ; scan for a 3 in the update list
\r
3500 mov es,ax ; scan in the data segment
\r
3502 pop di ; place to continue scaning from
\r
3509 ; found a tile, see if it needs to be masked on
\r
3515 sub di,[updateptr]
\r
3517 mov si,[updatemapofs-2+di] ; offset from originmap
\r
3518 add si,[originmap]
\r
3520 mov es,[mapsegs+2] ; foreground map plane segment
\r
3521 mov si,[es:si] ; foreground tile number
\r
3524 jz @@findtile ; 0 = no foreground tile
\r
3527 add bx,INTILE ;INTILE tile info table
\r
3529 test [BYTE PTR es:bx],80h ;high bit = masked tile
\r
3532 ;-------------------
\r
3535 ;=================
\r
3537 ; mask the tile CGA
\r
3539 ;=================
\r
3541 mov di,[blockstarts-2+di]
\r
3542 add di,[bufferofs]
\r
3543 mov es,[screenseg]
\r
3545 mov ds,[grsegs+STARTTILE16M*2+si]
\r
3547 mov bx,64 ;data starts 64 bytes after mask
\r
3553 mov ax,[es:di+lineoffset] ;background
\r
3555 or ax,[si+bx] ;masked data
\r
3556 mov [es:di+lineoffset],ax ;background
\r
3559 mov ax,[es:di+lineoffset+2] ;background
\r
3561 or ax,[si+bx] ;masked data
\r
3562 mov [es:di+lineoffset+2],ax ;background
\r
3565 lineoffset = lineoffset + SCREENWIDTH
\r
3569 ;-------------------
\r
3572 ;=================
\r
3576 ;=================
\r
3578 mov [BYTE planemask],1
\r
3579 mov [BYTE planenum],0
\r
3581 mov di,[blockstarts-2+di]
\r
3582 add di,[bufferofs]
\r
3583 mov [cs:screenstartcs],di
\r
3584 mov es,[screenseg]
\r
3586 mov ds,[grsegs+STARTTILE16M*2+si]
\r
3588 mov bx,32 ;data starts 32 bytes after mask
\r
3593 mov ah,[ss:planemask]
\r
3597 mov ah,[ss:planenum]
\r
3601 mov di,[cs:screenstartcs]
\r
3604 mov cx,[es:di+lineoffset] ;background
\r
3606 or cx,[si+bx] ;masked data
\r
3609 mov [es:di+lineoffset],cx
\r
3610 lineoffset = lineoffset + SCREENWIDTH
\r
3612 add bx,32 ;the mask is now further away
\r
3614 shl [ss:planemask],1 ;shift plane mask over for next plane
\r
3615 cmp [ss:planemask],10000b ;done all four planes?
\r
3616 je @@drawn ;drawn all four planes
\r
3622 ;-------------------
\r
3626 mov cx,-1 ;definately scan the entire thing
\r